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 }