k8s.io/apiserver@v0.31.1/pkg/registry/rest/delete.go (about)

     1  /*
     2  Copyright 2014 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 rest
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"time"
    23  
    24  	"k8s.io/apimachinery/pkg/api/errors"
    25  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    26  	"k8s.io/apimachinery/pkg/apis/meta/v1/validation"
    27  	"k8s.io/apimachinery/pkg/runtime"
    28  	"k8s.io/apimachinery/pkg/runtime/schema"
    29  	"k8s.io/apiserver/pkg/admission"
    30  	utilpointer "k8s.io/utils/pointer"
    31  )
    32  
    33  // RESTDeleteStrategy defines deletion behavior on an object that follows Kubernetes
    34  // API conventions.
    35  type RESTDeleteStrategy interface {
    36  	runtime.ObjectTyper
    37  }
    38  
    39  type GarbageCollectionPolicy string
    40  
    41  const (
    42  	DeleteDependents GarbageCollectionPolicy = "DeleteDependents"
    43  	OrphanDependents GarbageCollectionPolicy = "OrphanDependents"
    44  	// Unsupported means that the resource knows that it cannot be GC'd, so the finalizers
    45  	// should never be set in storage.
    46  	Unsupported GarbageCollectionPolicy = "Unsupported"
    47  )
    48  
    49  // GarbageCollectionDeleteStrategy must be implemented by the registry that wants to
    50  // orphan dependents by default.
    51  type GarbageCollectionDeleteStrategy interface {
    52  	// DefaultGarbageCollectionPolicy returns the default garbage collection behavior.
    53  	DefaultGarbageCollectionPolicy(ctx context.Context) GarbageCollectionPolicy
    54  }
    55  
    56  // RESTGracefulDeleteStrategy must be implemented by the registry that supports
    57  // graceful deletion.
    58  type RESTGracefulDeleteStrategy interface {
    59  	// CheckGracefulDelete should return true if the object can be gracefully deleted and set
    60  	// any default values on the DeleteOptions.
    61  	// NOTE: if return true, `options.GracePeriodSeconds` must be non-nil (nil will fail),
    62  	// that's what tells the deletion how "graceful" to be.
    63  	CheckGracefulDelete(ctx context.Context, obj runtime.Object, options *metav1.DeleteOptions) bool
    64  }
    65  
    66  // BeforeDelete tests whether the object can be gracefully deleted.
    67  // If graceful is set, the object should be gracefully deleted.  If gracefulPending
    68  // is set, the object has already been gracefully deleted (and the provided grace
    69  // period is longer than the time to deletion). An error is returned if the
    70  // condition cannot be checked or the gracePeriodSeconds is invalid. The options
    71  // argument may be updated with default values if graceful is true. Second place
    72  // where we set deletionTimestamp is pkg/registry/generic/registry/store.go.
    73  // This function is responsible for setting deletionTimestamp during gracefulDeletion,
    74  // other one for cascading deletions.
    75  func BeforeDelete(strategy RESTDeleteStrategy, ctx context.Context, obj runtime.Object, options *metav1.DeleteOptions) (graceful, gracefulPending bool, err error) {
    76  	objectMeta, gvk, kerr := objectMetaAndKind(strategy, obj)
    77  	if kerr != nil {
    78  		return false, false, kerr
    79  	}
    80  	if errs := validation.ValidateDeleteOptions(options); len(errs) > 0 {
    81  		return false, false, errors.NewInvalid(schema.GroupKind{Group: metav1.GroupName, Kind: "DeleteOptions"}, "", errs)
    82  	}
    83  	// Checking the Preconditions here to fail early. They'll be enforced later on when we actually do the deletion, too.
    84  	if options.Preconditions != nil {
    85  		if options.Preconditions.UID != nil && *options.Preconditions.UID != objectMeta.GetUID() {
    86  			return false, false, errors.NewConflict(schema.GroupResource{Group: gvk.Group, Resource: gvk.Kind}, objectMeta.GetName(), fmt.Errorf("the UID in the precondition (%s) does not match the UID in record (%s). The object might have been deleted and then recreated", *options.Preconditions.UID, objectMeta.GetUID()))
    87  		}
    88  		if options.Preconditions.ResourceVersion != nil && *options.Preconditions.ResourceVersion != objectMeta.GetResourceVersion() {
    89  			return false, false, errors.NewConflict(schema.GroupResource{Group: gvk.Group, Resource: gvk.Kind}, objectMeta.GetName(), fmt.Errorf("the ResourceVersion in the precondition (%s) does not match the ResourceVersion in record (%s). The object might have been modified", *options.Preconditions.ResourceVersion, objectMeta.GetResourceVersion()))
    90  		}
    91  	}
    92  
    93  	// Negative values will be treated as the value `1s` on the delete path.
    94  	if gracePeriodSeconds := options.GracePeriodSeconds; gracePeriodSeconds != nil && *gracePeriodSeconds < 0 {
    95  		options.GracePeriodSeconds = utilpointer.Int64(1)
    96  	}
    97  	if deletionGracePeriodSeconds := objectMeta.GetDeletionGracePeriodSeconds(); deletionGracePeriodSeconds != nil && *deletionGracePeriodSeconds < 0 {
    98  		objectMeta.SetDeletionGracePeriodSeconds(utilpointer.Int64(1))
    99  	}
   100  
   101  	gracefulStrategy, ok := strategy.(RESTGracefulDeleteStrategy)
   102  	if !ok {
   103  		// If we're not deleting gracefully there's no point in updating Generation, as we won't update
   104  		// the obcject before deleting it.
   105  		return false, false, nil
   106  	}
   107  	// if the object is already being deleted, no need to update generation.
   108  	if objectMeta.GetDeletionTimestamp() != nil {
   109  		// if we are already being deleted, we may only shorten the deletion grace period
   110  		// this means the object was gracefully deleted previously but deletionGracePeriodSeconds was not set,
   111  		// so we force deletion immediately
   112  		// IMPORTANT:
   113  		// The deletion operation happens in two phases.
   114  		// 1. Update to set DeletionGracePeriodSeconds and DeletionTimestamp
   115  		// 2. Delete the object from storage.
   116  		// If the update succeeds, but the delete fails (network error, internal storage error, etc.),
   117  		// a resource was previously left in a state that was non-recoverable.  We
   118  		// check if the existing stored resource has a grace period as 0 and if so
   119  		// attempt to delete immediately in order to recover from this scenario.
   120  		if objectMeta.GetDeletionGracePeriodSeconds() == nil || *objectMeta.GetDeletionGracePeriodSeconds() == 0 {
   121  			return false, false, nil
   122  		}
   123  		// only a shorter grace period may be provided by a user
   124  		if options.GracePeriodSeconds != nil {
   125  			period := int64(*options.GracePeriodSeconds)
   126  			if period >= *objectMeta.GetDeletionGracePeriodSeconds() {
   127  				return false, true, nil
   128  			}
   129  			newDeletionTimestamp := metav1.NewTime(
   130  				objectMeta.GetDeletionTimestamp().Add(-time.Second * time.Duration(*objectMeta.GetDeletionGracePeriodSeconds())).
   131  					Add(time.Second * time.Duration(*options.GracePeriodSeconds)))
   132  			objectMeta.SetDeletionTimestamp(&newDeletionTimestamp)
   133  			objectMeta.SetDeletionGracePeriodSeconds(&period)
   134  			return true, false, nil
   135  		}
   136  		// graceful deletion is pending, do nothing
   137  		options.GracePeriodSeconds = objectMeta.GetDeletionGracePeriodSeconds()
   138  		return false, true, nil
   139  	}
   140  
   141  	// `CheckGracefulDelete` will be implemented by specific strategy
   142  	if !gracefulStrategy.CheckGracefulDelete(ctx, obj, options) {
   143  		return false, false, nil
   144  	}
   145  
   146  	if options.GracePeriodSeconds == nil {
   147  		return false, false, errors.NewInternalError(fmt.Errorf("options.GracePeriodSeconds should not be nil"))
   148  	}
   149  
   150  	now := metav1.NewTime(metav1.Now().Add(time.Second * time.Duration(*options.GracePeriodSeconds)))
   151  	objectMeta.SetDeletionTimestamp(&now)
   152  	objectMeta.SetDeletionGracePeriodSeconds(options.GracePeriodSeconds)
   153  	// If it's the first graceful deletion we are going to set the DeletionTimestamp to non-nil.
   154  	// Controllers of the object that's being deleted shouldn't take any nontrivial actions, hence its behavior changes.
   155  	// Thus we need to bump object's Generation (if set). This handles generation bump during graceful deletion.
   156  	// The bump for objects that don't support graceful deletion is handled in pkg/registry/generic/registry/store.go.
   157  	if objectMeta.GetGeneration() > 0 {
   158  		objectMeta.SetGeneration(objectMeta.GetGeneration() + 1)
   159  	}
   160  
   161  	return true, false, nil
   162  }
   163  
   164  // AdmissionToValidateObjectDeleteFunc returns a admission validate func for object deletion
   165  func AdmissionToValidateObjectDeleteFunc(admit admission.Interface, staticAttributes admission.Attributes, objInterfaces admission.ObjectInterfaces) ValidateObjectFunc {
   166  	mutatingAdmission, isMutatingAdmission := admit.(admission.MutationInterface)
   167  	validatingAdmission, isValidatingAdmission := admit.(admission.ValidationInterface)
   168  
   169  	mutating := isMutatingAdmission && mutatingAdmission.Handles(staticAttributes.GetOperation())
   170  	validating := isValidatingAdmission && validatingAdmission.Handles(staticAttributes.GetOperation())
   171  
   172  	return func(ctx context.Context, old runtime.Object) error {
   173  		if !mutating && !validating {
   174  			return nil
   175  		}
   176  		finalAttributes := admission.NewAttributesRecord(
   177  			nil,
   178  			// Deep copy the object to avoid accidentally changing the object.
   179  			old.DeepCopyObject(),
   180  			staticAttributes.GetKind(),
   181  			staticAttributes.GetNamespace(),
   182  			staticAttributes.GetName(),
   183  			staticAttributes.GetResource(),
   184  			staticAttributes.GetSubresource(),
   185  			staticAttributes.GetOperation(),
   186  			staticAttributes.GetOperationOptions(),
   187  			staticAttributes.IsDryRun(),
   188  			staticAttributes.GetUserInfo(),
   189  		)
   190  		if mutating {
   191  			if err := mutatingAdmission.Admit(ctx, finalAttributes, objInterfaces); err != nil {
   192  				return err
   193  			}
   194  		}
   195  		if validating {
   196  			if err := validatingAdmission.Validate(ctx, finalAttributes, objInterfaces); err != nil {
   197  				return err
   198  			}
   199  		}
   200  		return nil
   201  	}
   202  }