github.com/swisscom/cloudfoundry-cli@v7.1.0+incompatible/actor/v2action/security_group.go (about) 1 package v2action 2 3 import ( 4 "sort" 5 6 "code.cloudfoundry.org/cli/actor/actionerror" 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 ) 11 12 // SecurityGroup represents a CF SecurityGroup. 13 type SecurityGroup ccv2.SecurityGroup 14 15 // SecurityGroupWithOrganizationSpaceAndLifecycle represents a security group with 16 // organization and space information. 17 type SecurityGroupWithOrganizationSpaceAndLifecycle struct { 18 SecurityGroup *SecurityGroup 19 Organization *Organization 20 Space *Space 21 Lifecycle constant.SecurityGroupLifecycle 22 } 23 24 func (actor Actor) BindSecurityGroupToSpace(securityGroupGUID string, spaceGUID string, lifecycle constant.SecurityGroupLifecycle) (Warnings, error) { 25 var ( 26 warnings ccv2.Warnings 27 err error 28 ) 29 30 switch lifecycle { 31 case constant.SecurityGroupLifecycleRunning: 32 warnings, err = actor.CloudControllerClient.UpdateSecurityGroupSpace(securityGroupGUID, spaceGUID) 33 case constant.SecurityGroupLifecycleStaging: 34 warnings, err = actor.CloudControllerClient.UpdateSecurityGroupStagingSpace(securityGroupGUID, spaceGUID) 35 default: 36 err = actionerror.InvalidLifecycleError{Lifecycle: string(lifecycle)} 37 } 38 39 return Warnings(warnings), err 40 } 41 42 func (actor Actor) GetSecurityGroupByName(securityGroupName string) (SecurityGroup, Warnings, error) { 43 securityGroups, warnings, err := actor.CloudControllerClient.GetSecurityGroups(ccv2.Filter{ 44 Type: constant.NameFilter, 45 Operator: constant.EqualOperator, 46 Values: []string{securityGroupName}, 47 }) 48 49 if err != nil { 50 return SecurityGroup{}, Warnings(warnings), err 51 } 52 53 if len(securityGroups) == 0 { 54 return SecurityGroup{}, Warnings(warnings), actionerror.SecurityGroupNotFoundError{Name: securityGroupName} 55 } 56 57 securityGroup := SecurityGroup{ 58 Name: securityGroups[0].Name, 59 GUID: securityGroups[0].GUID, 60 } 61 return securityGroup, Warnings(warnings), nil 62 } 63 64 type SpaceWithLifecycle struct { 65 ccv2.Space 66 Lifecycle constant.SecurityGroupLifecycle 67 } 68 69 func (actor Actor) getSecurityGroupSpacesAndAssignedLifecycles(securityGroupGUID string, includeStaging bool) ([]SpaceWithLifecycle, Warnings, error) { 70 var ( 71 spacesWithLifecycles []SpaceWithLifecycle 72 allWarnings Warnings 73 ) 74 75 runningSpaces, warnings, err := actor.CloudControllerClient.GetSecurityGroupSpaces(securityGroupGUID) 76 allWarnings = append(allWarnings, warnings...) 77 if err != nil { 78 return nil, Warnings(allWarnings), err 79 } 80 81 for _, space := range runningSpaces { 82 spacesWithLifecycles = append(spacesWithLifecycles, SpaceWithLifecycle{Space: space, Lifecycle: constant.SecurityGroupLifecycleRunning}) 83 } 84 85 if includeStaging { 86 stagingSpaces, warnings, err := actor.CloudControllerClient.GetSecurityGroupStagingSpaces(securityGroupGUID) 87 allWarnings = append(allWarnings, warnings...) 88 if err != nil { 89 return nil, Warnings(allWarnings), err 90 } 91 92 for _, space := range stagingSpaces { 93 spacesWithLifecycles = append(spacesWithLifecycles, SpaceWithLifecycle{Space: space, Lifecycle: constant.SecurityGroupLifecycleStaging}) 94 } 95 } 96 97 return spacesWithLifecycles, allWarnings, nil 98 } 99 100 // GetSecurityGroupsWithOrganizationSpaceAndLifecycle returns a list of security groups 101 // with org and space information, optionally including staging spaces. 102 func (actor Actor) GetSecurityGroupsWithOrganizationSpaceAndLifecycle(includeStaging bool) ([]SecurityGroupWithOrganizationSpaceAndLifecycle, Warnings, error) { 103 securityGroups, allWarnings, err := actor.CloudControllerClient.GetSecurityGroups() 104 if err != nil { 105 return nil, Warnings(allWarnings), err 106 } 107 108 cachedOrgs := make(map[string]Organization) 109 var secGroupOrgSpaces []SecurityGroupWithOrganizationSpaceAndLifecycle 110 111 for _, s := range securityGroups { 112 securityGroup := SecurityGroup{ 113 GUID: s.GUID, 114 Name: s.Name, 115 RunningDefault: s.RunningDefault, 116 StagingDefault: s.StagingDefault, 117 } 118 119 var getErr error 120 spaces, warnings, getErr := actor.getSecurityGroupSpacesAndAssignedLifecycles(s.GUID, includeStaging) 121 allWarnings = append(allWarnings, warnings...) 122 if getErr != nil { 123 if _, ok := getErr.(ccerror.ResourceNotFoundError); ok { 124 allWarnings = append(allWarnings, getErr.Error()) 125 continue 126 } 127 return nil, Warnings(allWarnings), getErr 128 } 129 130 if securityGroup.RunningDefault { 131 secGroupOrgSpaces = append(secGroupOrgSpaces, 132 SecurityGroupWithOrganizationSpaceAndLifecycle{ 133 SecurityGroup: &securityGroup, 134 Organization: &Organization{}, 135 Space: &Space{}, 136 Lifecycle: constant.SecurityGroupLifecycleRunning, 137 }) 138 } 139 140 if securityGroup.StagingDefault { 141 secGroupOrgSpaces = append(secGroupOrgSpaces, 142 SecurityGroupWithOrganizationSpaceAndLifecycle{ 143 SecurityGroup: &securityGroup, 144 Organization: &Organization{}, 145 Space: &Space{}, 146 Lifecycle: constant.SecurityGroupLifecycleStaging, 147 }) 148 } 149 150 if len(spaces) == 0 { 151 if !securityGroup.RunningDefault && !securityGroup.StagingDefault { 152 secGroupOrgSpaces = append(secGroupOrgSpaces, 153 SecurityGroupWithOrganizationSpaceAndLifecycle{ 154 SecurityGroup: &securityGroup, 155 Organization: &Organization{}, 156 Space: &Space{}, 157 }) 158 } 159 160 continue 161 } 162 163 for _, sp := range spaces { 164 space := Space{ 165 GUID: sp.GUID, 166 Name: sp.Name, 167 } 168 169 var org Organization 170 171 if cached, ok := cachedOrgs[sp.OrganizationGUID]; ok { 172 org = cached 173 } else { 174 var getOrgErr error 175 o, warnings, getOrgErr := actor.CloudControllerClient.GetOrganization(sp.OrganizationGUID) 176 allWarnings = append(allWarnings, warnings...) 177 if getOrgErr != nil { 178 if _, ok := getOrgErr.(ccerror.ResourceNotFoundError); ok { 179 allWarnings = append(allWarnings, getOrgErr.Error()) 180 continue 181 } 182 return nil, Warnings(allWarnings), getOrgErr 183 } 184 185 org = Organization{ 186 GUID: o.GUID, 187 Name: o.Name, 188 } 189 cachedOrgs[org.GUID] = org 190 } 191 192 secGroupOrgSpaces = append(secGroupOrgSpaces, 193 SecurityGroupWithOrganizationSpaceAndLifecycle{ 194 SecurityGroup: &securityGroup, 195 Organization: &org, 196 Space: &space, 197 Lifecycle: sp.Lifecycle, 198 }) 199 } 200 } 201 202 // Sort the results alphabetically by security group, then org, then space 203 sort.Slice(secGroupOrgSpaces, 204 func(i, j int) bool { 205 switch { 206 case secGroupOrgSpaces[i].SecurityGroup.Name < secGroupOrgSpaces[j].SecurityGroup.Name: 207 return true 208 case secGroupOrgSpaces[i].SecurityGroup.Name > secGroupOrgSpaces[j].SecurityGroup.Name: 209 return false 210 case secGroupOrgSpaces[i].SecurityGroup.RunningDefault && !secGroupOrgSpaces[i].SecurityGroup.RunningDefault: 211 return true 212 case !secGroupOrgSpaces[i].SecurityGroup.RunningDefault && secGroupOrgSpaces[i].SecurityGroup.RunningDefault: 213 return false 214 case secGroupOrgSpaces[i].Organization.Name < secGroupOrgSpaces[j].Organization.Name: 215 return true 216 case secGroupOrgSpaces[i].Organization.Name > secGroupOrgSpaces[j].Organization.Name: 217 return false 218 case secGroupOrgSpaces[i].SecurityGroup.StagingDefault && !secGroupOrgSpaces[i].SecurityGroup.StagingDefault: 219 return true 220 case !secGroupOrgSpaces[i].SecurityGroup.StagingDefault && secGroupOrgSpaces[i].SecurityGroup.StagingDefault: 221 return false 222 case secGroupOrgSpaces[i].Space.Name < secGroupOrgSpaces[j].Space.Name: 223 return true 224 case secGroupOrgSpaces[i].Space.Name > secGroupOrgSpaces[j].Space.Name: 225 return false 226 } 227 228 return secGroupOrgSpaces[i].Lifecycle < secGroupOrgSpaces[j].Lifecycle 229 }) 230 231 return secGroupOrgSpaces, Warnings(allWarnings), nil 232 } 233 234 // GetSpaceRunningSecurityGroupsBySpace returns a list of all security groups 235 // bound to this space in the 'running' lifecycle phase. 236 func (actor Actor) GetSpaceRunningSecurityGroupsBySpace(spaceGUID string) ([]SecurityGroup, Warnings, error) { 237 ccv2SecurityGroups, warnings, err := actor.CloudControllerClient.GetSpaceSecurityGroups(spaceGUID) 238 return processSecurityGroups(spaceGUID, ccv2SecurityGroups, Warnings(warnings), err) 239 } 240 241 // GetSpaceStagingSecurityGroupsBySpace returns a list of all security groups 242 // bound to this space in the 'staging' lifecycle phase. with an optional 243 func (actor Actor) GetSpaceStagingSecurityGroupsBySpace(spaceGUID string) ([]SecurityGroup, Warnings, error) { 244 ccv2SecurityGroups, warnings, err := actor.CloudControllerClient.GetSpaceStagingSecurityGroups(spaceGUID) 245 return processSecurityGroups(spaceGUID, ccv2SecurityGroups, Warnings(warnings), err) 246 } 247 248 func (actor Actor) UnbindSecurityGroupByNameAndSpace(securityGroupName string, spaceGUID string, lifecycle constant.SecurityGroupLifecycle) (Warnings, error) { 249 if lifecycle != constant.SecurityGroupLifecycleRunning && lifecycle != constant.SecurityGroupLifecycleStaging { 250 return nil, actionerror.InvalidLifecycleError{Lifecycle: string(lifecycle)} 251 } 252 253 var allWarnings Warnings 254 255 securityGroup, warnings, err := actor.GetSecurityGroupByName(securityGroupName) 256 257 allWarnings = append(allWarnings, warnings...) 258 if err != nil { 259 return allWarnings, err 260 } 261 262 warnings, err = actor.unbindSecurityGroupAndSpace(securityGroup, spaceGUID, lifecycle) 263 allWarnings = append(allWarnings, warnings...) 264 return allWarnings, err 265 } 266 267 func (actor Actor) UnbindSecurityGroupByNameOrganizationNameAndSpaceName(securityGroupName string, orgName string, spaceName string, lifecycle constant.SecurityGroupLifecycle) (Warnings, error) { 268 if lifecycle != constant.SecurityGroupLifecycleRunning && lifecycle != constant.SecurityGroupLifecycleStaging { 269 return nil, actionerror.InvalidLifecycleError{Lifecycle: string(lifecycle)} 270 } 271 272 var allWarnings Warnings 273 274 securityGroup, warnings, err := actor.GetSecurityGroupByName(securityGroupName) 275 allWarnings = append(allWarnings, warnings...) 276 if err != nil { 277 return allWarnings, err 278 } 279 280 org, warnings, err := actor.GetOrganizationByName(orgName) 281 allWarnings = append(allWarnings, warnings...) 282 if err != nil { 283 return allWarnings, err 284 } 285 286 space, warnings, err := actor.GetSpaceByOrganizationAndName(org.GUID, spaceName) 287 allWarnings = append(allWarnings, warnings...) 288 if err != nil { 289 return allWarnings, err 290 } 291 292 warnings, err = actor.unbindSecurityGroupAndSpace(securityGroup, space.GUID, lifecycle) 293 allWarnings = append(allWarnings, warnings...) 294 return allWarnings, err 295 } 296 297 func (actor Actor) unbindSecurityGroupAndSpace(securityGroup SecurityGroup, spaceGUID string, lifecycle constant.SecurityGroupLifecycle) (Warnings, error) { 298 if lifecycle == constant.SecurityGroupLifecycleRunning { 299 return actor.doUnbind(securityGroup, spaceGUID, lifecycle, 300 actor.isRunningSecurityGroupBoundToSpace, 301 actor.isStagingSecurityGroupBoundToSpace, 302 actor.CloudControllerClient.DeleteSecurityGroupSpace) 303 } 304 305 return actor.doUnbind(securityGroup, spaceGUID, lifecycle, 306 actor.isStagingSecurityGroupBoundToSpace, 307 actor.isRunningSecurityGroupBoundToSpace, 308 actor.CloudControllerClient.DeleteSecurityGroupStagingSpace) 309 } 310 311 func (Actor) doUnbind(securityGroup SecurityGroup, 312 spaceGUID string, 313 lifecycle constant.SecurityGroupLifecycle, 314 requestedPhaseSecurityGroupBoundToSpace func(string, string) (bool, Warnings, error), 315 otherPhaseSecurityGroupBoundToSpace func(string, string) (bool, Warnings, error), 316 removeSpaceFromPhaseSecurityGroup func(string, string) (ccv2.Warnings, error)) (Warnings, error) { 317 318 requestedPhaseBound, allWarnings, err := requestedPhaseSecurityGroupBoundToSpace(securityGroup.Name, spaceGUID) 319 if err != nil { 320 return allWarnings, err 321 } 322 323 if !requestedPhaseBound { 324 otherBound, warnings, otherr := otherPhaseSecurityGroupBoundToSpace(securityGroup.Name, spaceGUID) 325 allWarnings = append(allWarnings, warnings...) 326 327 if otherr != nil { 328 return allWarnings, otherr 329 } else if otherBound { 330 return allWarnings, actionerror.SecurityGroupNotBoundError{Name: securityGroup.Name, Lifecycle: lifecycle} 331 } else { 332 return allWarnings, nil 333 } 334 } 335 336 ccv2Warnings, err := removeSpaceFromPhaseSecurityGroup(securityGroup.GUID, spaceGUID) 337 allWarnings = append(allWarnings, Warnings(ccv2Warnings)...) 338 return allWarnings, err 339 } 340 341 func extractSecurityGroupRules(securityGroup SecurityGroup, lifecycle constant.SecurityGroupLifecycle) []SecurityGroupRule { 342 securityGroupRules := make([]SecurityGroupRule, len(securityGroup.Rules)) 343 344 for i, rule := range securityGroup.Rules { 345 securityGroupRules[i] = SecurityGroupRule{ 346 Name: securityGroup.Name, 347 Description: rule.Description, 348 Destination: rule.Destination, 349 Lifecycle: lifecycle, 350 Ports: rule.Ports, 351 Protocol: rule.Protocol, 352 } 353 } 354 355 return securityGroupRules 356 } 357 358 func processSecurityGroups(spaceGUID string, ccv2SecurityGroups []ccv2.SecurityGroup, warnings Warnings, err error) ([]SecurityGroup, Warnings, error) { 359 if err != nil { 360 switch err.(type) { 361 case ccerror.ResourceNotFoundError: 362 return []SecurityGroup{}, warnings, actionerror.SpaceNotFoundError{GUID: spaceGUID} 363 default: 364 return []SecurityGroup{}, warnings, err 365 } 366 } 367 368 securityGroups := make([]SecurityGroup, len(ccv2SecurityGroups)) 369 for i, securityGroup := range ccv2SecurityGroups { 370 securityGroups[i] = SecurityGroup(securityGroup) 371 } 372 373 return securityGroups, warnings, nil 374 } 375 376 func (actor Actor) isRunningSecurityGroupBoundToSpace(securityGroupName string, spaceGUID string) (bool, Warnings, error) { 377 ccv2SecurityGroups, warnings, err := actor.CloudControllerClient.GetSpaceSecurityGroups(spaceGUID, ccv2.Filter{ 378 Type: constant.NameFilter, 379 Operator: constant.EqualOperator, 380 Values: []string{securityGroupName}, 381 }) 382 return len(ccv2SecurityGroups) > 0, Warnings(warnings), err 383 } 384 385 func (actor Actor) isStagingSecurityGroupBoundToSpace(securityGroupName string, spaceGUID string) (bool, Warnings, error) { 386 ccv2SecurityGroups, warnings, err := actor.CloudControllerClient.GetSpaceStagingSecurityGroups(spaceGUID, ccv2.Filter{ 387 Type: constant.NameFilter, 388 Operator: constant.EqualOperator, 389 Values: []string{securityGroupName}, 390 }) 391 return len(ccv2SecurityGroups) > 0, Warnings(warnings), err 392 }