objectmeta.go 12 KB

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