sigs.k8s.io/cluster-api-provider-aws@v1.5.5/controllers/awsmachine_security_groups.go (about)

     1  /*
     2  Copyright 2019 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8  	http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package controllers
    18  
    19  import (
    20  	"sort"
    21  
    22  	infrav1 "sigs.k8s.io/cluster-api-provider-aws/api/v1beta1"
    23  	"sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/scope"
    24  	service "sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/services"
    25  )
    26  
    27  const (
    28  	// SecurityGroupsLastAppliedAnnotation is the key for the machine object
    29  	// annotation which tracks the SecurityGroups that the machine actuator is
    30  	// responsible for. These are the SecurityGroups that have been handled by
    31  	// the AdditionalSecurityGroups in the Machine Provider Config.
    32  	// See https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/
    33  	// for annotation formatting rules.
    34  	SecurityGroupsLastAppliedAnnotation = "sigs.k8s.io/cluster-api-provider-aws-last-applied-security-groups"
    35  )
    36  
    37  // Ensures that the security groups of the machine are correct
    38  // Returns bool, error
    39  // Bool indicates if changes were made or not, allowing the caller to decide
    40  // if the machine should be updated.
    41  func (r *AWSMachineReconciler) ensureSecurityGroups(ec2svc service.EC2Interface, scope *scope.MachineScope, additional []infrav1.AWSResourceReference, existing map[string][]string) (bool, error) {
    42  	annotation, err := r.machineAnnotationJSON(scope.AWSMachine, SecurityGroupsLastAppliedAnnotation)
    43  	if err != nil {
    44  		return false, err
    45  	}
    46  
    47  	core, err := ec2svc.GetCoreSecurityGroups(scope)
    48  	if err != nil {
    49  		return false, err
    50  	}
    51  
    52  	additionalSecurityGroupsIDs, err := ec2svc.GetAdditionalSecurityGroupsIDs(additional)
    53  	if err != nil {
    54  		return false, nil // nolint:nilerr
    55  	}
    56  
    57  	changed, ids := r.securityGroupsChanged(annotation, core, additionalSecurityGroupsIDs, existing)
    58  	if !changed {
    59  		return false, nil
    60  	}
    61  
    62  	if err := ec2svc.UpdateInstanceSecurityGroups(*scope.GetInstanceID(), ids); err != nil {
    63  		return false, err
    64  	}
    65  
    66  	// Build and store annotation.
    67  	newAnnotation := make(map[string]interface{}, len(additionalSecurityGroupsIDs))
    68  	for _, id := range additionalSecurityGroupsIDs {
    69  		newAnnotation[id] = struct{}{}
    70  	}
    71  
    72  	if err := r.updateMachineAnnotationJSON(scope.AWSMachine, SecurityGroupsLastAppliedAnnotation, newAnnotation); err != nil {
    73  		return false, err
    74  	}
    75  
    76  	return true, nil
    77  }
    78  
    79  // securityGroupsChanged determines which security groups to delete and which to add.
    80  func (r *AWSMachineReconciler) securityGroupsChanged(annotation map[string]interface{}, core []string, additional []string, existing map[string][]string) (bool, []string) {
    81  	state := map[string]bool{}
    82  	for _, s := range additional {
    83  		state[s] = true
    84  	}
    85  
    86  	// Loop over `annotation`, checking the state for things that were deleted since last time.
    87  	// If we find something in the `annotation`, but not in the state, we flag it as `false` (not found, deleted).
    88  	for groupID := range annotation {
    89  		if _, ok := state[groupID]; !ok {
    90  			state[groupID] = false
    91  		}
    92  	}
    93  
    94  	// add (or add back) the core security groups
    95  	for _, s := range core {
    96  		state[s] = true
    97  	}
    98  
    99  	// Build the security group list.
   100  	res := []string{}
   101  	for id, keep := range state {
   102  		if keep {
   103  			res = append(res, id)
   104  		}
   105  	}
   106  
   107  	for _, actual := range existing {
   108  		if len(actual) != len(res) {
   109  			return true, res
   110  		}
   111  
   112  		// Length is the same, check if the ids are the same too.
   113  		sort.Strings(actual)
   114  		sort.Strings(res)
   115  		for i, id := range res {
   116  			if actual[i] != id {
   117  				return true, res
   118  			}
   119  		}
   120  	}
   121  
   122  	return false, res
   123  }