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  }