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  }