github.com/arunkumar7540/cli@v6.45.0+incompatible/actor/v2action/service_instance_summary.go (about) 1 package v2action 2 3 import ( 4 "fmt" 5 "sort" 6 7 "code.cloudfoundry.org/cli/api/cloudcontroller/ccerror" 8 "code.cloudfoundry.org/cli/api/cloudcontroller/ccv2" 9 "code.cloudfoundry.org/cli/api/cloudcontroller/ccv2/constant" 10 "code.cloudfoundry.org/cli/util/sorting" 11 log "github.com/sirupsen/logrus" 12 ) 13 14 type ServiceInstanceShareType string 15 16 type LastOperation ccv2.LastOperation 17 18 const ( 19 ServiceInstanceIsSharedFrom ServiceInstanceShareType = "SharedFrom" 20 ServiceInstanceIsSharedTo ServiceInstanceShareType = "SharedTo" 21 ServiceInstanceIsNotShared ServiceInstanceShareType = "NotShared" 22 ) 23 24 type ServiceInstanceSummary struct { 25 ServiceInstance 26 27 ServicePlan ServicePlan 28 Service Service 29 ServiceInstanceSharingFeatureFlag bool 30 ServiceInstanceShareType ServiceInstanceShareType 31 ServiceInstanceSharedFrom ServiceInstanceSharedFrom 32 ServiceInstanceSharedTos []ServiceInstanceSharedTo 33 BoundApplications []BoundApplication 34 } 35 36 func (s ServiceInstanceSummary) IsShareable() bool { 37 return s.ServiceInstanceSharingFeatureFlag && s.Service.Extra.Shareable 38 } 39 40 func (s ServiceInstanceSummary) IsNotShared() bool { 41 return s.ServiceInstanceShareType == ServiceInstanceIsNotShared 42 } 43 44 func (s ServiceInstanceSummary) IsSharedFrom() bool { 45 return s.ServiceInstanceShareType == ServiceInstanceIsSharedFrom 46 } 47 48 func (s ServiceInstanceSummary) IsSharedTo() bool { 49 return s.ServiceInstanceShareType == ServiceInstanceIsSharedTo 50 } 51 52 func (s ServiceInstanceSummary) UpgradeAvailable() string { 53 if s.ServicePlan.MaintenanceInfo.Version == "" { 54 return "" 55 } 56 if s.ServicePlan.MaintenanceInfo.Version == s.ServiceInstance.MaintenanceInfo.Version { 57 return "no" 58 } 59 return "yes" 60 } 61 62 type BoundApplication struct { 63 AppName string 64 LastOperation LastOperation 65 ServiceBindingName string 66 } 67 68 func (actor Actor) GetServiceInstanceSummaryByNameAndSpace(name string, spaceGUID string) (ServiceInstanceSummary, Warnings, error) { 69 serviceInstance, instanceWarnings, err := actor.GetServiceInstanceByNameAndSpace(name, spaceGUID) 70 allWarnings := Warnings(instanceWarnings) 71 if err != nil { 72 return ServiceInstanceSummary{}, allWarnings, err 73 } 74 75 serviceInstanceSummary, warnings, err := actor.getSummaryInfoCompositeForInstance(spaceGUID, serviceInstance, true) 76 return serviceInstanceSummary, append(allWarnings, warnings...), err 77 } 78 79 func (actor Actor) GetServiceInstancesSummaryBySpace(spaceGUID string) ([]ServiceInstanceSummary, Warnings, error) { 80 spaceSummary, warnings, err := actor.CloudControllerClient.GetSpaceSummary(spaceGUID) 81 allWarnings := Warnings(warnings) 82 if err != nil { 83 return []ServiceInstanceSummary{}, allWarnings, err 84 } 85 86 services, warnings, err := actor.CloudControllerClient.GetServices() 87 allWarnings = append(allWarnings, warnings...) 88 if err != nil { 89 return []ServiceInstanceSummary{}, allWarnings, err 90 } 91 92 serviceGUIDToBrokerName := map[string]string{} 93 for _, service := range services { 94 serviceGUIDToBrokerName[service.GUID] = service.ServiceBrokerName 95 } 96 97 var serviceInstanceSummaries []ServiceInstanceSummary 98 for _, serviceInstance := range spaceSummary.ServiceInstances { 99 instanceSummary := ServiceInstanceSummary{} 100 101 instanceSummary.Name = serviceInstance.Name 102 instanceSummary.ServicePlan.Name = serviceInstance.ServicePlan.Name 103 instanceSummary.ServicePlan.MaintenanceInfo = serviceInstance.ServicePlan.MaintenanceInfo 104 instanceSummary.Service.Label = serviceInstance.ServicePlan.Service.Label 105 instanceSummary.LastOperation = serviceInstance.LastOperation 106 instanceSummary.Service.ServiceBrokerName = serviceGUIDToBrokerName[serviceInstance.ServicePlan.Service.GUID] 107 instanceSummary.ServiceInstance.Type = discoverServiceInstanceType(serviceInstance) 108 instanceSummary.ServiceInstance.MaintenanceInfo = serviceInstance.MaintenanceInfo 109 instanceSummary.BoundApplications = findServiceInstanceBoundApplications(serviceInstance.Name, spaceSummary.Applications) 110 111 serviceInstanceSummaries = append(serviceInstanceSummaries, instanceSummary) 112 } 113 114 return serviceInstanceSummaries, allWarnings, nil 115 } 116 117 // getAndSetSharedInformation gets a service instance's shared from or shared to information, 118 func (actor Actor) getAndSetSharedInformation(summary *ServiceInstanceSummary, spaceGUID string) (Warnings, error) { 119 var ( 120 warnings Warnings 121 err error 122 ) 123 124 // Part of determining if a service instance is shareable, we need to find 125 // out if the service_instance_sharing feature flag is enabled 126 featureFlags, featureFlagsWarnings, featureFlagsErr := actor.CloudControllerClient.GetConfigFeatureFlags() 127 allWarnings := Warnings(featureFlagsWarnings) 128 if featureFlagsErr != nil { 129 return allWarnings, featureFlagsErr 130 } 131 132 for _, flag := range featureFlags { 133 if flag.Name == string(constant.FeatureFlagServiceInstanceSharing) { 134 summary.ServiceInstanceSharingFeatureFlag = flag.Enabled 135 } 136 } 137 138 // Service instance is shared from if: 139 // 1. the source space of the service instance is empty (API returns json null) 140 // 2. the targeted space is not the same as the source space of the service instance AND 141 // we call the shared_from url and it returns a non-empty resource 142 if summary.ServiceInstance.SpaceGUID == "" || summary.ServiceInstance.SpaceGUID != spaceGUID { 143 summary.ServiceInstanceSharedFrom, warnings, err = actor.GetServiceInstanceSharedFromByServiceInstance(summary.ServiceInstance.GUID) 144 allWarnings = append(allWarnings, warnings...) 145 if err != nil { 146 // if the API version does not support service instance sharing, ignore the 404 147 if _, ok := err.(ccerror.ResourceNotFoundError); !ok { 148 return allWarnings, err 149 } 150 } 151 152 if summary.ServiceInstanceSharedFrom.SpaceGUID != "" { 153 summary.ServiceInstanceShareType = ServiceInstanceIsSharedFrom 154 } else { 155 summary.ServiceInstanceShareType = ServiceInstanceIsNotShared 156 } 157 158 return allWarnings, nil 159 } 160 161 // Service instance is shared to if: 162 // the targeted space is the same as the source space of the service instance AND 163 // we call the shared_to url and get a non-empty list 164 summary.ServiceInstanceSharedTos, warnings, err = actor.GetServiceInstanceSharedTosByServiceInstance(summary.ServiceInstance.GUID) 165 allWarnings = append(allWarnings, warnings...) 166 if err != nil { 167 // if the API version does not support service instance sharing, ignore the 404 168 if _, ok := err.(ccerror.ResourceNotFoundError); !ok { 169 return allWarnings, err 170 } 171 } 172 173 if len(summary.ServiceInstanceSharedTos) > 0 { 174 summary.ServiceInstanceShareType = ServiceInstanceIsSharedTo 175 } else { 176 summary.ServiceInstanceShareType = ServiceInstanceIsNotShared 177 } 178 179 return allWarnings, nil 180 } 181 182 func (actor Actor) getSummaryInfoCompositeForInstance(spaceGUID string, serviceInstance ServiceInstance, retrieveSharedInfo bool) (ServiceInstanceSummary, Warnings, error) { 183 log.WithField("GUID", serviceInstance.GUID).Info("looking up service instance info") 184 185 serviceInstanceSummary := ServiceInstanceSummary{ServiceInstance: serviceInstance} 186 var ( 187 serviceBindings []ServiceBinding 188 allWarnings Warnings 189 ) 190 191 if serviceInstance.IsManaged() { 192 log.Debug("service is managed") 193 if retrieveSharedInfo { 194 sharedWarnings, err := actor.getAndSetSharedInformation(&serviceInstanceSummary, spaceGUID) 195 allWarnings = Warnings(sharedWarnings) 196 if err != nil { 197 log.WithField("GUID", serviceInstance.GUID).Errorln("looking up share info:", err) 198 return serviceInstanceSummary, allWarnings, err 199 } 200 } 201 202 servicePlan, planWarnings, err := actor.GetServicePlan(serviceInstance.ServicePlanGUID) 203 allWarnings = append(allWarnings, planWarnings...) 204 if err != nil { 205 log.WithField("service_plan_guid", serviceInstance.ServicePlanGUID).Errorln("looking up service plan:", err) 206 if _, ok := err.(ccerror.ForbiddenError); !ok { 207 return serviceInstanceSummary, allWarnings, err 208 } 209 log.Warning("Forbidden Error - ignoring and continue") 210 allWarnings = append(allWarnings, fmt.Sprintf("This org is not authorized to view necessary data about this service plan. Contact your administrator regarding service GUID %s.", serviceInstance.ServicePlanGUID)) 211 } 212 serviceInstanceSummary.ServicePlan = servicePlan 213 214 service, serviceWarnings, err := actor.GetService(serviceInstance.ServiceGUID) 215 allWarnings = append(allWarnings, serviceWarnings...) 216 if err != nil { 217 log.WithField("service_guid", serviceInstance.ServiceGUID).Errorln("looking up service:", err) 218 if _, ok := err.(ccerror.ForbiddenError); !ok { 219 return serviceInstanceSummary, allWarnings, err 220 } 221 log.Warning("Forbidden Error - ignoring and continue") 222 allWarnings = append(allWarnings, fmt.Sprintf("This org is not authorized to view necessary data about this service. Contact your administrator regarding service GUID %s.", serviceInstance.ServiceGUID)) 223 } 224 serviceInstanceSummary.Service = service 225 226 var bindingsWarnings Warnings 227 serviceBindings, bindingsWarnings, err = actor.GetServiceBindingsByServiceInstance(serviceInstance.GUID) 228 allWarnings = append(allWarnings, bindingsWarnings...) 229 if err != nil { 230 log.WithField("GUID", serviceInstance.GUID).Errorln("looking up service binding:", err) 231 return serviceInstanceSummary, allWarnings, err 232 } 233 } else { 234 log.Debug("service is user provided") 235 var bindingsWarnings Warnings 236 var err error 237 serviceBindings, bindingsWarnings, err = actor.GetServiceBindingsByUserProvidedServiceInstance(serviceInstance.GUID) 238 allWarnings = append(allWarnings, bindingsWarnings...) 239 if err != nil { 240 log.WithField("service_instance_guid", serviceInstance.GUID).Errorln("looking up service bindings:", err) 241 return serviceInstanceSummary, allWarnings, err 242 } 243 } 244 245 for _, serviceBinding := range serviceBindings { 246 log.WithFields(log.Fields{ 247 "app_guid": serviceBinding.AppGUID, 248 "service_binding_guid": serviceBinding.GUID, 249 }).Debug("application lookup") 250 251 app, appWarnings, err := actor.GetApplication(serviceBinding.AppGUID) 252 allWarnings = append(allWarnings, appWarnings...) 253 if err != nil { 254 log.WithFields(log.Fields{ 255 "app_guid": serviceBinding.AppGUID, 256 "service_binding_guid": serviceBinding.GUID, 257 }).Errorln("looking up application:", err) 258 return serviceInstanceSummary, allWarnings, err 259 } 260 261 serviceInstanceSummary.BoundApplications = append( 262 serviceInstanceSummary.BoundApplications, 263 BoundApplication{ 264 AppName: app.Name, 265 ServiceBindingName: serviceBinding.Name, 266 LastOperation: LastOperation(serviceBinding.LastOperation), 267 }) 268 } 269 270 sort.Slice( 271 serviceInstanceSummary.BoundApplications, 272 func(i, j int) bool { 273 return sorting.LessIgnoreCase(serviceInstanceSummary.BoundApplications[i].AppName, serviceInstanceSummary.BoundApplications[j].AppName) 274 }) 275 276 return serviceInstanceSummary, allWarnings, nil 277 } 278 279 func findServiceInstanceBoundApplications(instanceName string, spaceApps []ccv2.SpaceSummaryApplication) []BoundApplication { 280 var applicationNames []BoundApplication 281 282 for _, app := range spaceApps { 283 for _, name := range app.ServiceNames { 284 if name == instanceName { 285 applicationNames = append(applicationNames, BoundApplication{AppName: app.Name}) 286 } 287 } 288 } 289 290 return applicationNames 291 } 292 293 func discoverServiceInstanceType(instance ccv2.SpaceSummaryServiceInstance) constant.ServiceInstanceType { 294 if isUserProvided(instance) { 295 return constant.UserProvidedService 296 } 297 return constant.ManagedService 298 } 299 300 func isUserProvided(instance ccv2.SpaceSummaryServiceInstance) bool { 301 // For now we rely on empty service_plan guid to decide if the instance is user provided or managed 302 // This could be improved in future if we add the Type of the service instnace to the summary endpoint result body 303 return instance.ServicePlan.GUID == "" 304 }