github.com/jghiloni/cli@v6.28.1-0.20170628223758-0ce05fe032a2+incompatible/actor/v2action/security_group.go (about) 1 package v2action 2 3 import ( 4 "fmt" 5 "sort" 6 7 "code.cloudfoundry.org/cli/api/cloudcontroller/ccerror" 8 "code.cloudfoundry.org/cli/api/cloudcontroller/ccv2" 9 ) 10 11 // SecurityGroup represents a CF SecurityGroup. 12 type SecurityGroup ccv2.SecurityGroup 13 14 // SecurityGroupWithOrganizationAndSpace 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 { 75 Filter: ccv2.NameFilter, 76 Operator: ccv2.EqualOperator, 77 Value: securityGroupName, 78 }, 79 }) 80 81 if err != nil { 82 return SecurityGroup{}, Warnings(warnings), err 83 } 84 85 if len(securityGroups) == 0 { 86 return SecurityGroup{}, Warnings(warnings), SecurityGroupNotFoundError{securityGroupName} 87 } 88 89 securityGroup := SecurityGroup{ 90 Name: securityGroups[0].Name, 91 GUID: securityGroups[0].GUID, 92 } 93 return securityGroup, Warnings(warnings), nil 94 } 95 96 type SpaceWithLifecycle struct { 97 ccv2.Space 98 Lifecycle ccv2.SecurityGroupLifecycle 99 } 100 101 func (actor Actor) getSecurityGroupSpacesAndAssignedLifecycles(securityGroupGUID string, includeStaging bool) ([]SpaceWithLifecycle, Warnings, error) { 102 var ( 103 spacesWithLifecycles []SpaceWithLifecycle 104 allWarnings Warnings 105 ) 106 107 runningSpaces, warnings, err := actor.CloudControllerClient.GetRunningSpacesBySecurityGroup(securityGroupGUID) 108 allWarnings = append(allWarnings, warnings...) 109 if err != nil { 110 return nil, Warnings(allWarnings), err 111 } 112 113 for _, space := range runningSpaces { 114 spacesWithLifecycles = append(spacesWithLifecycles, SpaceWithLifecycle{Space: space, Lifecycle: ccv2.SecurityGroupLifecycleRunning}) 115 } 116 117 if includeStaging { 118 stagingSpaces, warnings, err := actor.CloudControllerClient.GetStagingSpacesBySecurityGroup(securityGroupGUID) 119 allWarnings = append(allWarnings, warnings...) 120 if err != nil { 121 return nil, Warnings(allWarnings), err 122 } 123 124 for _, space := range stagingSpaces { 125 spacesWithLifecycles = append(spacesWithLifecycles, SpaceWithLifecycle{Space: space, Lifecycle: ccv2.SecurityGroupLifecycleStaging}) 126 } 127 } 128 129 return spacesWithLifecycles, allWarnings, nil 130 } 131 132 // GetSecurityGroupsWithOrganizationAndSpace returns a list of security groups 133 // with org and space information, optionally including staging spaces. 134 func (actor Actor) GetSecurityGroupsWithOrganizationSpaceAndLifecycle(includeStaging bool) ([]SecurityGroupWithOrganizationSpaceAndLifecycle, Warnings, error) { 135 var err error 136 137 securityGroups, allWarnings, err := actor.CloudControllerClient.GetSecurityGroups(nil) 138 if err != nil { 139 return nil, Warnings(allWarnings), err 140 } 141 142 cachedOrgs := make(map[string]Organization) 143 144 var secGroupOrgSpaces []SecurityGroupWithOrganizationSpaceAndLifecycle 145 146 for _, s := range securityGroups { 147 securityGroup := SecurityGroup{ 148 GUID: s.GUID, 149 Name: s.Name, 150 RunningDefault: s.RunningDefault, 151 StagingDefault: s.StagingDefault, 152 } 153 154 if securityGroup.RunningDefault { 155 secGroupOrgSpaces = append(secGroupOrgSpaces, 156 SecurityGroupWithOrganizationSpaceAndLifecycle{ 157 SecurityGroup: &securityGroup, 158 Organization: &Organization{}, 159 Space: &Space{}, 160 Lifecycle: ccv2.SecurityGroupLifecycleRunning, 161 }) 162 } 163 164 if securityGroup.StagingDefault { 165 secGroupOrgSpaces = append(secGroupOrgSpaces, 166 SecurityGroupWithOrganizationSpaceAndLifecycle{ 167 SecurityGroup: &securityGroup, 168 Organization: &Organization{}, 169 Space: &Space{}, 170 Lifecycle: ccv2.SecurityGroupLifecycleStaging, 171 }) 172 } 173 174 spaces, warnings, err := actor.getSecurityGroupSpacesAndAssignedLifecycles(s.GUID, includeStaging) 175 allWarnings = append(allWarnings, warnings...) 176 if err != nil { 177 return nil, Warnings(allWarnings), err 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 o, warnings, err := actor.CloudControllerClient.GetOrganization(sp.OrganizationGUID) 205 allWarnings = append(allWarnings, warnings...) 206 if err != nil { 207 return nil, Warnings(allWarnings), err 208 } 209 210 org = Organization{ 211 GUID: o.GUID, 212 Name: o.Name, 213 } 214 cachedOrgs[org.GUID] = org 215 } 216 217 secGroupOrgSpaces = append(secGroupOrgSpaces, 218 SecurityGroupWithOrganizationSpaceAndLifecycle{ 219 SecurityGroup: &securityGroup, 220 Organization: &org, 221 Space: &space, 222 Lifecycle: sp.Lifecycle, 223 }) 224 } 225 } 226 227 // Sort the results alphabetically by security group, then org, then space 228 sort.Slice(secGroupOrgSpaces, 229 func(i, j int) bool { 230 switch { 231 case secGroupOrgSpaces[i].SecurityGroup.Name < secGroupOrgSpaces[j].SecurityGroup.Name: 232 return true 233 case secGroupOrgSpaces[i].SecurityGroup.Name > secGroupOrgSpaces[j].SecurityGroup.Name: 234 return false 235 case secGroupOrgSpaces[i].SecurityGroup.RunningDefault && !secGroupOrgSpaces[i].SecurityGroup.RunningDefault: 236 return true 237 case !secGroupOrgSpaces[i].SecurityGroup.RunningDefault && secGroupOrgSpaces[i].SecurityGroup.RunningDefault: 238 return false 239 case secGroupOrgSpaces[i].Organization.Name < secGroupOrgSpaces[j].Organization.Name: 240 return true 241 case secGroupOrgSpaces[i].Organization.Name > secGroupOrgSpaces[j].Organization.Name: 242 return false 243 case secGroupOrgSpaces[i].SecurityGroup.StagingDefault && !secGroupOrgSpaces[i].SecurityGroup.StagingDefault: 244 return true 245 case !secGroupOrgSpaces[i].SecurityGroup.StagingDefault && secGroupOrgSpaces[i].SecurityGroup.StagingDefault: 246 return false 247 case secGroupOrgSpaces[i].Space.Name < secGroupOrgSpaces[j].Space.Name: 248 return true 249 case secGroupOrgSpaces[i].Space.Name > secGroupOrgSpaces[j].Space.Name: 250 return false 251 } 252 253 return secGroupOrgSpaces[i].Lifecycle < secGroupOrgSpaces[j].Lifecycle 254 }) 255 return secGroupOrgSpaces, Warnings(allWarnings), err 256 } 257 258 // GetSpaceRunningSecurityGroupsBySpace returns a list of all security groups 259 // bound to this space in the 'running' lifecycle phase. 260 func (actor Actor) GetSpaceRunningSecurityGroupsBySpace(spaceGUID string) ([]SecurityGroup, Warnings, error) { 261 ccv2SecurityGroups, warnings, err := actor.CloudControllerClient.GetSpaceRunningSecurityGroupsBySpace(spaceGUID, nil) 262 return processSecurityGroups(spaceGUID, ccv2SecurityGroups, Warnings(warnings), err) 263 } 264 265 // GetSpaceStagingSecurityGroupsBySpace returns a list of all security groups 266 // bound to this space in the 'staging' lifecycle phase. with an optional 267 func (actor Actor) GetSpaceStagingSecurityGroupsBySpace(spaceGUID string) ([]SecurityGroup, Warnings, error) { 268 ccv2SecurityGroups, warnings, err := actor.CloudControllerClient.GetSpaceStagingSecurityGroupsBySpace(spaceGUID, nil) 269 return processSecurityGroups(spaceGUID, ccv2SecurityGroups, Warnings(warnings), err) 270 } 271 272 func (actor Actor) UnbindSecurityGroupByNameAndSpace(securityGroupName string, spaceGUID string, lifecycle ccv2.SecurityGroupLifecycle) (Warnings, error) { 273 if lifecycle != ccv2.SecurityGroupLifecycleRunning && lifecycle != ccv2.SecurityGroupLifecycleStaging { 274 return nil, InvalidLifecycleError{lifecycle: lifecycle} 275 } 276 277 var allWarnings Warnings 278 279 securityGroup, warnings, err := actor.GetSecurityGroupByName(securityGroupName) 280 281 allWarnings = append(allWarnings, warnings...) 282 if err != nil { 283 return allWarnings, err 284 } 285 286 warnings, err = actor.unbindSecurityGroupAndSpace(securityGroup, spaceGUID, lifecycle) 287 allWarnings = append(allWarnings, warnings...) 288 return allWarnings, err 289 } 290 291 func (actor Actor) UnbindSecurityGroupByNameOrganizationNameAndSpaceName(securityGroupName string, orgName string, spaceName string, lifecycle ccv2.SecurityGroupLifecycle) (Warnings, error) { 292 if lifecycle != ccv2.SecurityGroupLifecycleRunning && lifecycle != ccv2.SecurityGroupLifecycleStaging { 293 return nil, InvalidLifecycleError{lifecycle: lifecycle} 294 } 295 296 var allWarnings Warnings 297 298 securityGroup, warnings, err := actor.GetSecurityGroupByName(securityGroupName) 299 allWarnings = append(allWarnings, warnings...) 300 if err != nil { 301 return allWarnings, err 302 } 303 304 org, warnings, err := actor.GetOrganizationByName(orgName) 305 allWarnings = append(allWarnings, warnings...) 306 if err != nil { 307 return allWarnings, err 308 } 309 310 space, warnings, err := actor.GetSpaceByOrganizationAndName(org.GUID, spaceName) 311 allWarnings = append(allWarnings, warnings...) 312 if err != nil { 313 return allWarnings, err 314 } 315 316 warnings, err = actor.unbindSecurityGroupAndSpace(securityGroup, space.GUID, lifecycle) 317 allWarnings = append(allWarnings, warnings...) 318 return allWarnings, err 319 } 320 321 func (actor Actor) unbindSecurityGroupAndSpace(securityGroup SecurityGroup, spaceGUID string, lifecycle ccv2.SecurityGroupLifecycle) (Warnings, error) { 322 if lifecycle == ccv2.SecurityGroupLifecycleRunning { 323 return actor.doUnbind(securityGroup, spaceGUID, lifecycle, 324 actor.isRunningSecurityGroupBoundToSpace, 325 actor.isStagingSecurityGroupBoundToSpace, 326 actor.CloudControllerClient.RemoveSpaceFromRunningSecurityGroup) 327 } else { 328 return actor.doUnbind(securityGroup, spaceGUID, lifecycle, 329 actor.isStagingSecurityGroupBoundToSpace, 330 actor.isRunningSecurityGroupBoundToSpace, 331 actor.CloudControllerClient.RemoveSpaceFromStagingSecurityGroup) 332 } 333 } 334 335 func (_ Actor) doUnbind(securityGroup SecurityGroup, 336 spaceGUID string, 337 lifecycle ccv2.SecurityGroupLifecycle, 338 requestedPhaseSecurityGroupBoundToSpace func(string, string) (bool, Warnings, error), 339 otherPhaseSecurityGroupBoundToSpace func(string, string) (bool, Warnings, error), 340 removeSpaceFromPhaseSecurityGroup func(string, string) (ccv2.Warnings, error)) (Warnings, error) { 341 342 requestedPhaseBound, allWarnings, err := requestedPhaseSecurityGroupBoundToSpace(securityGroup.Name, spaceGUID) 343 if err != nil { 344 return allWarnings, err 345 } 346 347 if !requestedPhaseBound { 348 otherBound, warnings, otherr := otherPhaseSecurityGroupBoundToSpace(securityGroup.Name, spaceGUID) 349 allWarnings = append(allWarnings, warnings...) 350 351 if otherr != nil { 352 return allWarnings, otherr 353 } else if otherBound { 354 return allWarnings, SecurityGroupNotBoundError{Name: securityGroup.Name, Lifecycle: lifecycle} 355 } else { 356 return allWarnings, nil 357 } 358 } 359 360 ccv2Warnings, err := removeSpaceFromPhaseSecurityGroup(securityGroup.GUID, spaceGUID) 361 allWarnings = append(allWarnings, Warnings(ccv2Warnings)...) 362 return allWarnings, err 363 } 364 365 func extractSecurityGroupRules(securityGroup SecurityGroup, lifecycle ccv2.SecurityGroupLifecycle) []SecurityGroupRule { 366 securityGroupRules := make([]SecurityGroupRule, len(securityGroup.Rules)) 367 368 for i, rule := range securityGroup.Rules { 369 securityGroupRules[i] = SecurityGroupRule{ 370 Name: securityGroup.Name, 371 Description: rule.Description, 372 Destination: rule.Destination, 373 Lifecycle: lifecycle, 374 Ports: rule.Ports, 375 Protocol: rule.Protocol, 376 } 377 } 378 379 return securityGroupRules 380 } 381 382 func processSecurityGroups(spaceGUID string, ccv2SecurityGroups []ccv2.SecurityGroup, warnings Warnings, err error) ([]SecurityGroup, Warnings, error) { 383 if err != nil { 384 switch err.(type) { 385 case ccerror.ResourceNotFoundError: 386 return []SecurityGroup{}, warnings, SpaceNotFoundError{GUID: spaceGUID} 387 default: 388 return []SecurityGroup{}, warnings, err 389 } 390 } 391 392 securityGroups := make([]SecurityGroup, len(ccv2SecurityGroups)) 393 for i, securityGroup := range ccv2SecurityGroups { 394 securityGroups[i] = SecurityGroup(securityGroup) 395 } 396 397 return securityGroups, warnings, nil 398 } 399 400 func (actor Actor) isRunningSecurityGroupBoundToSpace(securityGroupName string, spaceGUID string) (bool, Warnings, error) { 401 ccv2SecurityGroups, warnings, err := actor.CloudControllerClient.GetSpaceRunningSecurityGroupsBySpace(spaceGUID, []ccv2.Query{{ 402 Filter: ccv2.NameFilter, 403 Operator: ccv2.EqualOperator, 404 Value: securityGroupName, 405 }}) 406 return len(ccv2SecurityGroups) > 0, Warnings(warnings), err 407 } 408 409 func (actor Actor) isStagingSecurityGroupBoundToSpace(securityGroupName string, spaceGUID string) (bool, Warnings, error) { 410 ccv2SecurityGroups, warnings, err := actor.CloudControllerClient.GetSpaceStagingSecurityGroupsBySpace(spaceGUID, []ccv2.Query{{ 411 Filter: ccv2.NameFilter, 412 Operator: ccv2.EqualOperator, 413 Value: securityGroupName, 414 }}) 415 return len(ccv2SecurityGroups) > 0, Warnings(warnings), err 416 }