...

Source file src/sigs.k8s.io/controller-runtime/pkg/controller/controllerutil/controllerutil.go

Documentation: sigs.k8s.io/controller-runtime/pkg/controller/controllerutil

     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 controllerutil
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"reflect"
    23  
    24  	"k8s.io/apimachinery/pkg/api/equality"
    25  	apierrors "k8s.io/apimachinery/pkg/api/errors"
    26  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    27  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    28  	"k8s.io/apimachinery/pkg/runtime"
    29  	"k8s.io/apimachinery/pkg/runtime/schema"
    30  	"k8s.io/utils/ptr"
    31  
    32  	"sigs.k8s.io/controller-runtime/pkg/client"
    33  	"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
    34  )
    35  
    36  // AlreadyOwnedError is an error returned if the object you are trying to assign
    37  // a controller reference is already owned by another controller Object is the
    38  // subject and Owner is the reference for the current owner.
    39  type AlreadyOwnedError struct {
    40  	Object metav1.Object
    41  	Owner  metav1.OwnerReference
    42  }
    43  
    44  func (e *AlreadyOwnedError) Error() string {
    45  	return fmt.Sprintf("Object %s/%s is already owned by another %s controller %s", e.Object.GetNamespace(), e.Object.GetName(), e.Owner.Kind, e.Owner.Name)
    46  }
    47  
    48  func newAlreadyOwnedError(obj metav1.Object, owner metav1.OwnerReference) *AlreadyOwnedError {
    49  	return &AlreadyOwnedError{
    50  		Object: obj,
    51  		Owner:  owner,
    52  	}
    53  }
    54  
    55  // SetControllerReference sets owner as a Controller OwnerReference on controlled.
    56  // This is used for garbage collection of the controlled object and for
    57  // reconciling the owner object on changes to controlled (with a Watch + EnqueueRequestForOwner).
    58  // Since only one OwnerReference can be a controller, it returns an error if
    59  // there is another OwnerReference with Controller flag set.
    60  func SetControllerReference(owner, controlled metav1.Object, scheme *runtime.Scheme) error {
    61  	// Validate the owner.
    62  	ro, ok := owner.(runtime.Object)
    63  	if !ok {
    64  		return fmt.Errorf("%T is not a runtime.Object, cannot call SetControllerReference", owner)
    65  	}
    66  	if err := validateOwner(owner, controlled); err != nil {
    67  		return err
    68  	}
    69  
    70  	// Create a new controller ref.
    71  	gvk, err := apiutil.GVKForObject(ro, scheme)
    72  	if err != nil {
    73  		return err
    74  	}
    75  	ref := metav1.OwnerReference{
    76  		APIVersion:         gvk.GroupVersion().String(),
    77  		Kind:               gvk.Kind,
    78  		Name:               owner.GetName(),
    79  		UID:                owner.GetUID(),
    80  		BlockOwnerDeletion: ptr.To(true),
    81  		Controller:         ptr.To(true),
    82  	}
    83  
    84  	// Return early with an error if the object is already controlled.
    85  	if existing := metav1.GetControllerOf(controlled); existing != nil && !referSameObject(*existing, ref) {
    86  		return newAlreadyOwnedError(controlled, *existing)
    87  	}
    88  
    89  	// Update owner references and return.
    90  	upsertOwnerRef(ref, controlled)
    91  	return nil
    92  }
    93  
    94  // SetOwnerReference is a helper method to make sure the given object contains an object reference to the object provided.
    95  // This allows you to declare that owner has a dependency on the object without specifying it as a controller.
    96  // If a reference to the same object already exists, it'll be overwritten with the newly provided version.
    97  func SetOwnerReference(owner, object metav1.Object, scheme *runtime.Scheme) error {
    98  	// Validate the owner.
    99  	ro, ok := owner.(runtime.Object)
   100  	if !ok {
   101  		return fmt.Errorf("%T is not a runtime.Object, cannot call SetOwnerReference", owner)
   102  	}
   103  	if err := validateOwner(owner, object); err != nil {
   104  		return err
   105  	}
   106  
   107  	// Create a new owner ref.
   108  	gvk, err := apiutil.GVKForObject(ro, scheme)
   109  	if err != nil {
   110  		return err
   111  	}
   112  	ref := metav1.OwnerReference{
   113  		APIVersion: gvk.GroupVersion().String(),
   114  		Kind:       gvk.Kind,
   115  		UID:        owner.GetUID(),
   116  		Name:       owner.GetName(),
   117  	}
   118  
   119  	// Update owner references and return.
   120  	upsertOwnerRef(ref, object)
   121  	return nil
   122  }
   123  
   124  // RemoveOwnerReference is a helper method to make sure the given object removes an owner reference to the object provided.
   125  // This allows you to remove the owner to establish a new owner of the object in a subsequent call.
   126  func RemoveOwnerReference(owner, object metav1.Object, scheme *runtime.Scheme) error {
   127  	owners := object.GetOwnerReferences()
   128  	length := len(owners)
   129  	if length < 1 {
   130  		return fmt.Errorf("%T does not have any owner references", object)
   131  	}
   132  	ro, ok := owner.(runtime.Object)
   133  	if !ok {
   134  		return fmt.Errorf("%T is not a runtime.Object, cannot call RemoveOwnerReference", owner)
   135  	}
   136  	gvk, err := apiutil.GVKForObject(ro, scheme)
   137  	if err != nil {
   138  		return err
   139  	}
   140  
   141  	index := indexOwnerRef(owners, metav1.OwnerReference{
   142  		APIVersion: gvk.GroupVersion().String(),
   143  		Name:       owner.GetName(),
   144  		Kind:       gvk.Kind,
   145  	})
   146  	if index == -1 {
   147  		return fmt.Errorf("%T does not have an owner reference for %T", object, owner)
   148  	}
   149  
   150  	owners = append(owners[:index], owners[index+1:]...)
   151  	object.SetOwnerReferences(owners)
   152  	return nil
   153  }
   154  
   155  // HasControllerReference returns true if the object
   156  // has an owner ref with controller equal to true
   157  func HasControllerReference(object metav1.Object) bool {
   158  	owners := object.GetOwnerReferences()
   159  	for _, owner := range owners {
   160  		isTrue := owner.Controller
   161  		if owner.Controller != nil && *isTrue {
   162  			return true
   163  		}
   164  	}
   165  	return false
   166  }
   167  
   168  // RemoveControllerReference removes an owner reference where the controller
   169  // equals true
   170  func RemoveControllerReference(owner, object metav1.Object, scheme *runtime.Scheme) error {
   171  	if ok := HasControllerReference(object); !ok {
   172  		return fmt.Errorf("%T does not have a owner reference with controller equals true", object)
   173  	}
   174  	ro, ok := owner.(runtime.Object)
   175  	if !ok {
   176  		return fmt.Errorf("%T is not a runtime.Object, cannot call RemoveControllerReference", owner)
   177  	}
   178  	gvk, err := apiutil.GVKForObject(ro, scheme)
   179  	if err != nil {
   180  		return err
   181  	}
   182  	ownerRefs := object.GetOwnerReferences()
   183  	index := indexOwnerRef(ownerRefs, metav1.OwnerReference{
   184  		APIVersion: gvk.GroupVersion().String(),
   185  		Name:       owner.GetName(),
   186  		Kind:       gvk.Kind,
   187  	})
   188  
   189  	if index == -1 {
   190  		return fmt.Errorf("%T does not have an controller reference for %T", object, owner)
   191  	}
   192  
   193  	if ownerRefs[index].Controller == nil || !*ownerRefs[index].Controller {
   194  		return fmt.Errorf("%T owner is not the controller reference for %T", owner, object)
   195  	}
   196  
   197  	ownerRefs = append(ownerRefs[:index], ownerRefs[index+1:]...)
   198  	object.SetOwnerReferences(ownerRefs)
   199  	return nil
   200  }
   201  
   202  func upsertOwnerRef(ref metav1.OwnerReference, object metav1.Object) {
   203  	owners := object.GetOwnerReferences()
   204  	if idx := indexOwnerRef(owners, ref); idx == -1 {
   205  		owners = append(owners, ref)
   206  	} else {
   207  		owners[idx] = ref
   208  	}
   209  	object.SetOwnerReferences(owners)
   210  }
   211  
   212  // indexOwnerRef returns the index of the owner reference in the slice if found, or -1.
   213  func indexOwnerRef(ownerReferences []metav1.OwnerReference, ref metav1.OwnerReference) int {
   214  	for index, r := range ownerReferences {
   215  		if referSameObject(r, ref) {
   216  			return index
   217  		}
   218  	}
   219  	return -1
   220  }
   221  
   222  func validateOwner(owner, object metav1.Object) error {
   223  	ownerNs := owner.GetNamespace()
   224  	if ownerNs != "" {
   225  		objNs := object.GetNamespace()
   226  		if objNs == "" {
   227  			return fmt.Errorf("cluster-scoped resource must not have a namespace-scoped owner, owner's namespace %s", ownerNs)
   228  		}
   229  		if ownerNs != objNs {
   230  			return fmt.Errorf("cross-namespace owner references are disallowed, owner's namespace %s, obj's namespace %s", owner.GetNamespace(), object.GetNamespace())
   231  		}
   232  	}
   233  	return nil
   234  }
   235  
   236  // Returns true if a and b point to the same object.
   237  func referSameObject(a, b metav1.OwnerReference) bool {
   238  	aGV, err := schema.ParseGroupVersion(a.APIVersion)
   239  	if err != nil {
   240  		return false
   241  	}
   242  
   243  	bGV, err := schema.ParseGroupVersion(b.APIVersion)
   244  	if err != nil {
   245  		return false
   246  	}
   247  	return aGV.Group == bGV.Group && a.Kind == b.Kind && a.Name == b.Name
   248  }
   249  
   250  // OperationResult is the action result of a CreateOrUpdate call.
   251  type OperationResult string
   252  
   253  const ( // They should complete the sentence "Deployment default/foo has been ..."
   254  	// OperationResultNone means that the resource has not been changed.
   255  	OperationResultNone OperationResult = "unchanged"
   256  	// OperationResultCreated means that a new resource is created.
   257  	OperationResultCreated OperationResult = "created"
   258  	// OperationResultUpdated means that an existing resource is updated.
   259  	OperationResultUpdated OperationResult = "updated"
   260  	// OperationResultUpdatedStatus means that an existing resource and its status is updated.
   261  	OperationResultUpdatedStatus OperationResult = "updatedStatus"
   262  	// OperationResultUpdatedStatusOnly means that only an existing status is updated.
   263  	OperationResultUpdatedStatusOnly OperationResult = "updatedStatusOnly"
   264  )
   265  
   266  // CreateOrUpdate creates or updates the given object in the Kubernetes
   267  // cluster. The object's desired state must be reconciled with the existing
   268  // state inside the passed in callback MutateFn.
   269  //
   270  // The MutateFn is called regardless of creating or updating an object.
   271  //
   272  // It returns the executed operation and an error.
   273  //
   274  // Note: changes made by MutateFn to any sub-resource (status...), will be
   275  // discarded.
   276  func CreateOrUpdate(ctx context.Context, c client.Client, obj client.Object, f MutateFn) (OperationResult, error) {
   277  	key := client.ObjectKeyFromObject(obj)
   278  	if err := c.Get(ctx, key, obj); err != nil {
   279  		if !apierrors.IsNotFound(err) {
   280  			return OperationResultNone, err
   281  		}
   282  		if err := mutate(f, key, obj); err != nil {
   283  			return OperationResultNone, err
   284  		}
   285  		if err := c.Create(ctx, obj); err != nil {
   286  			return OperationResultNone, err
   287  		}
   288  		return OperationResultCreated, nil
   289  	}
   290  
   291  	existing := obj.DeepCopyObject()
   292  	if err := mutate(f, key, obj); err != nil {
   293  		return OperationResultNone, err
   294  	}
   295  
   296  	if equality.Semantic.DeepEqual(existing, obj) {
   297  		return OperationResultNone, nil
   298  	}
   299  
   300  	if err := c.Update(ctx, obj); err != nil {
   301  		return OperationResultNone, err
   302  	}
   303  	return OperationResultUpdated, nil
   304  }
   305  
   306  // CreateOrPatch creates or patches the given object in the Kubernetes
   307  // cluster. The object's desired state must be reconciled with the before
   308  // state inside the passed in callback MutateFn.
   309  //
   310  // The MutateFn is called regardless of creating or updating an object.
   311  //
   312  // It returns the executed operation and an error.
   313  //
   314  // Note: changes to any sub-resource other than status will be ignored.
   315  // Changes to the status sub-resource will only be applied if the object
   316  // already exist. To change the status on object creation, the easiest
   317  // way is to requeue the object in the controller if OperationResult is
   318  // OperationResultCreated
   319  func CreateOrPatch(ctx context.Context, c client.Client, obj client.Object, f MutateFn) (OperationResult, error) {
   320  	key := client.ObjectKeyFromObject(obj)
   321  	if err := c.Get(ctx, key, obj); err != nil {
   322  		if !apierrors.IsNotFound(err) {
   323  			return OperationResultNone, err
   324  		}
   325  		if f != nil {
   326  			if err := mutate(f, key, obj); err != nil {
   327  				return OperationResultNone, err
   328  			}
   329  		}
   330  		if err := c.Create(ctx, obj); err != nil {
   331  			return OperationResultNone, err
   332  		}
   333  		return OperationResultCreated, nil
   334  	}
   335  
   336  	// Create patches for the object and its possible status.
   337  	objPatch := client.MergeFrom(obj.DeepCopyObject().(client.Object))
   338  	statusPatch := client.MergeFrom(obj.DeepCopyObject().(client.Object))
   339  
   340  	// Create a copy of the original object as well as converting that copy to
   341  	// unstructured data.
   342  	before, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj.DeepCopyObject())
   343  	if err != nil {
   344  		return OperationResultNone, err
   345  	}
   346  
   347  	// Attempt to extract the status from the resource for easier comparison later
   348  	beforeStatus, hasBeforeStatus, err := unstructured.NestedFieldCopy(before, "status")
   349  	if err != nil {
   350  		return OperationResultNone, err
   351  	}
   352  
   353  	// If the resource contains a status then remove it from the unstructured
   354  	// copy to avoid unnecessary patching later.
   355  	if hasBeforeStatus {
   356  		unstructured.RemoveNestedField(before, "status")
   357  	}
   358  
   359  	// Mutate the original object.
   360  	if f != nil {
   361  		if err := mutate(f, key, obj); err != nil {
   362  			return OperationResultNone, err
   363  		}
   364  	}
   365  
   366  	// Convert the resource to unstructured to compare against our before copy.
   367  	after, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj)
   368  	if err != nil {
   369  		return OperationResultNone, err
   370  	}
   371  
   372  	// Attempt to extract the status from the resource for easier comparison later
   373  	afterStatus, hasAfterStatus, err := unstructured.NestedFieldCopy(after, "status")
   374  	if err != nil {
   375  		return OperationResultNone, err
   376  	}
   377  
   378  	// If the resource contains a status then remove it from the unstructured
   379  	// copy to avoid unnecessary patching later.
   380  	if hasAfterStatus {
   381  		unstructured.RemoveNestedField(after, "status")
   382  	}
   383  
   384  	result := OperationResultNone
   385  
   386  	if !reflect.DeepEqual(before, after) {
   387  		// Only issue a Patch if the before and after resources (minus status) differ
   388  		if err := c.Patch(ctx, obj, objPatch); err != nil {
   389  			return result, err
   390  		}
   391  		result = OperationResultUpdated
   392  	}
   393  
   394  	if (hasBeforeStatus || hasAfterStatus) && !reflect.DeepEqual(beforeStatus, afterStatus) {
   395  		// Only issue a Status Patch if the resource has a status and the beforeStatus
   396  		// and afterStatus copies differ
   397  		if result == OperationResultUpdated {
   398  			// If Status was replaced by Patch before, set it to afterStatus
   399  			objectAfterPatch, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj)
   400  			if err != nil {
   401  				return result, err
   402  			}
   403  			if err = unstructured.SetNestedField(objectAfterPatch, afterStatus, "status"); err != nil {
   404  				return result, err
   405  			}
   406  			// If Status was replaced by Patch before, restore patched structure to the obj
   407  			if err = runtime.DefaultUnstructuredConverter.FromUnstructured(objectAfterPatch, obj); err != nil {
   408  				return result, err
   409  			}
   410  		}
   411  		if err := c.Status().Patch(ctx, obj, statusPatch); err != nil {
   412  			return result, err
   413  		}
   414  		if result == OperationResultUpdated {
   415  			result = OperationResultUpdatedStatus
   416  		} else {
   417  			result = OperationResultUpdatedStatusOnly
   418  		}
   419  	}
   420  
   421  	return result, nil
   422  }
   423  
   424  // mutate wraps a MutateFn and applies validation to its result.
   425  func mutate(f MutateFn, key client.ObjectKey, obj client.Object) error {
   426  	if err := f(); err != nil {
   427  		return err
   428  	}
   429  	if newKey := client.ObjectKeyFromObject(obj); key != newKey {
   430  		return fmt.Errorf("MutateFn cannot mutate object name and/or object namespace")
   431  	}
   432  	return nil
   433  }
   434  
   435  // MutateFn is a function which mutates the existing object into its desired state.
   436  type MutateFn func() error
   437  
   438  // AddFinalizer accepts an Object and adds the provided finalizer if not present.
   439  // It returns an indication of whether it updated the object's list of finalizers.
   440  func AddFinalizer(o client.Object, finalizer string) (finalizersUpdated bool) {
   441  	f := o.GetFinalizers()
   442  	for _, e := range f {
   443  		if e == finalizer {
   444  			return false
   445  		}
   446  	}
   447  	o.SetFinalizers(append(f, finalizer))
   448  	return true
   449  }
   450  
   451  // RemoveFinalizer accepts an Object and removes the provided finalizer if present.
   452  // It returns an indication of whether it updated the object's list of finalizers.
   453  func RemoveFinalizer(o client.Object, finalizer string) (finalizersUpdated bool) {
   454  	f := o.GetFinalizers()
   455  	length := len(f)
   456  
   457  	index := 0
   458  	for i := 0; i < length; i++ {
   459  		if f[i] == finalizer {
   460  			continue
   461  		}
   462  		f[index] = f[i]
   463  		index++
   464  	}
   465  	o.SetFinalizers(f[:index])
   466  	return length != index
   467  }
   468  
   469  // ContainsFinalizer checks an Object that the provided finalizer is present.
   470  func ContainsFinalizer(o client.Object, finalizer string) bool {
   471  	f := o.GetFinalizers()
   472  	for _, e := range f {
   473  		if e == finalizer {
   474  			return true
   475  		}
   476  	}
   477  	return false
   478  }
   479  

View as plain text