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 }