code.cloudfoundry.org/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  }