github.com/iter8-tools/iter8@v1.1.2/controllers/finalizer.go (about)

     1  package controllers
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  
     7  	"github.com/iter8-tools/iter8/base/log"
     8  	"github.com/iter8-tools/iter8/controllers/k8sclient"
     9  	corev1 "k8s.io/api/core/v1"
    10  	kubeerrors "k8s.io/apimachinery/pkg/api/errors"
    11  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    12  	"k8s.io/apimachinery/pkg/runtime/schema"
    13  	"k8s.io/client-go/util/retry"
    14  )
    15  
    16  const (
    17  	// for application resources
    18  	iter8FinalizerStr = "iter8.tools/finalizer"
    19  )
    20  
    21  // add Iter8 finalizer to an application resource
    22  func addFinalizer(name string, namespace string, gvrShort string, client k8sclient.Interface, config *Config) {
    23  	err := retry.RetryOnConflict(retry.DefaultRetry, func() error {
    24  		// first, get the object
    25  		u, e := client.Resource(schema.GroupVersionResource{
    26  			Group:    config.ResourceTypes[gvrShort].Group,
    27  			Version:  config.ResourceTypes[gvrShort].Version,
    28  			Resource: config.ResourceTypes[gvrShort].Resource,
    29  		}).Namespace(namespace).Get(context.TODO(), name, metav1.GetOptions{})
    30  		if e != nil {
    31  			return e
    32  		}
    33  
    34  		// get old and new finalizers
    35  		oldFinalizers := map[string]bool{}
    36  		newFinalizers := map[string]bool{}
    37  		if u.GetDeletionTimestamp() == nil {
    38  			for _, f := range u.GetFinalizers() {
    39  				oldFinalizers[f] = true
    40  				newFinalizers[f] = true
    41  			}
    42  			// insert Iter8 finalizer
    43  			newFinalizers[iter8FinalizerStr] = true
    44  		}
    45  
    46  		// the only way newFinalizers could be of a different length is if
    47  		// oldFinalizers didn't have iter8FinalizerStr
    48  		if len(oldFinalizers) != len(newFinalizers) {
    49  			log.Logger.Trace("oldFinalizers: ", oldFinalizers)
    50  			log.Logger.Trace("newFinalizers: ", newFinalizers)
    51  			finalizers := []string{}
    52  			for key := range newFinalizers {
    53  				finalizers = append(finalizers, key)
    54  			}
    55  			u.SetFinalizers(finalizers)
    56  
    57  			log.Logger.Trace("attempting to update resource with finalizer")
    58  			_, e := client.Resource(schema.GroupVersionResource{
    59  				Group:    config.ResourceTypes[gvrShort].Group,
    60  				Version:  config.ResourceTypes[gvrShort].Version,
    61  				Resource: config.ResourceTypes[gvrShort].Resource,
    62  			}).Namespace(u.GetNamespace()).Update(context.TODO(), u, metav1.UpdateOptions{})
    63  			if e != nil {
    64  				log.Logger.WithStackTrace(e.Error()).Error("error while updating resource with finalizer")
    65  			} else {
    66  				// broadcast event
    67  
    68  				// get resource for event broadcasting
    69  				r, err := client.Resource(schema.GroupVersionResource{
    70  					Group:    config.ResourceTypes[gvrShort].Group,
    71  					Version:  config.ResourceTypes[gvrShort].Version,
    72  					Resource: config.ResourceTypes[gvrShort].Resource,
    73  				}).Namespace(namespace).Get(context.TODO(), name, metav1.GetOptions{})
    74  				if err != nil {
    75  					log.Logger.Warnf("could not get pod with name %s in namespace %s", name, namespace)
    76  				} else {
    77  					broadcastEvent(r, corev1.EventTypeNormal, "Added Iter8 finalizer", "Added Iter8 finalizer for resource", client)
    78  				}
    79  			}
    80  			return e
    81  		}
    82  
    83  		return nil
    84  	})
    85  
    86  	if err != nil {
    87  		if kubeerrors.IsNotFound(err) {
    88  			log.Logger.Debug(err)
    89  		} else {
    90  			log.Logger.WithStackTrace(err.Error()).Error(errors.New("failed to add finalizer with retry"))
    91  
    92  			// get resource for event broadcasting
    93  			r, err := client.Resource(schema.GroupVersionResource{
    94  				Group:    config.ResourceTypes[gvrShort].Group,
    95  				Version:  config.ResourceTypes[gvrShort].Version,
    96  				Resource: config.ResourceTypes[gvrShort].Resource,
    97  			}).Namespace(namespace).Get(context.TODO(), name, metav1.GetOptions{})
    98  			if err != nil {
    99  				log.Logger.Warnf("could not get pod with name %s in namespace %s", name, namespace)
   100  			}
   101  
   102  			broadcastEvent(r, corev1.EventTypeWarning, "Failed to add Iter8 finalizer", "Failed to add Iter8 finalizer for resource", client)
   103  		}
   104  	}
   105  }
   106  
   107  // remove Iter8 finalizer from an application resource
   108  func removeFinalizer(name string, namespace string, gvrShort string, client k8sclient.Interface, config *Config) {
   109  	err := retry.RetryOnConflict(retry.DefaultRetry, func() error {
   110  		// first, get the object
   111  		u, e := client.Resource(schema.GroupVersionResource{
   112  			Group:    config.ResourceTypes[gvrShort].Group,
   113  			Version:  config.ResourceTypes[gvrShort].Version,
   114  			Resource: config.ResourceTypes[gvrShort].Resource,
   115  		}).Namespace(namespace).Get(context.TODO(), name, metav1.GetOptions{})
   116  		if e != nil && kubeerrors.IsNotFound(e) {
   117  			return nil
   118  		} else if e != nil {
   119  			return e
   120  		}
   121  
   122  		if u.GetDeletionTimestamp() == nil {
   123  			log.Logger.Trace("object not terminating; will not remove finalizer")
   124  			return nil
   125  		}
   126  
   127  		// remove iter8 finalizer if present
   128  		var finalizers []string
   129  		for _, f := range u.GetFinalizers() {
   130  			if f != iter8FinalizerStr {
   131  				finalizers = append(finalizers, f)
   132  			}
   133  		}
   134  
   135  		if len(finalizers) < len(u.GetFinalizers()) {
   136  			// update finalizers in the object
   137  			// we do not want to remove non-Iter8 finalizers
   138  			if len(finalizers) == 0 {
   139  				u.SetFinalizers(nil)
   140  			} else {
   141  				u.SetFinalizers(finalizers)
   142  			}
   143  
   144  			// update object
   145  			_, e = client.Resource(schema.GroupVersionResource{
   146  				Group:    config.ResourceTypes[gvrShort].Group,
   147  				Version:  config.ResourceTypes[gvrShort].Version,
   148  				Resource: config.ResourceTypes[gvrShort].Resource,
   149  			}).Namespace(u.GetNamespace()).Update(context.TODO(), u, metav1.UpdateOptions{})
   150  
   151  			if e != nil {
   152  				// if object has been deleted, return
   153  				if kubeerrors.IsNotFound(e) {
   154  					return nil
   155  				}
   156  			}
   157  		}
   158  
   159  		return e
   160  	})
   161  
   162  	if err != nil {
   163  		log.Logger.WithStackTrace(err.Error()).Error(errors.New("failed to delete Iter8 finalizer"))
   164  
   165  		// get resource for event broadcasting
   166  		r, err := client.Resource(schema.GroupVersionResource{
   167  			Group:    config.ResourceTypes[gvrShort].Group,
   168  			Version:  config.ResourceTypes[gvrShort].Version,
   169  			Resource: config.ResourceTypes[gvrShort].Resource,
   170  		}).Namespace(namespace).Get(context.TODO(), name, metav1.GetOptions{})
   171  		if err != nil {
   172  			log.Logger.Warnf("could not get resource with name %s in namespace %s", name, namespace)
   173  		}
   174  
   175  		broadcastEvent(r, corev1.EventTypeWarning, "Failed to delete Iter8 finalizer", "Failed to delete Iter8 finalizer for resource", client)
   176  	}
   177  }