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