github.com/willmadison/cli@v6.40.1-0.20181018160101-29d5937903ff+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  type BoundApplication struct {
    53  	AppName            string
    54  	LastOperation      LastOperation
    55  	ServiceBindingName string
    56  }
    57  
    58  func (actor Actor) GetServiceInstanceSummaryByNameAndSpace(name string, spaceGUID string) (ServiceInstanceSummary, Warnings, error) {
    59  	serviceInstance, instanceWarnings, err := actor.GetServiceInstanceByNameAndSpace(name, spaceGUID)
    60  	allWarnings := Warnings(instanceWarnings)
    61  	if err != nil {
    62  		return ServiceInstanceSummary{}, allWarnings, err
    63  	}
    64  
    65  	serviceInstanceSummary, warnings, err := actor.getSummaryInfoCompositeForInstance(spaceGUID, serviceInstance, true)
    66  	return serviceInstanceSummary, append(allWarnings, warnings...), err
    67  }
    68  
    69  func (actor Actor) GetServiceInstancesSummaryBySpace(spaceGUID string) ([]ServiceInstanceSummary, Warnings, error) {
    70  	serviceInstances, warnings, err := actor.CloudControllerClient.GetSpaceServiceInstances(
    71  		spaceGUID,
    72  		true)
    73  	allWarnings := Warnings(warnings)
    74  
    75  	log.WithField("number_of_service_instances", len(serviceInstances)).Info("listing number of service instances")
    76  
    77  	var summaryInstances []ServiceInstanceSummary
    78  	for _, instance := range serviceInstances {
    79  		serviceInstanceSummary, summaryInfoWarnings, summaryInfoErr := actor.getSummaryInfoCompositeForInstance(
    80  			spaceGUID,
    81  			ServiceInstance(instance),
    82  			false)
    83  		allWarnings = append(allWarnings, summaryInfoWarnings...)
    84  		if summaryInfoErr != nil {
    85  			return nil, allWarnings, summaryInfoErr
    86  		}
    87  		summaryInstances = append(summaryInstances, serviceInstanceSummary)
    88  	}
    89  
    90  	sort.Slice(summaryInstances, func(i, j int) bool {
    91  		return sorting.LessIgnoreCase(summaryInstances[i].Name, summaryInstances[j].Name)
    92  	})
    93  
    94  	return summaryInstances, allWarnings, err
    95  }
    96  
    97  // getAndSetSharedInformation gets a service instance's shared from or shared to information,
    98  func (actor Actor) getAndSetSharedInformation(summary *ServiceInstanceSummary, spaceGUID string) (Warnings, error) {
    99  	var (
   100  		warnings Warnings
   101  		err      error
   102  	)
   103  
   104  	// Part of determining if a service instance is shareable, we need to find
   105  	// out if the service_instance_sharing feature flag is enabled
   106  	featureFlags, featureFlagsWarnings, featureFlagsErr := actor.CloudControllerClient.GetConfigFeatureFlags()
   107  	allWarnings := Warnings(featureFlagsWarnings)
   108  	if featureFlagsErr != nil {
   109  		return allWarnings, featureFlagsErr
   110  	}
   111  
   112  	for _, flag := range featureFlags {
   113  		if flag.Name == string(constant.FeatureFlagServiceInstanceSharing) {
   114  			summary.ServiceInstanceSharingFeatureFlag = flag.Enabled
   115  		}
   116  	}
   117  
   118  	// Service instance is shared from if:
   119  	// 1. the source space of the service instance is empty (API returns json null)
   120  	// 2. the targeted space is not the same as the source space of the service instance AND
   121  	//    we call the shared_from url and it returns a non-empty resource
   122  	if summary.ServiceInstance.SpaceGUID == "" || summary.ServiceInstance.SpaceGUID != spaceGUID {
   123  		summary.ServiceInstanceSharedFrom, warnings, err = actor.GetServiceInstanceSharedFromByServiceInstance(summary.ServiceInstance.GUID)
   124  		allWarnings = append(allWarnings, warnings...)
   125  		if err != nil {
   126  			// if the API version does not support service instance sharing, ignore the 404
   127  			if _, ok := err.(ccerror.ResourceNotFoundError); !ok {
   128  				return allWarnings, err
   129  			}
   130  		}
   131  
   132  		if summary.ServiceInstanceSharedFrom.SpaceGUID != "" {
   133  			summary.ServiceInstanceShareType = ServiceInstanceIsSharedFrom
   134  		} else {
   135  			summary.ServiceInstanceShareType = ServiceInstanceIsNotShared
   136  		}
   137  
   138  		return allWarnings, nil
   139  	}
   140  
   141  	// Service instance is shared to if:
   142  	// the targeted space is the same as the source space of the service instance AND
   143  	// we call the shared_to url and get a non-empty list
   144  	summary.ServiceInstanceSharedTos, warnings, err = actor.GetServiceInstanceSharedTosByServiceInstance(summary.ServiceInstance.GUID)
   145  	allWarnings = append(allWarnings, warnings...)
   146  	if err != nil {
   147  		// if the API version does not support service instance sharing, ignore the 404
   148  		if _, ok := err.(ccerror.ResourceNotFoundError); !ok {
   149  			return allWarnings, err
   150  		}
   151  	}
   152  
   153  	if len(summary.ServiceInstanceSharedTos) > 0 {
   154  		summary.ServiceInstanceShareType = ServiceInstanceIsSharedTo
   155  	} else {
   156  		summary.ServiceInstanceShareType = ServiceInstanceIsNotShared
   157  	}
   158  
   159  	return allWarnings, nil
   160  }
   161  
   162  func (actor Actor) getSummaryInfoCompositeForInstance(spaceGUID string, serviceInstance ServiceInstance, retrieveSharedInfo bool) (ServiceInstanceSummary, Warnings, error) {
   163  	log.WithField("GUID", serviceInstance.GUID).Info("looking up service instance info")
   164  
   165  	serviceInstanceSummary := ServiceInstanceSummary{ServiceInstance: serviceInstance}
   166  	var (
   167  		serviceBindings []ServiceBinding
   168  		allWarnings     Warnings
   169  	)
   170  
   171  	if serviceInstance.IsManaged() {
   172  		log.Debug("service is managed")
   173  		if retrieveSharedInfo {
   174  			sharedWarnings, err := actor.getAndSetSharedInformation(&serviceInstanceSummary, spaceGUID)
   175  			allWarnings = Warnings(sharedWarnings)
   176  			if err != nil {
   177  				log.WithField("GUID", serviceInstance.GUID).Errorln("looking up share info:", err)
   178  				return serviceInstanceSummary, allWarnings, err
   179  			}
   180  		}
   181  
   182  		servicePlan, planWarnings, err := actor.GetServicePlan(serviceInstance.ServicePlanGUID)
   183  		allWarnings = append(allWarnings, planWarnings...)
   184  		if err != nil {
   185  			log.WithField("service_plan_guid", serviceInstance.ServicePlanGUID).Errorln("looking up service plan:", err)
   186  			if _, ok := err.(ccerror.ForbiddenError); !ok {
   187  				return serviceInstanceSummary, allWarnings, err
   188  			}
   189  			log.Warning("Forbidden Error - ignoring and continue")
   190  			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))
   191  		}
   192  		serviceInstanceSummary.ServicePlan = servicePlan
   193  
   194  		service, serviceWarnings, err := actor.GetService(serviceInstance.ServiceGUID)
   195  		allWarnings = append(allWarnings, serviceWarnings...)
   196  		if err != nil {
   197  			log.WithField("service_guid", serviceInstance.ServiceGUID).Errorln("looking up service:", err)
   198  			if _, ok := err.(ccerror.ForbiddenError); !ok {
   199  				return serviceInstanceSummary, allWarnings, err
   200  			}
   201  			log.Warning("Forbidden Error - ignoring and continue")
   202  			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))
   203  		}
   204  		serviceInstanceSummary.Service = service
   205  
   206  		var bindingsWarnings Warnings
   207  		serviceBindings, bindingsWarnings, err = actor.GetServiceBindingsByServiceInstance(serviceInstance.GUID)
   208  		allWarnings = append(allWarnings, bindingsWarnings...)
   209  		if err != nil {
   210  			log.WithField("GUID", serviceInstance.GUID).Errorln("looking up service binding:", err)
   211  			return serviceInstanceSummary, allWarnings, err
   212  		}
   213  	} else {
   214  		log.Debug("service is user provided")
   215  		var bindingsWarnings Warnings
   216  		var err error
   217  		serviceBindings, bindingsWarnings, err = actor.GetServiceBindingsByUserProvidedServiceInstance(serviceInstance.GUID)
   218  		allWarnings = append(allWarnings, bindingsWarnings...)
   219  		if err != nil {
   220  			log.WithField("service_instance_guid", serviceInstance.GUID).Errorln("looking up service bindings:", err)
   221  			return serviceInstanceSummary, allWarnings, err
   222  		}
   223  	}
   224  
   225  	for _, serviceBinding := range serviceBindings {
   226  		log.WithFields(log.Fields{
   227  			"app_guid":             serviceBinding.AppGUID,
   228  			"service_binding_guid": serviceBinding.GUID,
   229  		}).Debug("application lookup")
   230  
   231  		app, appWarnings, err := actor.GetApplication(serviceBinding.AppGUID)
   232  		allWarnings = append(allWarnings, appWarnings...)
   233  		if err != nil {
   234  			log.WithFields(log.Fields{
   235  				"app_guid":             serviceBinding.AppGUID,
   236  				"service_binding_guid": serviceBinding.GUID,
   237  			}).Errorln("looking up application:", err)
   238  			return serviceInstanceSummary, allWarnings, err
   239  		}
   240  
   241  		serviceInstanceSummary.BoundApplications = append(
   242  			serviceInstanceSummary.BoundApplications,
   243  			BoundApplication{
   244  				AppName:            app.Name,
   245  				ServiceBindingName: serviceBinding.Name,
   246  				LastOperation:      LastOperation(serviceBinding.LastOperation),
   247  			})
   248  	}
   249  
   250  	sort.Slice(
   251  		serviceInstanceSummary.BoundApplications,
   252  		func(i, j int) bool {
   253  			return sorting.LessIgnoreCase(serviceInstanceSummary.BoundApplications[i].AppName, serviceInstanceSummary.BoundApplications[j].AppName)
   254  		})
   255  
   256  	return serviceInstanceSummary, allWarnings, nil
   257  }