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 }