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