github.com/cloudfoundry-community/cloudfoundry-cli@v6.44.1-0.20240130060226-cda5ed8e89a5+incompatible/api/cloudcontroller/ccv2/service_instance.go (about)

     1  package ccv2
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	"net/url"
     8  
     9  	"code.cloudfoundry.org/cli/api/cloudcontroller"
    10  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccerror"
    11  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccv2/constant"
    12  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccv2/internal"
    13  )
    14  
    15  // ServiceInstance represents a Cloud Controller Service Instance.
    16  type ServiceInstance struct {
    17  	// GUID is the unique service instance identifier.
    18  	GUID string
    19  
    20  	// Name is the name given to the service instance.
    21  	Name string
    22  
    23  	// SpaceGUID is the unique identifier of the space that this service instance
    24  	// belongs to.
    25  	SpaceGUID string
    26  
    27  	// ServiceGUID is the unique identifier of the service that this service
    28  	// instance belongs to.
    29  	ServiceGUID string
    30  
    31  	// ServicePlanGUID is the unique identifier of the service plan that this
    32  	// service instance belongs to.
    33  	ServicePlanGUID string
    34  
    35  	// Type is the type of service instance.
    36  	Type constant.ServiceInstanceType
    37  
    38  	// Tags is a list of all tags for the service instance.
    39  	Tags []string
    40  
    41  	// DashboardURL is the service-broker provided URL to access administrative
    42  	// features of the service instance.
    43  	DashboardURL string
    44  
    45  	// RouteServiceURL is the URL of the user-provided service to which requests
    46  	// for bound routes will be forwarded.
    47  	RouteServiceURL string
    48  
    49  	// LastOperation is the status of the last operation requested on the service
    50  	// instance.
    51  	LastOperation LastOperation
    52  
    53  	// Arbitrary parameters to pass along to the service instance.
    54  	Parameters map[string]interface{}
    55  }
    56  
    57  // Managed returns true if the Service Instance is a managed service.
    58  func (serviceInstance ServiceInstance) Managed() bool {
    59  	return serviceInstance.Type == constant.ManagedService
    60  }
    61  
    62  // UnmarshalJSON helps unmarshal a Cloud Controller Service Instance response.
    63  func (serviceInstance *ServiceInstance) UnmarshalJSON(data []byte) error {
    64  	var ccServiceInstance struct {
    65  		Metadata internal.Metadata
    66  		Entity   struct {
    67  			Name            string                 `json:"name"`
    68  			SpaceGUID       string                 `json:"space_guid"`
    69  			ServiceGUID     string                 `json:"service_guid"`
    70  			ServicePlanGUID string                 `json:"service_plan_guid"`
    71  			Type            string                 `json:"type"`
    72  			Tags            []string               `json:"tags"`
    73  			DashboardURL    string                 `json:"dashboard_url"`
    74  			RouteServiceURL string                 `json:"route_service_url"`
    75  			LastOperation   LastOperation          `json:"last_operation"`
    76  			Parameters      map[string]interface{} `json:"parameters"`
    77  		}
    78  	}
    79  	err := cloudcontroller.DecodeJSON(data, &ccServiceInstance)
    80  	if err != nil {
    81  		return err
    82  	}
    83  
    84  	serviceInstance.GUID = ccServiceInstance.Metadata.GUID
    85  	serviceInstance.Name = ccServiceInstance.Entity.Name
    86  	serviceInstance.SpaceGUID = ccServiceInstance.Entity.SpaceGUID
    87  	serviceInstance.ServiceGUID = ccServiceInstance.Entity.ServiceGUID
    88  	serviceInstance.ServicePlanGUID = ccServiceInstance.Entity.ServicePlanGUID
    89  	serviceInstance.Type = constant.ServiceInstanceType(ccServiceInstance.Entity.Type)
    90  	serviceInstance.Tags = ccServiceInstance.Entity.Tags
    91  	serviceInstance.DashboardURL = ccServiceInstance.Entity.DashboardURL
    92  	serviceInstance.RouteServiceURL = ccServiceInstance.Entity.RouteServiceURL
    93  	serviceInstance.LastOperation = ccServiceInstance.Entity.LastOperation
    94  	serviceInstance.Parameters = ccServiceInstance.Entity.Parameters
    95  	return nil
    96  }
    97  
    98  // MarshalJSON converts an user provided service instance into a Cloud Controller user provided service instance.
    99  func (serviceInstance ServiceInstance) MarshalJSON() ([]byte, error) {
   100  	ccObj := struct {
   101  		Name            string                 `json:"name,omitempty"`
   102  		SpaceGUID       string                 `json:"space_guid,omitempty"`
   103  		ServicePlanGUID string                 `json:"service_plan_guid,omitempty"`
   104  		Tags            []string               `json:"tags,omitempty"`
   105  		Parameters      map[string]interface{} `json:"parameters,omitempty"`
   106  	}{
   107  		Name:            serviceInstance.Name,
   108  		SpaceGUID:       serviceInstance.SpaceGUID,
   109  		ServicePlanGUID: serviceInstance.ServicePlanGUID,
   110  		Tags:            serviceInstance.Tags,
   111  		Parameters:      serviceInstance.Parameters,
   112  	}
   113  
   114  	return json.Marshal(ccObj)
   115  }
   116  
   117  // UserProvided returns true if the Service Instance is a user provided
   118  // service.
   119  func (serviceInstance ServiceInstance) UserProvided() bool {
   120  	return serviceInstance.Type == constant.UserProvidedService
   121  }
   122  
   123  type createServiceInstanceRequestBody struct {
   124  	Name            string                 `json:"name"`
   125  	ServicePlanGUID string                 `json:"service_plan_guid"`
   126  	SpaceGUID       string                 `json:"space_guid"`
   127  	Parameters      map[string]interface{} `json:"parameters,omitempty"`
   128  	Tags            []string               `json:"tags,omitempty"`
   129  }
   130  
   131  // CreateServiceInstance posts a service instance resource with the provided
   132  // attributes to the api and returns the result.
   133  func (client *Client) CreateServiceInstance(spaceGUID, servicePlanGUID, serviceInstance string, parameters map[string]interface{}, tags []string) (ServiceInstance, Warnings, error) {
   134  	requestBody := createServiceInstanceRequestBody{
   135  		Name:            serviceInstance,
   136  		ServicePlanGUID: servicePlanGUID,
   137  		SpaceGUID:       spaceGUID,
   138  		Parameters:      parameters,
   139  		Tags:            tags,
   140  	}
   141  
   142  	bodyBytes, err := json.Marshal(requestBody)
   143  	if err != nil {
   144  		return ServiceInstance{}, nil, err
   145  	}
   146  
   147  	request, err := client.newHTTPRequest(requestOptions{
   148  		RequestName: internal.PostServiceInstancesRequest,
   149  		Body:        bytes.NewReader(bodyBytes),
   150  		Query:       url.Values{"accepts_incomplete": {"true"}},
   151  	})
   152  	if err != nil {
   153  		return ServiceInstance{}, nil, err
   154  	}
   155  
   156  	var instance ServiceInstance
   157  	response := cloudcontroller.Response{
   158  		DecodeJSONResponseInto: &instance,
   159  	}
   160  
   161  	err = client.connection.Make(request, &response)
   162  	return instance, response.Warnings, err
   163  }
   164  
   165  // CreateServiceInstance posts a service instance resource with the provided
   166  // attributes to the api and returns the result.
   167  func (client *Client) CreateServiceInstanceFromObject(serviceInstance ServiceInstance) (ServiceInstance, Warnings, error) {
   168  	body, err := json.Marshal(serviceInstance)
   169  	if err != nil {
   170  		return ServiceInstance{}, nil, err
   171  	}
   172  
   173  	request, err := client.newHTTPRequest(requestOptions{
   174  		RequestName: internal.PostServiceInstancesRequest,
   175  		Body:        bytes.NewReader(body),
   176  		Query:       url.Values{"accepts_incomplete": {"true"}},
   177  	})
   178  	if err != nil {
   179  		return ServiceInstance{}, nil, err
   180  	}
   181  
   182  	var instance ServiceInstance
   183  	response := cloudcontroller.Response{
   184  		DecodeJSONResponseInto: &instance,
   185  	}
   186  
   187  	err = client.connection.Make(request, &response)
   188  	return instance, response.Warnings, err
   189  }
   190  
   191  // GetServiceInstance returns the service instance with the given GUID. This
   192  // service can be either a managed or user provided.
   193  func (client *Client) GetServiceInstance(serviceInstanceGUID string) (ServiceInstance, Warnings, error) {
   194  	request, err := client.newHTTPRequest(requestOptions{
   195  		RequestName: internal.GetServiceInstanceRequest,
   196  		URIParams:   Params{"service_instance_guid": serviceInstanceGUID},
   197  	})
   198  	if err != nil {
   199  		return ServiceInstance{}, nil, err
   200  	}
   201  
   202  	var serviceInstance ServiceInstance
   203  	response := cloudcontroller.Response{
   204  		DecodeJSONResponseInto: &serviceInstance,
   205  	}
   206  
   207  	err = client.connection.Make(request, &response)
   208  	return serviceInstance, response.Warnings, err
   209  }
   210  
   211  // GetServiceInstances returns back a list of *managed* Service Instances based
   212  // off of the provided filters.
   213  func (client *Client) GetServiceInstances(filters ...Filter) ([]ServiceInstance, Warnings, error) {
   214  	request, err := client.newHTTPRequest(requestOptions{
   215  		RequestName: internal.GetServiceInstancesRequest,
   216  		Query:       ConvertFilterParameters(filters),
   217  	})
   218  	if err != nil {
   219  		return nil, nil, err
   220  	}
   221  
   222  	var fullInstancesList []ServiceInstance
   223  	warnings, err := client.paginate(request, ServiceInstance{}, func(item interface{}) error {
   224  		if instance, ok := item.(ServiceInstance); ok {
   225  			fullInstancesList = append(fullInstancesList, instance)
   226  		} else {
   227  			return ccerror.UnknownObjectInListError{
   228  				Expected:   ServiceInstance{},
   229  				Unexpected: item,
   230  			}
   231  		}
   232  		return nil
   233  	})
   234  
   235  	return fullInstancesList, warnings, err
   236  }
   237  
   238  // GetSpaceServiceInstances returns back a list of Service Instances based off
   239  // of the space and filters provided. User provided services will be included
   240  // if includeUserProvidedServices is set to true.
   241  func (client *Client) GetSpaceServiceInstances(spaceGUID string, includeUserProvidedServices bool, filters ...Filter) ([]ServiceInstance, Warnings, error) {
   242  	query := ConvertFilterParameters(filters)
   243  
   244  	if includeUserProvidedServices {
   245  		query.Add("return_user_provided_service_instances", "true")
   246  	}
   247  
   248  	request, err := client.newHTTPRequest(requestOptions{
   249  		RequestName: internal.GetSpaceServiceInstancesRequest,
   250  		URIParams:   map[string]string{"guid": spaceGUID},
   251  		Query:       query,
   252  	})
   253  	if err != nil {
   254  		return nil, nil, err
   255  	}
   256  
   257  	var fullInstancesList []ServiceInstance
   258  	warnings, err := client.paginate(request, ServiceInstance{}, func(item interface{}) error {
   259  		if instance, ok := item.(ServiceInstance); ok {
   260  			fullInstancesList = append(fullInstancesList, instance)
   261  		} else {
   262  			return ccerror.UnknownObjectInListError{
   263  				Expected:   ServiceInstance{},
   264  				Unexpected: item,
   265  			}
   266  		}
   267  		return nil
   268  	})
   269  
   270  	return fullInstancesList, warnings, err
   271  }
   272  
   273  // UpdateServiceInstance updates the service instance with the given GUID.
   274  func (client *Client) UpdateServiceInstance(serviceInstance ServiceInstance) (ServiceInstance, Warnings, error) {
   275  	body, err := json.Marshal(serviceInstance)
   276  	if err != nil {
   277  		return ServiceInstance{}, nil, err
   278  	}
   279  
   280  	request, err := client.newHTTPRequest(requestOptions{
   281  		RequestName: internal.PutServiceInstanceRequest,
   282  		URIParams:   Params{"service_instance_guid": serviceInstance.GUID},
   283  		Body:        bytes.NewReader(body),
   284  		Query:       url.Values{"accepts_incomplete": {"true"}},
   285  	})
   286  	if err != nil {
   287  		return ServiceInstance{}, nil, err
   288  	}
   289  
   290  	var updatedObj ServiceInstance
   291  	response := cloudcontroller.Response{
   292  		DecodeJSONResponseInto: &updatedObj,
   293  	}
   294  
   295  	err = client.connection.Make(request, &response)
   296  	return updatedObj, response.Warnings, err
   297  }
   298  
   299  // DeleteServiceInstance delete a service instance
   300  func (client *Client) DeleteServiceInstance(guid string, recursive bool, purge bool) (async bool, warn Warnings, err error) {
   301  	request, err := client.newHTTPRequest(requestOptions{
   302  		RequestName: internal.DeleteServiceInstanceRequest,
   303  		URIParams: Params{
   304  			"service_instance_guid": guid,
   305  		},
   306  		Query: url.Values{
   307  			"accepts_incomplete": {"true"},
   308  			"recursive":          {fmt.Sprintf("%t", recursive)},
   309  			"purge":              {fmt.Sprintf("%t", purge)},
   310  		},
   311  	})
   312  	if err != nil {
   313  		return false, nil, err
   314  	}
   315  
   316  	response := cloudcontroller.Response{}
   317  
   318  	err = client.connection.Make(request, &response)
   319  	return response.HTTPResponse.StatusCode == 202, response.Warnings, err
   320  }