objectmeta.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  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)).WithOrigin("format=k8s-label-key")
  40. }
  41. }
  42. if err := ValidateAnnotationsSize(annotations); err != nil {
  43. allErrs = append(allErrs, field.TooLong(fldPath, "" /*unused*/, 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, "must not be empty"))
  66. }
  67. if len(ownerReference.Name) == 0 {
  68. allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), ownerReference.Name, "must not be empty"))
  69. }
  70. if len(ownerReference.UID) == 0 {
  71. allErrs = append(allErrs, field.Invalid(fldPath.Child("uid"), ownerReference.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. func ValidateObjectMeta(objMeta *metav1.ObjectMeta, requiresNamespace bool, nameFn ValidateNameFunc, fldPath *field.Path) field.ErrorList {
  124. metadata, err := meta.Accessor(objMeta)
  125. if err != nil {
  126. var allErrs field.ErrorList
  127. allErrs = append(allErrs, field.Invalid(fldPath, objMeta, err.Error()))
  128. return allErrs
  129. }
  130. return ValidateObjectMetaAccessor(metadata, requiresNamespace, nameFn, fldPath)
  131. }
  132. // objectMetaValidationOptions defines behavioral modifications for validating
  133. // an ObjectMeta.
  134. type objectMetaValidationOptions struct {
  135. /* nothing here yet */
  136. }
  137. // ObjectMetaValidationOption specifies a behavioral modifier for
  138. // ValidateObjectMetaWithOpts and ValidateObjectMetaAccessorWithOpts.
  139. type ObjectMetaValidationOption func(opts *objectMetaValidationOptions)
  140. // ValidateObjectMetaWithOpts validates an object's metadata on creation. It
  141. // expects that name generation has already been performed, so name validation
  142. // is always executed.
  143. //
  144. // This is similar to ValidateObjectMeta, but uses options to buy future-safety
  145. // and uses different signature for the name validation function. It also does
  146. // not directly validate the generateName field, because name generation
  147. // should have already been performed and it is the result of that generastion
  148. // that must conform to the nameFn.
  149. func ValidateObjectMetaWithOpts(objMeta *metav1.ObjectMeta, isNamespaced bool, nameFn ValidateNameFuncWithErrors, fldPath *field.Path, options ...ObjectMetaValidationOption) field.ErrorList {
  150. metadata, err := meta.Accessor(objMeta)
  151. if err != nil {
  152. var allErrs field.ErrorList
  153. allErrs = append(allErrs, field.InternalError(fldPath, err))
  154. return allErrs
  155. }
  156. return ValidateObjectMetaAccessorWithOpts(metadata, isNamespaced, nameFn, fldPath, options...)
  157. }
  158. // ValidateObjectMetaAccessor validates an object's metadata on creation. It expects that name generation has already
  159. // been performed.
  160. func ValidateObjectMetaAccessor(meta metav1.Object, requiresNamespace bool, nameFn ValidateNameFunc, fldPath *field.Path) field.ErrorList {
  161. var allErrs field.ErrorList
  162. if len(meta.GetGenerateName()) != 0 {
  163. for _, msg := range nameFn(meta.GetGenerateName(), true) {
  164. allErrs = append(allErrs, field.Invalid(fldPath.Child("generateName"), meta.GetGenerateName(), msg))
  165. }
  166. }
  167. // If the generated name validates, but the calculated value does not, it's a problem with generation, and we
  168. // report it here. This may confuse users, but indicates a programming bug and still must be validated.
  169. // If there are multiple fields out of which one is required then add an or as a separator
  170. if len(meta.GetName()) == 0 {
  171. allErrs = append(allErrs, field.Required(fldPath.Child("name"), "name or generateName is required"))
  172. } else {
  173. for _, msg := range nameFn(meta.GetName(), false) {
  174. allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), meta.GetName(), msg))
  175. }
  176. }
  177. return append(allErrs, validateObjectMetaAccessorWithOptsCommon(meta, requiresNamespace, fldPath, nil)...)
  178. }
  179. // ValidateObjectMetaAccessorWithOpts validates an object's metadata on
  180. // creation. It expects that name generation has already been performed, so
  181. // name validation is always executed.
  182. //
  183. // This is similar to ValidateObjectMetaAccessor, but uses options to buy
  184. // future-safety and uses different signature for the name validation function.
  185. // It also does not directly validate the generateName field, because name
  186. // generation should have already been performed and it is the result of that
  187. // generastion that must conform to the nameFn.
  188. func ValidateObjectMetaAccessorWithOpts(meta metav1.Object, isNamespaced bool, nameFn ValidateNameFuncWithErrors, fldPath *field.Path, options ...ObjectMetaValidationOption) field.ErrorList {
  189. opts := objectMetaValidationOptions{}
  190. for _, opt := range options {
  191. opt(&opts)
  192. }
  193. var allErrs field.ErrorList
  194. // generateName is not directly validated here. Types can have
  195. // different rules for name generation, and the nameFn is for validating
  196. // the post-generation data, not the input. In the past we assumed that
  197. // name generation was always "append 5 random characters", but that's not
  198. // NECESSARILY true. Also, the nameFn should always be considering the max
  199. // length of the name, and it doesn't know enough about the name generation
  200. // to do that. Also, given a bad generateName, the user will get errors
  201. // for both the generateName and name fields. We will focus validation on
  202. // the name field, which should give a better UX overall.
  203. // TODO(thockin): should we do a max-length check here? e.g. 1K or 4K?
  204. if len(meta.GetGenerateName()) != 0 && len(meta.GetName()) == 0 {
  205. allErrs = append(allErrs,
  206. field.InternalError(fldPath.Child("name"), fmt.Errorf("generateName was specified (%q), but no name was generated", meta.GetGenerateName())))
  207. }
  208. if len(meta.GetName()) == 0 {
  209. allErrs = append(allErrs, field.Required(fldPath.Child("name"), "name or generateName is required"))
  210. } else {
  211. allErrs = append(allErrs, nameFn(fldPath.Child("name"), meta.GetName())...)
  212. }
  213. return append(allErrs, validateObjectMetaAccessorWithOptsCommon(meta, isNamespaced, fldPath, &opts)...)
  214. }
  215. // validateObjectMetaAccessorWithOptsCommon is a shared function for validating
  216. // the parts of an ObjectMeta with are handled the same in both paths..
  217. func validateObjectMetaAccessorWithOptsCommon(meta metav1.Object, isNamespaced bool, fldPath *field.Path, _ *objectMetaValidationOptions) field.ErrorList {
  218. var allErrs field.ErrorList
  219. if isNamespaced {
  220. if len(meta.GetNamespace()) == 0 {
  221. allErrs = append(allErrs, field.Required(fldPath.Child("namespace"), ""))
  222. } else {
  223. for _, msg := range ValidateNamespaceName(meta.GetNamespace(), false) {
  224. allErrs = append(allErrs, field.Invalid(fldPath.Child("namespace"), meta.GetNamespace(), msg))
  225. }
  226. }
  227. } else {
  228. if len(meta.GetNamespace()) != 0 {
  229. // TODO(thockin): change to "may not be specified on this type" or something
  230. allErrs = append(allErrs, field.Forbidden(fldPath.Child("namespace"), "not allowed on this type"))
  231. }
  232. }
  233. allErrs = append(allErrs, ValidateNonnegativeField(meta.GetGeneration(), fldPath.Child("generation"))...)
  234. allErrs = append(allErrs, v1validation.ValidateLabels(meta.GetLabels(), fldPath.Child("labels"))...)
  235. allErrs = append(allErrs, ValidateAnnotations(meta.GetAnnotations(), fldPath.Child("annotations"))...)
  236. allErrs = append(allErrs, ValidateOwnerReferences(meta.GetOwnerReferences(), fldPath.Child("ownerReferences"))...)
  237. allErrs = append(allErrs, ValidateFinalizers(meta.GetFinalizers(), fldPath.Child("finalizers"))...)
  238. allErrs = append(allErrs, v1validation.ValidateManagedFields(meta.GetManagedFields(), fldPath.Child("managedFields"))...)
  239. return allErrs
  240. }
  241. // ValidateFinalizers tests if the finalizers name are valid, and if there are conflicting finalizers.
  242. func ValidateFinalizers(finalizers []string, fldPath *field.Path) field.ErrorList {
  243. allErrs := field.ErrorList{}
  244. hasFinalizerOrphanDependents := false
  245. hasFinalizerDeleteDependents := false
  246. for _, finalizer := range finalizers {
  247. allErrs = append(allErrs, ValidateFinalizerName(finalizer, fldPath)...)
  248. if finalizer == metav1.FinalizerOrphanDependents {
  249. hasFinalizerOrphanDependents = true
  250. }
  251. if finalizer == metav1.FinalizerDeleteDependents {
  252. hasFinalizerDeleteDependents = true
  253. }
  254. }
  255. if hasFinalizerDeleteDependents && hasFinalizerOrphanDependents {
  256. allErrs = append(allErrs, field.Invalid(fldPath, finalizers, fmt.Sprintf("finalizer %s and %s cannot be both set", metav1.FinalizerOrphanDependents, metav1.FinalizerDeleteDependents)))
  257. }
  258. return allErrs
  259. }
  260. // ValidateObjectMetaUpdate validates an object's metadata when updated.
  261. func ValidateObjectMetaUpdate(newMeta, oldMeta *metav1.ObjectMeta, fldPath *field.Path) field.ErrorList {
  262. newMetadata, err := meta.Accessor(newMeta)
  263. if err != nil {
  264. allErrs := field.ErrorList{}
  265. allErrs = append(allErrs, field.Invalid(fldPath, newMeta, err.Error()))
  266. return allErrs
  267. }
  268. oldMetadata, err := meta.Accessor(oldMeta)
  269. if err != nil {
  270. allErrs := field.ErrorList{}
  271. allErrs = append(allErrs, field.Invalid(fldPath, oldMeta, err.Error()))
  272. return allErrs
  273. }
  274. return ValidateObjectMetaAccessorUpdate(newMetadata, oldMetadata, fldPath)
  275. }
  276. // ValidateObjectMetaAccessorUpdate validates an object's metadata when updated.
  277. func ValidateObjectMetaAccessorUpdate(newMeta, oldMeta metav1.Object, fldPath *field.Path) field.ErrorList {
  278. var allErrs field.ErrorList
  279. // Finalizers cannot be added if the object is already being deleted.
  280. if oldMeta.GetDeletionTimestamp() != nil {
  281. allErrs = append(allErrs, ValidateNoNewFinalizers(newMeta.GetFinalizers(), oldMeta.GetFinalizers(), fldPath.Child("finalizers"))...)
  282. }
  283. // Reject updates that don't specify a resource version
  284. if len(newMeta.GetResourceVersion()) == 0 {
  285. allErrs = append(allErrs, field.Invalid(fldPath.Child("resourceVersion"), newMeta.GetResourceVersion(), "must be specified for an update"))
  286. }
  287. // Generation shouldn't be decremented
  288. if newMeta.GetGeneration() < oldMeta.GetGeneration() {
  289. allErrs = append(allErrs, field.Invalid(fldPath.Child("generation"), newMeta.GetGeneration(), "must not be decremented"))
  290. }
  291. allErrs = append(allErrs, ValidateImmutableField(newMeta.GetName(), oldMeta.GetName(), fldPath.Child("name"))...)
  292. allErrs = append(allErrs, ValidateImmutableField(newMeta.GetNamespace(), oldMeta.GetNamespace(), fldPath.Child("namespace"))...)
  293. allErrs = append(allErrs, ValidateImmutableField(newMeta.GetUID(), oldMeta.GetUID(), fldPath.Child("uid"))...)
  294. allErrs = append(allErrs, ValidateImmutableField(newMeta.GetCreationTimestamp(), oldMeta.GetCreationTimestamp(), fldPath.Child("creationTimestamp"))...)
  295. allErrs = append(allErrs, ValidateImmutableField(newMeta.GetDeletionTimestamp(), oldMeta.GetDeletionTimestamp(), fldPath.Child("deletionTimestamp"))...)
  296. allErrs = append(allErrs, ValidateImmutableField(newMeta.GetDeletionGracePeriodSeconds(), oldMeta.GetDeletionGracePeriodSeconds(), fldPath.Child("deletionGracePeriodSeconds"))...)
  297. allErrs = append(allErrs, v1validation.ValidateLabels(newMeta.GetLabels(), fldPath.Child("labels"))...)
  298. allErrs = append(allErrs, ValidateAnnotations(newMeta.GetAnnotations(), fldPath.Child("annotations"))...)
  299. allErrs = append(allErrs, ValidateOwnerReferences(newMeta.GetOwnerReferences(), fldPath.Child("ownerReferences"))...)
  300. allErrs = append(allErrs, v1validation.ValidateManagedFields(newMeta.GetManagedFields(), fldPath.Child("managedFields"))...)
  301. return allErrs
  302. }