sigs.k8s.io/cluster-api-provider-aws@v1.5.5/controllers/awsmachine_tags.go (about) 1 /* 2 Copyright 2018 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 infrav1 "sigs.k8s.io/cluster-api-provider-aws/api/v1beta1" 21 service "sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/services" 22 ) 23 24 const ( 25 // TagsLastAppliedAnnotation is the key for the machine object annotation 26 // which tracks the AdditionalTags in the Machine Provider Config. 27 // See https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ 28 // for annotation formatting rules. 29 TagsLastAppliedAnnotation = "sigs.k8s.io/cluster-api-provider-aws-last-applied-tags" 30 31 // VolumeTagsLastAppliedAnnotation is the key for the ebs volumes annotation 32 // which tracks the AdditionalTags in the Machine Provider Config. 33 // See https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ 34 // for annotation formatting rules. 35 VolumeTagsLastAppliedAnnotation = "sigs.k8s.io/cluster-api-provider-last-applied-tags-on-volumes" 36 ) 37 38 // Ensure that the tags of the machine are correct 39 // Returns bool, error 40 // Bool indicates if changes were made or not, allowing the caller to decide 41 // if the machine should be updated. 42 func (r *AWSMachineReconciler) ensureTags(svc service.EC2Interface, machine *infrav1.AWSMachine, instanceID *string, additionalTags map[string]string) (bool, error) { 43 annotation, err := r.machineAnnotationJSON(machine, TagsLastAppliedAnnotation) 44 if err != nil { 45 return false, err 46 } 47 48 // Check if the instance tags were changed. If they were, update them. 49 // It would be possible here to only send new/updated tags, but for the 50 // moment we send everything, even if only a single tag was created or 51 // updated. 52 changed, created, deleted, newAnnotation := r.tagsChanged(annotation, additionalTags) 53 if changed { 54 err = svc.UpdateResourceTags(instanceID, created, deleted) 55 if err != nil { 56 return false, err 57 } 58 59 // We also need to update the annotation if anything changed. 60 err = r.updateMachineAnnotationJSON(machine, TagsLastAppliedAnnotation, newAnnotation) 61 if err != nil { 62 return false, err 63 } 64 } 65 66 return changed, nil 67 } 68 69 // Ensure that the tags of the volumes in the machine are correct 70 // Returns tags which are being created/updated/deleted and error. 71 func (r *AWSMachineReconciler) ensureVolumeTags(svc service.EC2Interface, volumeID *string, annotation map[string]interface{}, additionalTags map[string]string) (map[string]interface{}, error) { 72 // Check if the volume tags were changed. If they were, update them. 73 // It would be possible here to only send new/updated tags, but for the 74 // moment we send everything, even if only a single tag was created or 75 // updated. 76 changed, created, deleted, subAnnotation := r.tagsChanged(annotation, additionalTags) 77 if changed { 78 err := svc.UpdateResourceTags(volumeID, created, deleted) 79 if err != nil { 80 return nil, err 81 } 82 } 83 84 return subAnnotation, nil 85 } 86 87 // tagsChanged determines which tags to delete and which to add. 88 func (r *AWSMachineReconciler) tagsChanged(annotation map[string]interface{}, src map[string]string) (bool, map[string]string, map[string]string, map[string]interface{}) { 89 // Bool tracking if we found any changed state. 90 changed := false 91 92 // Tracking for created/updated 93 created := map[string]string{} 94 95 // Tracking for tags that were deleted. 96 deleted := map[string]string{} 97 98 // The new annotation that we need to set if anything is created/updated. 99 newAnnotation := map[string]interface{}{} 100 101 // Loop over annotation, checking if entries are in src. 102 // If an entry is present in annotation but not src, it has been deleted 103 // since last time. We flag this in the deleted map. 104 for t, v := range annotation { 105 _, ok := src[t] 106 107 // Entry isn't in src, it has been deleted. 108 if !ok { 109 // Cast v to a string here. This should be fine, tags are always 110 // strings. 111 deleted[t] = v.(string) 112 changed = true 113 } 114 } 115 116 // Loop over src, checking for entries in annotation. 117 // 118 // If an entry is in src, but not annotation, it has been created since 119 // last time. 120 // 121 // If an entry is in both src and annotation, we compare their values, if 122 // the value in src differs from that in annotation, the tag has been 123 // updated since last time. 124 for t, v := range src { 125 av, ok := annotation[t] 126 127 // Entries in the src always need to be noted in the newAnnotation. We 128 // know they're going to be created or updated. 129 newAnnotation[t] = v 130 131 // Entry isn't in annotation, it's new. 132 if !ok { 133 created[t] = v 134 newAnnotation[t] = v 135 changed = true 136 continue 137 } 138 139 // Entry is in annotation, has the value changed? 140 if v != av { 141 created[t] = v 142 changed = true 143 } 144 145 // Entry existed in both src and annotation, and their values were 146 // equal. Nothing to do. 147 } 148 149 // We made it through the loop, and everything that was in src, was also 150 // in dst. Nothing changed. 151 return changed, created, deleted, newAnnotation 152 }