github.com/DaAlbrecht/cf-cli@v0.0.0-20231128151943-1fe19bb400b9/actor/v7action/service_instance_details.go (about)

     1  package v7action
     2  
     3  import (
     4  	"code.cloudfoundry.org/cli/actor/actionerror"
     5  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccerror"
     6  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccv3"
     7  	"code.cloudfoundry.org/cli/resources"
     8  	"code.cloudfoundry.org/cli/util/extract"
     9  	"code.cloudfoundry.org/cli/util/railway"
    10  )
    11  
    12  const featureFlagServiceInstanceSharing string = "service_instance_sharing"
    13  
    14  type ServiceInstanceBoundAppCount struct {
    15  	OrgName       string
    16  	SpaceName     string
    17  	BoundAppCount int
    18  }
    19  
    20  type UsageSummaryWithSpaceAndOrg struct {
    21  	SpaceName        string
    22  	OrganizationName string
    23  	BoundAppCount    int
    24  }
    25  
    26  type SharedStatus struct {
    27  	FeatureFlagIsDisabled     bool
    28  	OfferingDisablesSharing   bool
    29  	IsSharedToOtherSpaces     bool
    30  	IsSharedFromOriginalSpace bool
    31  	UsageSummary              []UsageSummaryWithSpaceAndOrg
    32  }
    33  
    34  type ServiceInstanceParameters map[string]interface{}
    35  
    36  type ServiceInstanceUpgradeState int
    37  
    38  type ServiceInstanceUpgradeStatus struct {
    39  	State       ServiceInstanceUpgradeState
    40  	Description string
    41  }
    42  
    43  const (
    44  	ServiceInstanceUpgradeNotSupported ServiceInstanceUpgradeState = iota
    45  	ServiceInstanceUpgradeAvailable
    46  	ServiceInstanceUpgradeNotAvailable
    47  )
    48  
    49  type ServiceInstanceDetails struct {
    50  	resources.ServiceInstance
    51  	SpaceName         string
    52  	OrganizationName  string
    53  	ServiceOffering   resources.ServiceOffering
    54  	ServicePlan       resources.ServicePlan
    55  	ServiceBrokerName string
    56  	SharedStatus      SharedStatus
    57  	UpgradeStatus     ServiceInstanceUpgradeStatus
    58  	BoundApps         []resources.ServiceCredentialBinding
    59  }
    60  
    61  func (actor Actor) GetServiceInstanceDetails(serviceInstanceName string, spaceGUID string, omitApps bool) (ServiceInstanceDetails, Warnings, error) {
    62  	var serviceInstanceDetails ServiceInstanceDetails
    63  
    64  	warnings, err := railway.Sequentially(
    65  		func() (warnings ccv3.Warnings, err error) {
    66  			serviceInstanceDetails, warnings, err = actor.getServiceInstanceDetails(serviceInstanceName, spaceGUID)
    67  			return
    68  		},
    69  		func() (warnings ccv3.Warnings, err error) {
    70  			serviceInstanceDetails.SharedStatus, warnings, err = actor.getServiceInstanceSharedStatus(serviceInstanceDetails, spaceGUID)
    71  			return
    72  		},
    73  		func() (warnings ccv3.Warnings, err error) {
    74  			serviceInstanceDetails.UpgradeStatus, warnings, err = actor.getServiceInstanceUpgradeStatus(serviceInstanceDetails)
    75  			return
    76  		},
    77  		func() (warnings ccv3.Warnings, err error) {
    78  			if !omitApps {
    79  				serviceInstanceDetails.BoundApps, warnings, err = actor.getServiceInstanceBoundApps(serviceInstanceDetails.GUID)
    80  			}
    81  			return
    82  		},
    83  	)
    84  	if err != nil {
    85  		return ServiceInstanceDetails{}, Warnings(warnings), err
    86  	}
    87  
    88  	return serviceInstanceDetails, Warnings(warnings), nil
    89  }
    90  
    91  func (actor Actor) GetServiceInstanceParameters(serviceInstanceName string, spaceGUID string) (ServiceInstanceParameters, Warnings, error) {
    92  	var serviceInstance resources.ServiceInstance
    93  	var parameters ServiceInstanceParameters
    94  
    95  	warnings, err := railway.Sequentially(
    96  		func() (warnings ccv3.Warnings, err error) {
    97  			serviceInstance, _, warnings, err = actor.getServiceInstanceByNameAndSpace(serviceInstanceName, spaceGUID)
    98  			return
    99  		},
   100  		func() (warnings ccv3.Warnings, err error) {
   101  			parameters, warnings, err = actor.getServiceInstanceParameters(serviceInstance.GUID)
   102  			return
   103  		},
   104  	)
   105  	if err != nil {
   106  		return ServiceInstanceParameters{}, Warnings(warnings), err
   107  	}
   108  
   109  	return parameters, Warnings(warnings), nil
   110  }
   111  
   112  func (actor Actor) getServiceInstanceDetails(serviceInstanceName string, spaceGUID string) (ServiceInstanceDetails, ccv3.Warnings, error) {
   113  	query := []ccv3.Query{
   114  		{
   115  			Key:    ccv3.FieldsServicePlan,
   116  			Values: []string{"name", "guid"},
   117  		},
   118  		{
   119  			Key:    ccv3.FieldsServicePlanServiceOffering,
   120  			Values: []string{"name", "guid", "description", "tags", "documentation_url"},
   121  		},
   122  		{
   123  			Key:    ccv3.FieldsServicePlanServiceOfferingServiceBroker,
   124  			Values: []string{"name", "guid"},
   125  		},
   126  		{
   127  			Key:    ccv3.FieldsSpace,
   128  			Values: []string{"name", "guid"},
   129  		},
   130  		{
   131  			Key:    ccv3.FieldsSpaceOrganization,
   132  			Values: []string{"name", "guid"},
   133  		},
   134  	}
   135  
   136  	serviceInstance, included, warnings, err := actor.CloudControllerClient.GetServiceInstanceByNameAndSpace(serviceInstanceName, spaceGUID, query...)
   137  	switch err.(type) {
   138  	case nil:
   139  	case ccerror.ServiceInstanceNotFoundError:
   140  		return ServiceInstanceDetails{}, warnings, actionerror.ServiceInstanceNotFoundError{Name: serviceInstanceName}
   141  	default:
   142  		return ServiceInstanceDetails{}, warnings, err
   143  	}
   144  
   145  	result := ServiceInstanceDetails{
   146  		ServiceInstance:   serviceInstance,
   147  		ServicePlan:       extractServicePlan(included),
   148  		ServiceOffering:   extractServiceOffering(included),
   149  		ServiceBrokerName: extract.First("Name", included.ServiceBrokers),
   150  		SpaceName:         extract.First("Name", included.Spaces),
   151  		OrganizationName:  extract.First("Name", included.Organizations),
   152  	}
   153  
   154  	return result, warnings, nil
   155  }
   156  
   157  func (actor Actor) getServiceInstanceParameters(serviceInstanceGUID string) (ServiceInstanceParameters, ccv3.Warnings, error) {
   158  	params, warnings, err := actor.CloudControllerClient.GetServiceInstanceParameters(serviceInstanceGUID)
   159  
   160  	switch err := err.(type) {
   161  	case nil:
   162  		return ServiceInstanceParameters(params), warnings, nil
   163  	case ccerror.ResourceNotFoundError,
   164  		ccerror.ServiceInstanceParametersFetchNotSupportedError:
   165  		return ServiceInstanceParameters{}, warnings, actionerror.ServiceInstanceParamsFetchingNotSupportedError{}
   166  	default:
   167  		return ServiceInstanceParameters{}, warnings, err
   168  	}
   169  }
   170  
   171  func (actor Actor) getServiceInstanceSharedStatus(serviceInstanceDetails ServiceInstanceDetails, targetedSpace string) (SharedStatus, ccv3.Warnings, error) {
   172  	if serviceInstanceDetails.Type != resources.ManagedServiceInstance {
   173  		return SharedStatus{}, nil, nil
   174  	}
   175  
   176  	if targetedSpace != serviceInstanceDetails.SpaceGUID {
   177  		return SharedStatus{IsSharedFromOriginalSpace: true}, nil, nil
   178  	}
   179  
   180  	var (
   181  		featureFlag             resources.FeatureFlag
   182  		offeringDisablesSharing bool
   183  		sharedSpaces            []ccv3.SpaceWithOrganization
   184  		usageSummaries          []resources.ServiceInstanceUsageSummary
   185  	)
   186  
   187  	warnings, err := railway.Sequentially(
   188  		func() (warnings ccv3.Warnings, err error) {
   189  			featureFlag, warnings, err = actor.CloudControllerClient.GetFeatureFlag(featureFlagServiceInstanceSharing)
   190  			return
   191  		},
   192  		func() (warnings ccv3.Warnings, err error) {
   193  			offeringDisablesSharing, warnings, err = actor.getOfferingSharingDetails(serviceInstanceDetails.ServiceOffering.GUID)
   194  			return
   195  		},
   196  		func() (warnings ccv3.Warnings, err error) {
   197  			sharedSpaces, warnings, err = actor.CloudControllerClient.GetServiceInstanceSharedSpaces(serviceInstanceDetails.GUID)
   198  			return
   199  		},
   200  		func() (warnings ccv3.Warnings, err error) {
   201  			if len(sharedSpaces) > 0 {
   202  				usageSummaries, warnings, err = actor.CloudControllerClient.GetServiceInstanceUsageSummary(serviceInstanceDetails.GUID)
   203  			}
   204  			return
   205  		},
   206  	)
   207  	if err != nil {
   208  		return SharedStatus{}, warnings, err
   209  	}
   210  
   211  	sharedStatus := SharedStatus{
   212  		IsSharedToOtherSpaces:   len(sharedSpaces) > 0,
   213  		OfferingDisablesSharing: offeringDisablesSharing,
   214  		FeatureFlagIsDisabled:   !featureFlag.Enabled,
   215  		UsageSummary:            buildUsageSummary(sharedSpaces, usageSummaries),
   216  	}
   217  
   218  	return sharedStatus, warnings, nil
   219  }
   220  
   221  func (actor Actor) getOfferingSharingDetails(serviceOfferingGUID string) (bool, ccv3.Warnings, error) {
   222  	serviceOffering, serviceOfferingWarning, err :=
   223  		actor.CloudControllerClient.GetServiceOfferingByGUID(serviceOfferingGUID)
   224  
   225  	switch err := err.(type) {
   226  	case nil:
   227  		return !serviceOffering.AllowsInstanceSharing, serviceOfferingWarning, nil
   228  	case ccerror.ServiceOfferingNotFoundError:
   229  		return false, serviceOfferingWarning, nil
   230  	default:
   231  		return false, serviceOfferingWarning, err
   232  	}
   233  }
   234  
   235  func (actor Actor) getServiceInstanceUpgradeStatus(serviceInstanceDetails ServiceInstanceDetails) (ServiceInstanceUpgradeStatus, ccv3.Warnings, error) {
   236  	if !serviceInstanceDetails.UpgradeAvailable.Value {
   237  		if serviceInstanceDetails.MaintenanceInfoVersion == "" {
   238  			return ServiceInstanceUpgradeStatus{State: ServiceInstanceUpgradeNotSupported}, nil, nil
   239  		}
   240  		return ServiceInstanceUpgradeStatus{State: ServiceInstanceUpgradeNotAvailable}, nil, nil
   241  	}
   242  
   243  	servicePlan, warnings, err := actor.CloudControllerClient.GetServicePlanByGUID(serviceInstanceDetails.ServicePlanGUID)
   244  	switch err.(type) {
   245  	case nil:
   246  		return ServiceInstanceUpgradeStatus{
   247  			State:       ServiceInstanceUpgradeAvailable,
   248  			Description: servicePlan.MaintenanceInfoDescription,
   249  		}, warnings, nil
   250  	case ccerror.ServicePlanNotFound:
   251  		return ServiceInstanceUpgradeStatus{
   252  			State:       ServiceInstanceUpgradeAvailable,
   253  			Description: "No upgrade details where found",
   254  		}, warnings, nil
   255  	default:
   256  		return ServiceInstanceUpgradeStatus{}, warnings, err
   257  	}
   258  }
   259  
   260  func (actor Actor) getServiceInstanceBoundApps(serviceInstanceGUID string) ([]resources.ServiceCredentialBinding, ccv3.Warnings, error) {
   261  	return actor.CloudControllerClient.GetServiceCredentialBindings(
   262  		ccv3.Query{Key: ccv3.Include, Values: []string{"app"}},
   263  		ccv3.Query{Key: ccv3.ServiceInstanceGUIDFilter, Values: []string{serviceInstanceGUID}},
   264  		ccv3.Query{Key: ccv3.TypeFilter, Values: []string{"app"}},
   265  	)
   266  }
   267  
   268  func extractServicePlan(included ccv3.IncludedResources) resources.ServicePlan {
   269  	if len(included.ServicePlans) == 1 {
   270  		return included.ServicePlans[0]
   271  	}
   272  
   273  	return resources.ServicePlan{}
   274  }
   275  
   276  func extractServiceOffering(included ccv3.IncludedResources) resources.ServiceOffering {
   277  	if len(included.ServiceOfferings) == 1 {
   278  		return included.ServiceOfferings[0]
   279  	}
   280  
   281  	return resources.ServiceOffering{}
   282  }
   283  
   284  func buildUsageSummary(sharedSpaces []ccv3.SpaceWithOrganization, usageSummaries []resources.ServiceInstanceUsageSummary) []UsageSummaryWithSpaceAndOrg {
   285  	var spaceGUIDToNames = make(map[string]ccv3.SpaceWithOrganization)
   286  	var sharedSpacesUsage []UsageSummaryWithSpaceAndOrg
   287  
   288  	for _, sharedSpace := range sharedSpaces {
   289  		spaceGUIDToNames[sharedSpace.SpaceGUID] = sharedSpace
   290  	}
   291  	for _, usageSummary := range usageSummaries {
   292  		summary := UsageSummaryWithSpaceAndOrg{
   293  			SpaceName:        spaceGUIDToNames[usageSummary.SpaceGUID].SpaceName,
   294  			OrganizationName: spaceGUIDToNames[usageSummary.SpaceGUID].OrganizationName,
   295  			BoundAppCount:    usageSummary.BoundAppCount,
   296  		}
   297  		sharedSpacesUsage = append(sharedSpacesUsage, summary)
   298  	}
   299  	return sharedSpacesUsage
   300  }