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  }