github.com/IBM-Cloud/bluemix-go@v0.0.0-20240423071914-9e96525baef4/api/resource/resourcev1/catalog/resource_catalog.go (about)

     1  package catalog
     2  
     3  import (
     4  	"fmt"
     5  	"net/http"
     6  
     7  	"github.com/IBM-Cloud/bluemix-go/bmxerror"
     8  	"github.com/IBM-Cloud/bluemix-go/client"
     9  	"github.com/IBM-Cloud/bluemix-go/helpers"
    10  	"github.com/IBM-Cloud/bluemix-go/models"
    11  	"github.com/IBM-Cloud/bluemix-go/rest"
    12  	"github.com/IBM-Cloud/bluemix-go/utils"
    13  )
    14  
    15  // ErrCodeServiceDoesnotExist ...
    16  const ErrCodeServicePlanDoesnotExist = "ServicePlanDoesnotExist"
    17  const ErrCodeServiceDoesnotExist = "ServiceDoesnotExist"
    18  const ErrCodeServiceDeploymentNotFound = "ServiceDeploymentNotFound"
    19  
    20  func newResourceCatalogAPI(c *client.Client) ResourceCatalogRepository {
    21  	return &resourceCatalog{
    22  		client: c,
    23  	}
    24  }
    25  
    26  type resourceCatalog struct {
    27  	client *client.Client
    28  }
    29  
    30  type ResourceCatalogRepository interface {
    31  	Get(serviceID string, indepth bool) (models.Service, error)
    32  	FindByName(name string, indepth bool) ([]models.Service, error)
    33  	ListServices(cb func(service models.Service) bool) error
    34  	ListServicePlans(cb func(servicePlan models.ServicePlan) bool, service models.Service) error
    35  	GetServiceID(serviceName string) (string, error)
    36  	GetServicePlanID(service models.Service, planName string) (string, error)
    37  	GetServiceName(serviceID string) (string, error)
    38  	GetServicePlanName(servicePlanID string) (string, error)
    39  	ListDeployments(servicePlanID string) ([]models.ServiceDeployment, error)
    40  	GetServicePlan(servicePlanID string) (models.ServicePlan, error)
    41  	ListDeploymentAliases(servicePlanID string) ([]models.ServiceDeploymentAlias, error)
    42  	GetDeploymentAlias(servicePlanID string, instanceTarget string, regionID string) (*models.ServiceDeploymentAlias, error)
    43  	GetServices() ([]models.Service, error)
    44  	GetServicePlans(service models.Service) ([]models.ServicePlan, error)
    45  }
    46  
    47  func (r *resourceCatalog) GetServicePlanID(service models.Service, planName string) (string, error) {
    48  	var servicePlanID string
    49  	err := r.ListServicePlans(func(servicePlan models.ServicePlan) bool {
    50  		if servicePlan.Name == planName {
    51  			servicePlanID = servicePlan.ID
    52  			return false
    53  		}
    54  		return true
    55  	}, service)
    56  	if err != nil {
    57  		return "", err
    58  	}
    59  	return servicePlanID, nil
    60  }
    61  
    62  func (r *resourceCatalog) GetServiceID(serviceName string) (string, error) {
    63  	var serviceID string
    64  	err := r.ListServices(func(service models.Service) bool {
    65  		if service.Name == serviceName {
    66  			serviceID = service.ID
    67  			return false
    68  		}
    69  		return true
    70  	})
    71  	if err != nil {
    72  		return "", err
    73  	}
    74  	return serviceID, nil
    75  }
    76  
    77  func (r *resourceCatalog) ListServices(cb func(service models.Service) bool) error {
    78  	listRequest := rest.GetRequest("/api/v1/")
    79  	req, err := listRequest.Build()
    80  	if err != nil {
    81  		return err
    82  	}
    83  
    84  	_, err = r.client.GetPaginated(
    85  		req.URL.String(),
    86  		NewResourceCatalogPaginatedResources(models.Service{}, *r.client.Config.Endpoint),
    87  		func(resource interface{}) bool {
    88  			if catalogResource, ok := resource.(models.Service); ok {
    89  				return cb(catalogResource)
    90  			}
    91  			return false
    92  		})
    93  
    94  	return err
    95  }
    96  func (r *resourceCatalog) ListServicePlans(cb func(service models.ServicePlan) bool, service models.Service) error {
    97  	var urlSuffix string
    98  	if service.Kind == "iaas" {
    99  		urlSuffix = "/flavor"
   100  	} else {
   101  		urlSuffix = "/plan"
   102  	}
   103  	listRequest := rest.GetRequest("/api/v1/" + service.ID + urlSuffix)
   104  	req, err := listRequest.Build()
   105  	if err != nil {
   106  		return err
   107  	}
   108  	_, err = r.client.GetPaginated(
   109  		req.URL.String(),
   110  		NewResourceCatalogPaginatedResources(models.ServicePlan{}, *r.client.Config.Endpoint),
   111  		func(resource interface{}) bool {
   112  			if resourcePlan, ok := resource.(models.ServicePlan); ok {
   113  				return cb(resourcePlan)
   114  			}
   115  			return false
   116  		})
   117  
   118  	return err
   119  }
   120  
   121  func (r *resourceCatalog) GetServiceName(serviceID string) (string, error) {
   122  	request := rest.GetRequest(helpers.GetFullURL(*r.client.Config.Endpoint, "/api/v1/"+serviceID))
   123  	resp := map[string]interface{}{}
   124  	_, err := r.client.SendRequest(request, &resp)
   125  	if err != nil {
   126  		return "", err
   127  	}
   128  	if resp["kind"] == "runtime" || resp["kind"] == "service" || resp["kind"] == "iaas" || resp["kind"] == "platform_service" || resp["kind"] == "template" || resp["kind"] == "composite" {
   129  		if name, ok := resp["name"].(string); ok {
   130  			return name, nil
   131  		}
   132  		return "", nil
   133  	}
   134  	return "", bmxerror.New(ErrCodeServiceDoesnotExist,
   135  		fmt.Sprintf("Given service : %q doesn't exist", serviceID))
   136  }
   137  
   138  func (r *resourceCatalog) GetServicePlanName(servicePlanID string) (string, error) {
   139  	request := rest.GetRequest(helpers.GetFullURL(*r.client.Config.Endpoint, "/api/v1/"+servicePlanID))
   140  	resp := map[string]interface{}{}
   141  	_, err := r.client.SendRequest(request, &resp)
   142  	if err != nil {
   143  		return "", err
   144  	}
   145  	if resp["kind"] == "flavor" || resp["kind"] == "plan" {
   146  		if name, ok := resp["name"].(string); ok {
   147  			return name, nil
   148  		}
   149  		return "", nil
   150  	}
   151  	return "", bmxerror.New(ErrCodeServicePlanDoesnotExist,
   152  		fmt.Sprintf("Given service plan : %q doesn't exist", servicePlanID))
   153  }
   154  
   155  func (r *resourceCatalog) ListDeployments(servicePlanID string) ([]models.ServiceDeployment, error) {
   156  	deployments := []models.ServiceDeployment{}
   157  	listRequest := rest.GetRequest("/api/v1/" + servicePlanID + "/deployment?include=*")
   158  	req, err := listRequest.Build()
   159  	if err != nil {
   160  		return deployments, err
   161  	}
   162  	_, err = r.client.GetPaginated(
   163  		req.URL.String(),
   164  		NewResourceCatalogPaginatedResources(models.ServiceDeployment{}, *r.client.Config.Endpoint),
   165  		func(resource interface{}) bool {
   166  			if catalogDeployment, ok := resource.(models.ServiceDeployment); ok {
   167  				deployments = append(deployments, catalogDeployment)
   168  			}
   169  			return true
   170  		})
   171  	return deployments, err
   172  }
   173  
   174  func (r *resourceCatalog) Get(serviceID string, indepth bool) (models.Service, error) {
   175  	request := rest.GetRequest(helpers.GetFullURL(*r.client.Config.Endpoint, fmt.Sprintf("/api/v1/%s", serviceID)))
   176  	if indepth {
   177  		request = request.Query("include", "*")
   178  	}
   179  	service := models.Service{}
   180  	resp, err := r.client.SendRequest(request, &service)
   181  	if err != nil {
   182  		if resp.StatusCode == http.StatusNotFound {
   183  			return models.Service{}, bmxerror.New(ErrCodeServicePlanDoesnotExist,
   184  				fmt.Sprintf("Given service : %q doesn't exist", serviceID))
   185  		}
   186  		return models.Service{}, err
   187  	}
   188  	return service, nil
   189  }
   190  
   191  func (r *resourceCatalog) FindByName(name string, indepth bool) ([]models.Service, error) {
   192  	services := []models.Service{}
   193  	request := rest.GetRequest("/api/v1/").Query("q", name)
   194  	if indepth {
   195  		request = request.Query("include", "*")
   196  	}
   197  	req, err := request.Build()
   198  	if err != nil {
   199  		return services, err
   200  	}
   201  	_, err = r.client.GetPaginated(
   202  		req.URL.String(),
   203  		NewResourceCatalogPaginatedResources(models.Service{}, *r.client.Config.Endpoint),
   204  		func(rb interface{}) bool {
   205  			if r, ok := rb.(models.Service); ok {
   206  				services = append(services, visitServiceTree(r, name)...)
   207  			}
   208  			return true
   209  		})
   210  	if err != nil {
   211  		return []models.Service{}, err
   212  	}
   213  	if len(services) == 0 {
   214  		return services, bmxerror.New(ErrCodeServiceDoesnotExist,
   215  			fmt.Sprintf("Given service : %q doesn't exist", name))
   216  	}
   217  	return services, err
   218  }
   219  
   220  func (r *resourceCatalog) GetServicePlan(servicePlanID string) (models.ServicePlan, error) {
   221  	request := rest.GetRequest(helpers.GetFullURL(*r.client.Config.Endpoint, fmt.Sprintf("/api/v1/%s", servicePlanID)))
   222  	servicePlan := models.ServicePlan{}
   223  	resp, err := r.client.SendRequest(request, &servicePlan)
   224  	if err != nil {
   225  		if resp.StatusCode == http.StatusNotFound {
   226  			return models.ServicePlan{}, bmxerror.New(ErrCodeServicePlanDoesnotExist,
   227  				fmt.Sprintf("Given service plan : %q doesn't exist", servicePlanID))
   228  		}
   229  		return models.ServicePlan{}, err
   230  	}
   231  	return servicePlan, nil
   232  }
   233  
   234  func (r *resourceCatalog) ListDeploymentAliases(serviceDeploymentID string) ([]models.ServiceDeploymentAlias, error) {
   235  	aliases := []models.ServiceDeploymentAlias{}
   236  	listRequest := rest.GetRequest("/api/v1/" + serviceDeploymentID + "/alias?include=*")
   237  	req, err := listRequest.Build()
   238  	if err != nil {
   239  		return aliases, err
   240  	}
   241  	_, err = r.client.GetPaginated(
   242  		req.URL.String(),
   243  		NewResourceCatalogPaginatedResources(models.ServiceDeploymentAlias{}, *r.client.Config.Endpoint),
   244  		func(resource interface{}) bool {
   245  			if deploymentAlias, ok := resource.(models.ServiceDeploymentAlias); ok {
   246  				aliases = append(aliases, deploymentAlias)
   247  			}
   248  			return true
   249  		})
   250  	return aliases, err
   251  }
   252  
   253  func (r *resourceCatalog) GetDeploymentAlias(servicePlanID string, instanceTarget string, currentRegion string) (*models.ServiceDeploymentAlias, error) {
   254  	deployments, err := r.ListDeployments(servicePlanID)
   255  	if err != nil {
   256  		return nil, err
   257  	}
   258  	found := false
   259  	var deploymentID string
   260  	for _, deployment := range deployments {
   261  		deploymentLocation := utils.GetLocationFromTargetCRN(deployment.Metadata.Deployment.TargetCrn.Resource)
   262  		if deploymentLocation == instanceTarget {
   263  			deploymentID = deployment.ID
   264  			found = true
   265  			break
   266  		}
   267  	}
   268  	if !found {
   269  		//Should not go here since instanceTarget is get from deployments when create service instance
   270  		return nil, bmxerror.New(ErrCodeServiceDeploymentNotFound,
   271  			fmt.Sprintf("Service alias Deployment doesn't exist for %q", instanceTarget))
   272  	}
   273  	aliases, err := r.ListDeploymentAliases(deploymentID)
   274  	if err != nil {
   275  		return nil, err
   276  	}
   277  	for _, alias := range aliases {
   278  		if alias.Metadata.Deployment.Location == currentRegion {
   279  			return &alias, nil
   280  		}
   281  	}
   282  	return nil, nil
   283  }
   284  
   285  func visitServiceTree(rootService models.Service, name string) []models.Service {
   286  	services := []models.Service{}
   287  	if rootService.Name == name && isService(rootService) {
   288  		services = append(services, rootService)
   289  	}
   290  	for _, child := range rootService.Children {
   291  		services = append(services, visitServiceTree(child, name)...)
   292  	}
   293  	return services
   294  }
   295  
   296  func (r *resourceCatalog) GetServices() ([]models.Service, error) {
   297  	listRequest := rest.GetRequest("/api/v1/")
   298  	var services []models.Service
   299  	req, err := listRequest.Build()
   300  	if err != nil {
   301  		return nil, err
   302  	}
   303  
   304  	_, err = r.client.GetPaginated(
   305  		req.URL.String(),
   306  		NewResourceCatalogPaginatedResources(models.Service{}, *r.client.Config.Endpoint),
   307  		func(resource interface{}) bool {
   308  			if catalogResource, ok := resource.(models.Service); ok {
   309  				services = append(services, catalogResource)
   310  				return true
   311  			}
   312  			return false
   313  		})
   314  
   315  	if err != nil {
   316  		return []models.Service{}, err
   317  	}
   318  
   319  	return services, nil
   320  }
   321  
   322  func (r *resourceCatalog) GetServicePlans(service models.Service) ([]models.ServicePlan, error) {
   323  	var urlSuffix string
   324  	if service.Kind == "iaas" {
   325  		urlSuffix = "/flavor"
   326  	} else {
   327  		urlSuffix = "/plan"
   328  	}
   329  	listRequest := rest.GetRequest("/api/v1/" + service.ID + urlSuffix)
   330  	req, err := listRequest.Build()
   331  	if err != nil {
   332  		return nil, err
   333  	}
   334  	var servicePlans []models.ServicePlan
   335  	_, err = r.client.GetPaginated(
   336  		req.URL.String(),
   337  		NewResourceCatalogPaginatedResources(models.ServicePlan{}, *r.client.Config.Endpoint),
   338  		func(resource interface{}) bool {
   339  			if resourcePlan, ok := resource.(models.ServicePlan); ok {
   340  				servicePlans = append(servicePlans, resourcePlan)
   341  			}
   342  			return false
   343  		})
   344  
   345  	if err != nil {
   346  		return []models.ServicePlan{}, err
   347  	}
   348  
   349  	return servicePlans, nil
   350  }
   351  
   352  func isService(e models.Service) bool {
   353  	// TODO: COS is 'iaas' kind, but considered to be a service
   354  	if e.Kind == "service" || e.Kind == "iaas" || e.Kind == "composite" {
   355  		return true
   356  	}
   357  	return false
   358  }