github.com/sleungcy/cli@v7.1.0+incompatible/actor/v7action/service_access.go (about)

     1  package v7action
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"sort"
     7  
     8  	"code.cloudfoundry.org/cli/actor/actionerror"
     9  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccv3"
    10  	"code.cloudfoundry.org/cli/resources"
    11  )
    12  
    13  type ServicePlanWithSpaceAndOrganization ccv3.ServicePlanWithSpaceAndOrganization
    14  
    15  type ServicePlanAccess struct {
    16  	BrokerName          string
    17  	ServiceOfferingName string
    18  	ServicePlanName     string
    19  	VisibilityType      resources.ServicePlanVisibilityType
    20  	VisibilityDetails   []string
    21  }
    22  
    23  type SkippedPlans []string
    24  
    25  type offeringDetails struct{ offeringName, brokerName string }
    26  
    27  func (actor *Actor) GetServiceAccess(offeringName, brokerName, orgName string) ([]ServicePlanAccess, Warnings, error) {
    28  	var orgGUID string
    29  	var allWarnings Warnings
    30  	if orgName != "" {
    31  		org, orgWarnings, err := actor.GetOrganizationByName(orgName)
    32  		if err != nil {
    33  			return nil, orgWarnings, err
    34  		}
    35  		allWarnings = append(allWarnings, orgWarnings...)
    36  
    37  		orgGUID = org.GUID
    38  	}
    39  
    40  	plansQuery := buildPlansFilterForGet(offeringName, brokerName, orgGUID)
    41  
    42  	offerings, offeringsWarnings, err := actor.getServiceOfferings(offeringName, brokerName)
    43  	allWarnings = append(allWarnings, offeringsWarnings...)
    44  	if err != nil {
    45  		return nil, allWarnings, err
    46  	}
    47  
    48  	plans, plansWarnings, err := actor.CloudControllerClient.GetServicePlansWithSpaceAndOrganization(plansQuery...)
    49  	allWarnings = append(allWarnings, plansWarnings...)
    50  	if err != nil {
    51  		return nil, allWarnings, err
    52  	}
    53  
    54  	var result []ServicePlanAccess
    55  	for _, plan := range plans {
    56  		if offering, ok := offerings[plan.ServiceOfferingGUID]; ok {
    57  			visibilityDetails, warnings, err := actor.getServicePlanVisibilityDetails(ServicePlanWithSpaceAndOrganization(plan))
    58  			allWarnings = append(allWarnings, warnings...)
    59  			if err != nil {
    60  				return nil, allWarnings, err
    61  			}
    62  
    63  			result = append(result, ServicePlanAccess{
    64  				ServicePlanName:     plan.Name,
    65  				VisibilityType:      plan.VisibilityType,
    66  				VisibilityDetails:   visibilityDetails,
    67  				ServiceOfferingName: offering.offeringName,
    68  				BrokerName:          offering.brokerName,
    69  			})
    70  		}
    71  	}
    72  
    73  	sort.Slice(result, func(i, j int) bool {
    74  		if result[i].BrokerName != result[j].BrokerName {
    75  			return result[i].BrokerName < result[j].BrokerName
    76  		}
    77  		if result[i].ServiceOfferingName != result[j].ServiceOfferingName {
    78  			return result[i].ServiceOfferingName < result[j].ServiceOfferingName
    79  		}
    80  		return result[i].ServicePlanName < result[j].ServicePlanName
    81  	})
    82  
    83  	return result, allWarnings, err
    84  }
    85  
    86  func (actor *Actor) EnableServiceAccess(offeringName, brokerName, orgName, planName string) (SkippedPlans, Warnings, error) {
    87  	var allWarnings Warnings
    88  
    89  	offering, offeringWarnings, err := actor.CloudControllerClient.GetServiceOfferingByNameAndBroker(offeringName, brokerName)
    90  	allWarnings = append(allWarnings, offeringWarnings...)
    91  	if err != nil {
    92  		return nil, allWarnings, actionerror.EnrichAPIErrors(err)
    93  	}
    94  
    95  	plansQuery := buildPlansFilterForUpdate(offering.GUID, planName)
    96  	plans, planWarnings, err := actor.CloudControllerClient.GetServicePlans(plansQuery...)
    97  	allWarnings = append(allWarnings, planWarnings...)
    98  	if err != nil {
    99  		return nil, allWarnings, err
   100  	}
   101  
   102  	if len(plans) == 0 {
   103  		return nil, allWarnings, actionerror.ServicePlanNotFoundError{
   104  			PlanName:     planName,
   105  			OfferingName: offeringName,
   106  		}
   107  	}
   108  
   109  	if offeringIsSpaceScoped(plans) {
   110  		return nil, allWarnings, actionerror.ServicePlanVisibilityTypeError{}
   111  	}
   112  
   113  	visibility := resources.ServicePlanVisibility{Type: resources.ServicePlanVisibilityPublic}
   114  	if orgName != "" {
   115  		org, orgWarnings, err := actor.GetOrganizationByName(orgName)
   116  		allWarnings = append(allWarnings, orgWarnings...)
   117  		if err != nil {
   118  			return nil, allWarnings, err
   119  		}
   120  
   121  		visibility.Type = resources.ServicePlanVisibilityOrganization
   122  		visibility.Organizations = []resources.ServicePlanVisibilityDetail{{GUID: org.GUID}}
   123  	}
   124  
   125  	var skipped SkippedPlans
   126  	for _, plan := range plans {
   127  		if plan.VisibilityType == resources.ServicePlanVisibilityPublic && visibility.Type == resources.ServicePlanVisibilityOrganization {
   128  			skipped = append(skipped, plan.Name)
   129  			continue
   130  		}
   131  
   132  		_, visibilityWarnings, err := actor.CloudControllerClient.UpdateServicePlanVisibility(
   133  			plan.GUID,
   134  			visibility,
   135  		)
   136  		allWarnings = append(allWarnings, visibilityWarnings...)
   137  		if err != nil {
   138  			return nil, allWarnings, err
   139  		}
   140  	}
   141  
   142  	return skipped, allWarnings, nil
   143  }
   144  
   145  func (actor *Actor) DisableServiceAccess(offeringName, brokerName, orgName, planName string) (SkippedPlans, Warnings, error) {
   146  	var allWarnings Warnings
   147  
   148  	offering, offeringWarnings, err := actor.CloudControllerClient.GetServiceOfferingByNameAndBroker(offeringName, brokerName)
   149  	allWarnings = append(allWarnings, offeringWarnings...)
   150  	if err != nil {
   151  		return SkippedPlans{}, allWarnings, actionerror.EnrichAPIErrors(err)
   152  	}
   153  
   154  	plansQuery := buildPlansFilterForUpdate(offering.GUID, planName)
   155  	plans, planWarnings, err := actor.CloudControllerClient.GetServicePlans(plansQuery...)
   156  	allWarnings = append(allWarnings, planWarnings...)
   157  	if err != nil {
   158  		return SkippedPlans{}, allWarnings, err
   159  	}
   160  
   161  	if len(plans) == 0 {
   162  		return SkippedPlans{}, allWarnings, actionerror.ServicePlanNotFoundError{
   163  			PlanName:     planName,
   164  			OfferingName: offeringName,
   165  		}
   166  	}
   167  
   168  	if offeringIsSpaceScoped(plans) {
   169  		return SkippedPlans{}, allWarnings, actionerror.ServicePlanVisibilityTypeError{}
   170  	}
   171  
   172  	var (
   173  		disableWarnings Warnings
   174  		skipped         SkippedPlans
   175  	)
   176  
   177  	if orgName != "" {
   178  		skipped, disableWarnings, err = actor.disableOrganizationServiceAccess(plans, orgName)
   179  	} else {
   180  		skipped, disableWarnings, err = actor.disableAllServiceAccess(plans)
   181  	}
   182  
   183  	allWarnings = append(allWarnings, disableWarnings...)
   184  	return skipped, allWarnings, err
   185  }
   186  
   187  func (actor *Actor) disableAllServiceAccess(plans []resources.ServicePlan) (SkippedPlans, Warnings, error) {
   188  	var (
   189  		allWarnings Warnings
   190  		skipped     SkippedPlans
   191  	)
   192  
   193  	visibility := resources.ServicePlanVisibility{Type: resources.ServicePlanVisibilityAdmin}
   194  	for _, plan := range plans {
   195  		if plan.VisibilityType == resources.ServicePlanVisibilityAdmin {
   196  			skipped = append(skipped, plan.Name)
   197  			continue
   198  		}
   199  
   200  		_, visibilityWarnings, err := actor.CloudControllerClient.UpdateServicePlanVisibility(
   201  			plan.GUID,
   202  			visibility,
   203  		)
   204  		allWarnings = append(allWarnings, visibilityWarnings...)
   205  		if err != nil {
   206  			return skipped, allWarnings, err
   207  		}
   208  	}
   209  	return skipped, allWarnings, nil
   210  }
   211  
   212  func (actor *Actor) disableOrganizationServiceAccess(plans []resources.ServicePlan, orgName string) (SkippedPlans, Warnings, error) {
   213  	var allWarnings Warnings
   214  
   215  	org, orgWarnings, err := actor.GetOrganizationByName(orgName)
   216  	allWarnings = append(allWarnings, orgWarnings...)
   217  	if err != nil {
   218  		return nil, allWarnings, err
   219  	}
   220  
   221  	for _, plan := range plans {
   222  		if plan.VisibilityType == resources.ServicePlanVisibilityPublic {
   223  			return nil, allWarnings, errors.New("Cannot remove organization level access for public plans.")
   224  		}
   225  	}
   226  
   227  	var skipped SkippedPlans
   228  	for _, plan := range plans {
   229  		if plan.VisibilityType == resources.ServicePlanVisibilityAdmin {
   230  			skipped = append(skipped, plan.Name)
   231  			continue
   232  		}
   233  
   234  		deleteWarnings, err := actor.CloudControllerClient.DeleteServicePlanVisibility(plan.GUID, org.GUID)
   235  		allWarnings = append(allWarnings, deleteWarnings...)
   236  		if err != nil {
   237  			return skipped, allWarnings, err
   238  		}
   239  	}
   240  
   241  	return skipped, allWarnings, nil
   242  }
   243  
   244  func (actor *Actor) getServiceOfferings(service, broker string) (map[string]offeringDetails, Warnings, error) {
   245  	var offeringsQuery []ccv3.Query
   246  
   247  	if broker != "" {
   248  		offeringsQuery = append(offeringsQuery, ccv3.Query{
   249  			Key:    ccv3.ServiceBrokerNamesFilter,
   250  			Values: []string{broker},
   251  		})
   252  	}
   253  
   254  	if service != "" {
   255  		offeringsQuery = append(offeringsQuery, ccv3.Query{
   256  			Key:    ccv3.NameFilter,
   257  			Values: []string{service},
   258  		})
   259  	}
   260  
   261  	serviceOfferings, warnings, err := actor.CloudControllerClient.GetServiceOfferings(offeringsQuery...)
   262  	if err != nil {
   263  		return nil, Warnings(warnings), err
   264  	}
   265  	if len(serviceOfferings) == 0 && len(offeringsQuery) > 0 {
   266  		return nil, Warnings(warnings), actionerror.ServiceNotFoundError{Name: service, Broker: broker}
   267  	}
   268  
   269  	offerings := make(map[string]offeringDetails)
   270  	for _, o := range serviceOfferings {
   271  		offerings[o.GUID] = offeringDetails{
   272  			offeringName: o.Name,
   273  			brokerName:   o.ServiceBrokerName,
   274  		}
   275  	}
   276  	return offerings, Warnings(warnings), err
   277  }
   278  
   279  func (actor *Actor) getServicePlanVisibilityDetails(plan ServicePlanWithSpaceAndOrganization) (names []string, warnings Warnings, err error) {
   280  	if plan.VisibilityType == resources.ServicePlanVisibilityOrganization {
   281  		result, vwarn, err := actor.CloudControllerClient.GetServicePlanVisibility(plan.GUID)
   282  		warnings = Warnings(vwarn)
   283  		if err != nil {
   284  			return nil, warnings, err
   285  		}
   286  
   287  		for _, organization := range result.Organizations {
   288  			names = append(names, organization.Name)
   289  		}
   290  	}
   291  
   292  	if plan.VisibilityType == resources.ServicePlanVisibilitySpace {
   293  		names = []string{fmt.Sprintf("%s (org: %s)", plan.SpaceName, plan.OrganizationName)}
   294  	}
   295  
   296  	return names, warnings, nil
   297  }
   298  
   299  func buildPlansFilterForGet(offeringName, brokerName, orgGUID string) (query []ccv3.Query) {
   300  	if offeringName != "" {
   301  		query = append(query, ccv3.Query{
   302  			Key:    ccv3.ServiceOfferingNamesFilter,
   303  			Values: []string{offeringName},
   304  		})
   305  	}
   306  
   307  	if brokerName != "" {
   308  		query = append(query, ccv3.Query{
   309  			Key:    ccv3.ServiceBrokerNamesFilter,
   310  			Values: []string{brokerName},
   311  		})
   312  	}
   313  
   314  	if orgGUID != "" {
   315  		query = append(query, ccv3.Query{
   316  			Key:    ccv3.OrganizationGUIDFilter,
   317  			Values: []string{orgGUID},
   318  		})
   319  	}
   320  
   321  	return query
   322  }
   323  
   324  func buildPlansFilterForUpdate(offeringGUID, planName string) []ccv3.Query {
   325  	plansQuery := []ccv3.Query{{
   326  		Key:    ccv3.ServiceOfferingGUIDsFilter,
   327  		Values: []string{offeringGUID},
   328  	}}
   329  
   330  	if planName != "" {
   331  		plansQuery = append(plansQuery, ccv3.Query{
   332  			Key:    ccv3.NameFilter,
   333  			Values: []string{planName},
   334  		})
   335  	}
   336  
   337  	return plansQuery
   338  }
   339  
   340  func offeringIsSpaceScoped(plans []resources.ServicePlan) bool {
   341  	// All plans from a space scoped offering will have the same visibility type
   342  	return plans[0].VisibilityType == resources.ServicePlanVisibilitySpace
   343  }