k8s.io/apiserver@v0.31.1/pkg/registry/rest/update.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  
    23  	"k8s.io/apimachinery/pkg/api/errors"
    24  	"k8s.io/apimachinery/pkg/api/meta"
    25  	genericvalidation "k8s.io/apimachinery/pkg/api/validation"
    26  	"k8s.io/apimachinery/pkg/api/validation/path"
    27  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    28  	"k8s.io/apimachinery/pkg/runtime"
    29  	"k8s.io/apimachinery/pkg/util/validation/field"
    30  	"k8s.io/apiserver/pkg/admission"
    31  	genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
    32  	"k8s.io/apiserver/pkg/warning"
    33  )
    34  
    35  // RESTUpdateStrategy defines the minimum validation, accepted input, and
    36  // name generation behavior to update an object that follows Kubernetes
    37  // API conventions. A resource may have many UpdateStrategies, depending on
    38  // the call pattern in use.
    39  type RESTUpdateStrategy interface {
    40  	runtime.ObjectTyper
    41  	// NamespaceScoped returns true if the object must be within a namespace.
    42  	NamespaceScoped() bool
    43  	// AllowCreateOnUpdate returns true if the object can be created by a PUT.
    44  	AllowCreateOnUpdate() bool
    45  	// PrepareForUpdate is invoked on update before validation to normalize
    46  	// the object.  For example: remove fields that are not to be persisted,
    47  	// sort order-insensitive list fields, etc.  This should not remove fields
    48  	// whose presence would be considered a validation error.
    49  	PrepareForUpdate(ctx context.Context, obj, old runtime.Object)
    50  	// ValidateUpdate is invoked after default fields in the object have been
    51  	// filled in before the object is persisted.  This method should not mutate
    52  	// the object.
    53  	ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList
    54  	// WarningsOnUpdate returns warnings to the client performing the update.
    55  	// WarningsOnUpdate is invoked after default fields in the object have been filled in
    56  	// and after ValidateUpdate has passed, before Canonicalize is called, and before the object is persisted.
    57  	// This method must not mutate either object.
    58  	//
    59  	// Be brief; limit warnings to 120 characters if possible.
    60  	// Don't include a "Warning:" prefix in the message (that is added by clients on output).
    61  	// Warnings returned about a specific field should be formatted as "path.to.field: message".
    62  	// For example: `spec.imagePullSecrets[0].name: invalid empty name ""`
    63  	//
    64  	// Use warning messages to describe problems the client making the API request should correct or be aware of.
    65  	// For example:
    66  	// - use of deprecated fields/labels/annotations that will stop working in a future release
    67  	// - use of obsolete fields/labels/annotations that are non-functional
    68  	// - malformed or invalid specifications that prevent successful handling of the submitted object,
    69  	//   but are not rejected by validation for compatibility reasons
    70  	//
    71  	// Warnings should not be returned for fields which cannot be resolved by the caller.
    72  	// For example, do not warn about spec fields in a status update.
    73  	WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string
    74  	// Canonicalize allows an object to be mutated into a canonical form. This
    75  	// ensures that code that operates on these objects can rely on the common
    76  	// form for things like comparison.  Canonicalize is invoked after
    77  	// validation has succeeded but before the object has been persisted.
    78  	// This method may mutate the object.
    79  	Canonicalize(obj runtime.Object)
    80  	// AllowUnconditionalUpdate returns true if the object can be updated
    81  	// unconditionally (irrespective of the latest resource version), when
    82  	// there is no resource version specified in the object.
    83  	AllowUnconditionalUpdate() bool
    84  }
    85  
    86  // TODO: add other common fields that require global validation.
    87  func validateCommonFields(obj, old runtime.Object, strategy RESTUpdateStrategy) (field.ErrorList, error) {
    88  	allErrs := field.ErrorList{}
    89  	objectMeta, err := meta.Accessor(obj)
    90  	if err != nil {
    91  		return nil, fmt.Errorf("failed to get new object metadata: %v", err)
    92  	}
    93  	oldObjectMeta, err := meta.Accessor(old)
    94  	if err != nil {
    95  		return nil, fmt.Errorf("failed to get old object metadata: %v", err)
    96  	}
    97  	allErrs = append(allErrs, genericvalidation.ValidateObjectMetaAccessor(objectMeta, strategy.NamespaceScoped(), path.ValidatePathSegmentName, field.NewPath("metadata"))...)
    98  	allErrs = append(allErrs, genericvalidation.ValidateObjectMetaAccessorUpdate(objectMeta, oldObjectMeta, field.NewPath("metadata"))...)
    99  
   100  	return allErrs, nil
   101  }
   102  
   103  // BeforeUpdate ensures that common operations for all resources are performed on update. It only returns
   104  // errors that can be converted to api.Status. It will invoke update validation with the provided existing
   105  // and updated objects.
   106  // It sets zero values only if the object does not have a zero value for the respective field.
   107  func BeforeUpdate(strategy RESTUpdateStrategy, ctx context.Context, obj, old runtime.Object) error {
   108  	objectMeta, kind, kerr := objectMetaAndKind(strategy, obj)
   109  	if kerr != nil {
   110  		return kerr
   111  	}
   112  
   113  	// ensure namespace on the object is correct, or error if a conflicting namespace was set in the object
   114  	requestNamespace, ok := genericapirequest.NamespaceFrom(ctx)
   115  	if !ok {
   116  		return errors.NewInternalError(fmt.Errorf("no namespace information found in request context"))
   117  	}
   118  	if err := EnsureObjectNamespaceMatchesRequestNamespace(ExpectedNamespaceForScope(requestNamespace, strategy.NamespaceScoped()), objectMeta); err != nil {
   119  		return err
   120  	}
   121  
   122  	// Ensure requests cannot update generation
   123  	oldMeta, err := meta.Accessor(old)
   124  	if err != nil {
   125  		return err
   126  	}
   127  	objectMeta.SetGeneration(oldMeta.GetGeneration())
   128  
   129  	strategy.PrepareForUpdate(ctx, obj, old)
   130  
   131  	// Use the existing UID if none is provided
   132  	if len(objectMeta.GetUID()) == 0 {
   133  		objectMeta.SetUID(oldMeta.GetUID())
   134  	}
   135  	// ignore changes to timestamp
   136  	if oldCreationTime := oldMeta.GetCreationTimestamp(); !oldCreationTime.IsZero() {
   137  		objectMeta.SetCreationTimestamp(oldMeta.GetCreationTimestamp())
   138  	}
   139  	// an update can never remove/change a deletion timestamp
   140  	if !oldMeta.GetDeletionTimestamp().IsZero() {
   141  		objectMeta.SetDeletionTimestamp(oldMeta.GetDeletionTimestamp())
   142  	}
   143  	// an update can never remove/change grace period seconds
   144  	if oldMeta.GetDeletionGracePeriodSeconds() != nil && objectMeta.GetDeletionGracePeriodSeconds() == nil {
   145  		objectMeta.SetDeletionGracePeriodSeconds(oldMeta.GetDeletionGracePeriodSeconds())
   146  	}
   147  
   148  	// Ensure some common fields, like UID, are validated for all resources.
   149  	errs, err := validateCommonFields(obj, old, strategy)
   150  	if err != nil {
   151  		return errors.NewInternalError(err)
   152  	}
   153  
   154  	errs = append(errs, strategy.ValidateUpdate(ctx, obj, old)...)
   155  	if len(errs) > 0 {
   156  		return errors.NewInvalid(kind.GroupKind(), objectMeta.GetName(), errs)
   157  	}
   158  
   159  	for _, w := range strategy.WarningsOnUpdate(ctx, obj, old) {
   160  		warning.AddWarning(ctx, "", w)
   161  	}
   162  
   163  	strategy.Canonicalize(obj)
   164  
   165  	return nil
   166  }
   167  
   168  // TransformFunc is a function to transform and return newObj
   169  type TransformFunc func(ctx context.Context, newObj runtime.Object, oldObj runtime.Object) (transformedNewObj runtime.Object, err error)
   170  
   171  // defaultUpdatedObjectInfo implements UpdatedObjectInfo
   172  type defaultUpdatedObjectInfo struct {
   173  	// obj is the updated object
   174  	obj runtime.Object
   175  
   176  	// transformers is an optional list of transforming functions that modify or
   177  	// replace obj using information from the context, old object, or other sources.
   178  	transformers []TransformFunc
   179  }
   180  
   181  // DefaultUpdatedObjectInfo returns an UpdatedObjectInfo impl based on the specified object.
   182  func DefaultUpdatedObjectInfo(obj runtime.Object, transformers ...TransformFunc) UpdatedObjectInfo {
   183  	return &defaultUpdatedObjectInfo{obj, transformers}
   184  }
   185  
   186  // Preconditions satisfies the UpdatedObjectInfo interface.
   187  func (i *defaultUpdatedObjectInfo) Preconditions() *metav1.Preconditions {
   188  	// Attempt to get the UID out of the object
   189  	accessor, err := meta.Accessor(i.obj)
   190  	if err != nil {
   191  		// If no UID can be read, no preconditions are possible
   192  		return nil
   193  	}
   194  
   195  	// If empty, no preconditions needed
   196  	uid := accessor.GetUID()
   197  	if len(uid) == 0 {
   198  		return nil
   199  	}
   200  
   201  	return &metav1.Preconditions{UID: &uid}
   202  }
   203  
   204  // UpdatedObject satisfies the UpdatedObjectInfo interface.
   205  // It returns a copy of the held obj, passed through any configured transformers.
   206  func (i *defaultUpdatedObjectInfo) UpdatedObject(ctx context.Context, oldObj runtime.Object) (runtime.Object, error) {
   207  	var err error
   208  	// Start with the configured object
   209  	newObj := i.obj
   210  
   211  	// If the original is non-nil (might be nil if the first transformer builds the object from the oldObj), make a copy,
   212  	// so we don't return the original. BeforeUpdate can mutate the returned object, doing things like clearing ResourceVersion.
   213  	// If we're re-called, we need to be able to return the pristine version.
   214  	if newObj != nil {
   215  		newObj = newObj.DeepCopyObject()
   216  	}
   217  
   218  	// Allow any configured transformers to update the new object
   219  	for _, transformer := range i.transformers {
   220  		newObj, err = transformer(ctx, newObj, oldObj)
   221  		if err != nil {
   222  			return nil, err
   223  		}
   224  	}
   225  
   226  	return newObj, nil
   227  }
   228  
   229  // wrappedUpdatedObjectInfo allows wrapping an existing objInfo and
   230  // chaining additional transformations/checks on the result of UpdatedObject()
   231  type wrappedUpdatedObjectInfo struct {
   232  	// obj is the updated object
   233  	objInfo UpdatedObjectInfo
   234  
   235  	// transformers is an optional list of transforming functions that modify or
   236  	// replace obj using information from the context, old object, or other sources.
   237  	transformers []TransformFunc
   238  }
   239  
   240  // WrapUpdatedObjectInfo returns an UpdatedObjectInfo impl that delegates to
   241  // the specified objInfo, then calls the passed transformers
   242  func WrapUpdatedObjectInfo(objInfo UpdatedObjectInfo, transformers ...TransformFunc) UpdatedObjectInfo {
   243  	return &wrappedUpdatedObjectInfo{objInfo, transformers}
   244  }
   245  
   246  // Preconditions satisfies the UpdatedObjectInfo interface.
   247  func (i *wrappedUpdatedObjectInfo) Preconditions() *metav1.Preconditions {
   248  	return i.objInfo.Preconditions()
   249  }
   250  
   251  // UpdatedObject satisfies the UpdatedObjectInfo interface.
   252  // It delegates to the wrapped objInfo and passes the result through any configured transformers.
   253  func (i *wrappedUpdatedObjectInfo) UpdatedObject(ctx context.Context, oldObj runtime.Object) (runtime.Object, error) {
   254  	newObj, err := i.objInfo.UpdatedObject(ctx, oldObj)
   255  	if err != nil {
   256  		return newObj, err
   257  	}
   258  
   259  	// Allow any configured transformers to update the new object or error
   260  	for _, transformer := range i.transformers {
   261  		newObj, err = transformer(ctx, newObj, oldObj)
   262  		if err != nil {
   263  			return nil, err
   264  		}
   265  	}
   266  
   267  	return newObj, nil
   268  }
   269  
   270  // AdmissionToValidateObjectUpdateFunc converts validating admission to a rest validate object update func
   271  func AdmissionToValidateObjectUpdateFunc(admit admission.Interface, staticAttributes admission.Attributes, o admission.ObjectInterfaces) ValidateObjectUpdateFunc {
   272  	validatingAdmission, ok := admit.(admission.ValidationInterface)
   273  	if !ok {
   274  		return func(ctx context.Context, obj, old runtime.Object) error { return nil }
   275  	}
   276  	return func(ctx context.Context, obj, old runtime.Object) error {
   277  		finalAttributes := admission.NewAttributesRecord(
   278  			obj,
   279  			old,
   280  			staticAttributes.GetKind(),
   281  			staticAttributes.GetNamespace(),
   282  			staticAttributes.GetName(),
   283  			staticAttributes.GetResource(),
   284  			staticAttributes.GetSubresource(),
   285  			staticAttributes.GetOperation(),
   286  			staticAttributes.GetOperationOptions(),
   287  			staticAttributes.IsDryRun(),
   288  			staticAttributes.GetUserInfo(),
   289  		)
   290  		if !validatingAdmission.Handles(finalAttributes.GetOperation()) {
   291  			return nil
   292  		}
   293  		return validatingAdmission.Validate(ctx, finalAttributes, o)
   294  	}
   295  }