github.com/spotmaxtech/k8s-apimachinery-v0260@v0.0.1/pkg/api/validation/objectmeta.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 validation
    18  
    19  import (
    20  	"fmt"
    21  	"strings"
    22  
    23  	apiequality "github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/api/equality"
    24  	"github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/api/meta"
    25  	metav1 "github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/apis/meta/v1"
    26  	v1validation "github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/apis/meta/v1/validation"
    27  	"github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/runtime/schema"
    28  	"github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/util/sets"
    29  	"github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/util/validation"
    30  	"github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/util/validation/field"
    31  )
    32  
    33  // FieldImmutableErrorMsg is a error message for field is immutable.
    34  const FieldImmutableErrorMsg string = `field is immutable`
    35  
    36  const TotalAnnotationSizeLimitB int = 256 * (1 << 10) // 256 kB
    37  
    38  // BannedOwners is a black list of object that are not allowed to be owners.
    39  var BannedOwners = map[schema.GroupVersionKind]struct{}{
    40  	{Group: "", Version: "v1", Kind: "Event"}: {},
    41  }
    42  
    43  // ValidateAnnotations validates that a set of annotations are correctly defined.
    44  func ValidateAnnotations(annotations map[string]string, fldPath *field.Path) field.ErrorList {
    45  	allErrs := field.ErrorList{}
    46  	for k := range annotations {
    47  		// The rule is QualifiedName except that case doesn't matter, so convert to lowercase before checking.
    48  		for _, msg := range validation.IsQualifiedName(strings.ToLower(k)) {
    49  			allErrs = append(allErrs, field.Invalid(fldPath, k, msg))
    50  		}
    51  	}
    52  	if err := ValidateAnnotationsSize(annotations); err != nil {
    53  		allErrs = append(allErrs, field.TooLong(fldPath, "", TotalAnnotationSizeLimitB))
    54  	}
    55  	return allErrs
    56  }
    57  
    58  func ValidateAnnotationsSize(annotations map[string]string) error {
    59  	var totalSize int64
    60  	for k, v := range annotations {
    61  		totalSize += (int64)(len(k)) + (int64)(len(v))
    62  	}
    63  	if totalSize > (int64)(TotalAnnotationSizeLimitB) {
    64  		return fmt.Errorf("annotations size %d is larger than limit %d", totalSize, TotalAnnotationSizeLimitB)
    65  	}
    66  	return nil
    67  }
    68  
    69  func validateOwnerReference(ownerReference metav1.OwnerReference, fldPath *field.Path) field.ErrorList {
    70  	allErrs := field.ErrorList{}
    71  	gvk := schema.FromAPIVersionAndKind(ownerReference.APIVersion, ownerReference.Kind)
    72  	// gvk.Group is empty for the legacy group.
    73  	if len(gvk.Version) == 0 {
    74  		allErrs = append(allErrs, field.Invalid(fldPath.Child("apiVersion"), ownerReference.APIVersion, "version must not be empty"))
    75  	}
    76  	if len(gvk.Kind) == 0 {
    77  		allErrs = append(allErrs, field.Invalid(fldPath.Child("kind"), ownerReference.Kind, "kind must not be empty"))
    78  	}
    79  	if len(ownerReference.Name) == 0 {
    80  		allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), ownerReference.Name, "name must not be empty"))
    81  	}
    82  	if len(ownerReference.UID) == 0 {
    83  		allErrs = append(allErrs, field.Invalid(fldPath.Child("uid"), ownerReference.UID, "uid must not be empty"))
    84  	}
    85  	if _, ok := BannedOwners[gvk]; ok {
    86  		allErrs = append(allErrs, field.Invalid(fldPath, ownerReference, fmt.Sprintf("%s is disallowed from being an owner", gvk)))
    87  	}
    88  	return allErrs
    89  }
    90  
    91  // ValidateOwnerReferences validates that a set of owner references are correctly defined.
    92  func ValidateOwnerReferences(ownerReferences []metav1.OwnerReference, fldPath *field.Path) field.ErrorList {
    93  	allErrs := field.ErrorList{}
    94  	firstControllerName := ""
    95  	for _, ref := range ownerReferences {
    96  		allErrs = append(allErrs, validateOwnerReference(ref, fldPath)...)
    97  		if ref.Controller != nil && *ref.Controller {
    98  			curControllerName := ref.Kind + "/" + ref.Name
    99  			if firstControllerName != "" {
   100  				allErrs = append(allErrs, field.Invalid(fldPath, ownerReferences,
   101  					fmt.Sprintf("Only one reference can have Controller set to true. Found \"true\" in references for %v and %v", firstControllerName, curControllerName)))
   102  			} else {
   103  				firstControllerName = curControllerName
   104  			}
   105  		}
   106  	}
   107  	return allErrs
   108  }
   109  
   110  // ValidateFinalizerName validates finalizer names.
   111  func ValidateFinalizerName(stringValue string, fldPath *field.Path) field.ErrorList {
   112  	allErrs := field.ErrorList{}
   113  	for _, msg := range validation.IsQualifiedName(stringValue) {
   114  		allErrs = append(allErrs, field.Invalid(fldPath, stringValue, msg))
   115  	}
   116  
   117  	return allErrs
   118  }
   119  
   120  // ValidateNoNewFinalizers validates the new finalizers has no new finalizers compare to old finalizers.
   121  func ValidateNoNewFinalizers(newFinalizers []string, oldFinalizers []string, fldPath *field.Path) field.ErrorList {
   122  	allErrs := field.ErrorList{}
   123  	extra := sets.NewString(newFinalizers...).Difference(sets.NewString(oldFinalizers...))
   124  	if len(extra) != 0 {
   125  		allErrs = append(allErrs, field.Forbidden(fldPath, fmt.Sprintf("no new finalizers can be added if the object is being deleted, found new finalizers %#v", extra.List())))
   126  	}
   127  	return allErrs
   128  }
   129  
   130  // ValidateImmutableField validates the new value and the old value are deeply equal.
   131  func ValidateImmutableField(newVal, oldVal interface{}, fldPath *field.Path) field.ErrorList {
   132  	allErrs := field.ErrorList{}
   133  	if !apiequality.Semantic.DeepEqual(oldVal, newVal) {
   134  		allErrs = append(allErrs, field.Invalid(fldPath, newVal, FieldImmutableErrorMsg))
   135  	}
   136  	return allErrs
   137  }
   138  
   139  // ValidateObjectMeta validates an object's metadata on creation. It expects that name generation has already
   140  // been performed.
   141  // It doesn't return an error for rootscoped resources with namespace, because namespace should already be cleared before.
   142  func ValidateObjectMeta(objMeta *metav1.ObjectMeta, requiresNamespace bool, nameFn ValidateNameFunc, fldPath *field.Path) field.ErrorList {
   143  	metadata, err := meta.Accessor(objMeta)
   144  	if err != nil {
   145  		var allErrs field.ErrorList
   146  		allErrs = append(allErrs, field.Invalid(fldPath, objMeta, err.Error()))
   147  		return allErrs
   148  	}
   149  	return ValidateObjectMetaAccessor(metadata, requiresNamespace, nameFn, fldPath)
   150  }
   151  
   152  // ValidateObjectMetaAccessor validates an object's metadata on creation. It expects that name generation has already
   153  // been performed.
   154  // It doesn't return an error for rootscoped resources with namespace, because namespace should already be cleared before.
   155  func ValidateObjectMetaAccessor(meta metav1.Object, requiresNamespace bool, nameFn ValidateNameFunc, fldPath *field.Path) field.ErrorList {
   156  	var allErrs field.ErrorList
   157  
   158  	if len(meta.GetGenerateName()) != 0 {
   159  		for _, msg := range nameFn(meta.GetGenerateName(), true) {
   160  			allErrs = append(allErrs, field.Invalid(fldPath.Child("generateName"), meta.GetGenerateName(), msg))
   161  		}
   162  	}
   163  	// If the generated name validates, but the calculated value does not, it's a problem with generation, and we
   164  	// report it here. This may confuse users, but indicates a programming bug and still must be validated.
   165  	// If there are multiple fields out of which one is required then add an or as a separator
   166  	if len(meta.GetName()) == 0 {
   167  		allErrs = append(allErrs, field.Required(fldPath.Child("name"), "name or generateName is required"))
   168  	} else {
   169  		for _, msg := range nameFn(meta.GetName(), false) {
   170  			allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), meta.GetName(), msg))
   171  		}
   172  	}
   173  	if requiresNamespace {
   174  		if len(meta.GetNamespace()) == 0 {
   175  			allErrs = append(allErrs, field.Required(fldPath.Child("namespace"), ""))
   176  		} else {
   177  			for _, msg := range ValidateNamespaceName(meta.GetNamespace(), false) {
   178  				allErrs = append(allErrs, field.Invalid(fldPath.Child("namespace"), meta.GetNamespace(), msg))
   179  			}
   180  		}
   181  	} else {
   182  		if len(meta.GetNamespace()) != 0 {
   183  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("namespace"), "not allowed on this type"))
   184  		}
   185  	}
   186  
   187  	allErrs = append(allErrs, ValidateNonnegativeField(meta.GetGeneration(), fldPath.Child("generation"))...)
   188  	allErrs = append(allErrs, v1validation.ValidateLabels(meta.GetLabels(), fldPath.Child("labels"))...)
   189  	allErrs = append(allErrs, ValidateAnnotations(meta.GetAnnotations(), fldPath.Child("annotations"))...)
   190  	allErrs = append(allErrs, ValidateOwnerReferences(meta.GetOwnerReferences(), fldPath.Child("ownerReferences"))...)
   191  	allErrs = append(allErrs, ValidateFinalizers(meta.GetFinalizers(), fldPath.Child("finalizers"))...)
   192  	allErrs = append(allErrs, v1validation.ValidateManagedFields(meta.GetManagedFields(), fldPath.Child("managedFields"))...)
   193  	return allErrs
   194  }
   195  
   196  // ValidateFinalizers tests if the finalizers name are valid, and if there are conflicting finalizers.
   197  func ValidateFinalizers(finalizers []string, fldPath *field.Path) field.ErrorList {
   198  	allErrs := field.ErrorList{}
   199  	hasFinalizerOrphanDependents := false
   200  	hasFinalizerDeleteDependents := false
   201  	for _, finalizer := range finalizers {
   202  		allErrs = append(allErrs, ValidateFinalizerName(finalizer, fldPath)...)
   203  		if finalizer == metav1.FinalizerOrphanDependents {
   204  			hasFinalizerOrphanDependents = true
   205  		}
   206  		if finalizer == metav1.FinalizerDeleteDependents {
   207  			hasFinalizerDeleteDependents = true
   208  		}
   209  	}
   210  	if hasFinalizerDeleteDependents && hasFinalizerOrphanDependents {
   211  		allErrs = append(allErrs, field.Invalid(fldPath, finalizers, fmt.Sprintf("finalizer %s and %s cannot be both set", metav1.FinalizerOrphanDependents, metav1.FinalizerDeleteDependents)))
   212  	}
   213  	return allErrs
   214  }
   215  
   216  // ValidateObjectMetaUpdate validates an object's metadata when updated.
   217  func ValidateObjectMetaUpdate(newMeta, oldMeta *metav1.ObjectMeta, fldPath *field.Path) field.ErrorList {
   218  	newMetadata, err := meta.Accessor(newMeta)
   219  	if err != nil {
   220  		allErrs := field.ErrorList{}
   221  		allErrs = append(allErrs, field.Invalid(fldPath, newMeta, err.Error()))
   222  		return allErrs
   223  	}
   224  	oldMetadata, err := meta.Accessor(oldMeta)
   225  	if err != nil {
   226  		allErrs := field.ErrorList{}
   227  		allErrs = append(allErrs, field.Invalid(fldPath, oldMeta, err.Error()))
   228  		return allErrs
   229  	}
   230  	return ValidateObjectMetaAccessorUpdate(newMetadata, oldMetadata, fldPath)
   231  }
   232  
   233  // ValidateObjectMetaAccessorUpdate validates an object's metadata when updated.
   234  func ValidateObjectMetaAccessorUpdate(newMeta, oldMeta metav1.Object, fldPath *field.Path) field.ErrorList {
   235  	var allErrs field.ErrorList
   236  
   237  	// Finalizers cannot be added if the object is already being deleted.
   238  	if oldMeta.GetDeletionTimestamp() != nil {
   239  		allErrs = append(allErrs, ValidateNoNewFinalizers(newMeta.GetFinalizers(), oldMeta.GetFinalizers(), fldPath.Child("finalizers"))...)
   240  	}
   241  
   242  	// Reject updates that don't specify a resource version
   243  	if len(newMeta.GetResourceVersion()) == 0 {
   244  		allErrs = append(allErrs, field.Invalid(fldPath.Child("resourceVersion"), newMeta.GetResourceVersion(), "must be specified for an update"))
   245  	}
   246  
   247  	// Generation shouldn't be decremented
   248  	if newMeta.GetGeneration() < oldMeta.GetGeneration() {
   249  		allErrs = append(allErrs, field.Invalid(fldPath.Child("generation"), newMeta.GetGeneration(), "must not be decremented"))
   250  	}
   251  
   252  	allErrs = append(allErrs, ValidateImmutableField(newMeta.GetName(), oldMeta.GetName(), fldPath.Child("name"))...)
   253  	allErrs = append(allErrs, ValidateImmutableField(newMeta.GetNamespace(), oldMeta.GetNamespace(), fldPath.Child("namespace"))...)
   254  	allErrs = append(allErrs, ValidateImmutableField(newMeta.GetUID(), oldMeta.GetUID(), fldPath.Child("uid"))...)
   255  	allErrs = append(allErrs, ValidateImmutableField(newMeta.GetCreationTimestamp(), oldMeta.GetCreationTimestamp(), fldPath.Child("creationTimestamp"))...)
   256  	allErrs = append(allErrs, ValidateImmutableField(newMeta.GetDeletionTimestamp(), oldMeta.GetDeletionTimestamp(), fldPath.Child("deletionTimestamp"))...)
   257  	allErrs = append(allErrs, ValidateImmutableField(newMeta.GetDeletionGracePeriodSeconds(), oldMeta.GetDeletionGracePeriodSeconds(), fldPath.Child("deletionGracePeriodSeconds"))...)
   258  
   259  	allErrs = append(allErrs, v1validation.ValidateLabels(newMeta.GetLabels(), fldPath.Child("labels"))...)
   260  	allErrs = append(allErrs, ValidateAnnotations(newMeta.GetAnnotations(), fldPath.Child("annotations"))...)
   261  	allErrs = append(allErrs, ValidateOwnerReferences(newMeta.GetOwnerReferences(), fldPath.Child("ownerReferences"))...)
   262  	allErrs = append(allErrs, v1validation.ValidateManagedFields(newMeta.GetManagedFields(), fldPath.Child("managedFields"))...)
   263  
   264  	return allErrs
   265  }