github.com/lukasheimann/cloudfoundrycli@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 }