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  }