update.go 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  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 rest
  14. import (
  15. "fmt"
  16. "k8s.io/kubernetes/pkg/api"
  17. "k8s.io/kubernetes/pkg/api/errors"
  18. "k8s.io/kubernetes/pkg/api/meta"
  19. "k8s.io/kubernetes/pkg/api/validation"
  20. "k8s.io/kubernetes/pkg/runtime"
  21. "k8s.io/kubernetes/pkg/util/validation/field"
  22. )
  23. // RESTUpdateStrategy defines the minimum validation, accepted input, and
  24. // name generation behavior to update an object that follows Kubernetes
  25. // API conventions. A resource may have many UpdateStrategies, depending on
  26. // the call pattern in use.
  27. type RESTUpdateStrategy interface {
  28. runtime.ObjectTyper
  29. // NamespaceScoped returns true if the object must be within a namespace.
  30. NamespaceScoped() bool
  31. // AllowCreateOnUpdate returns true if the object can be created by a PUT.
  32. AllowCreateOnUpdate() bool
  33. // PrepareForUpdate is invoked on update before validation to normalize
  34. // the object. For example: remove fields that are not to be persisted,
  35. // sort order-insensitive list fields, etc. This should not remove fields
  36. // whose presence would be considered a validation error.
  37. PrepareForUpdate(ctx api.Context, obj, old runtime.Object)
  38. // ValidateUpdate is invoked after default fields in the object have been
  39. // filled in before the object is persisted. This method should not mutate
  40. // the object.
  41. ValidateUpdate(ctx api.Context, obj, old runtime.Object) field.ErrorList
  42. // Canonicalize is invoked after validation has succeeded but before the
  43. // object has been persisted. This method may mutate the object.
  44. Canonicalize(obj runtime.Object)
  45. // AllowUnconditionalUpdate returns true if the object can be updated
  46. // unconditionally (irrespective of the latest resource version), when
  47. // there is no resource version specified in the object.
  48. AllowUnconditionalUpdate() bool
  49. }
  50. // TODO: add other common fields that require global validation.
  51. func validateCommonFields(obj, old runtime.Object) (field.ErrorList, error) {
  52. allErrs := field.ErrorList{}
  53. objectMeta, err := api.ObjectMetaFor(obj)
  54. if err != nil {
  55. return nil, fmt.Errorf("failed to get new object metadata: %v", err)
  56. }
  57. oldObjectMeta, err := api.ObjectMetaFor(old)
  58. if err != nil {
  59. return nil, fmt.Errorf("failed to get old object metadata: %v", err)
  60. }
  61. allErrs = append(allErrs, validation.ValidateObjectMetaUpdate(objectMeta, oldObjectMeta, field.NewPath("metadata"))...)
  62. return allErrs, nil
  63. }
  64. // BeforeUpdate ensures that common operations for all resources are performed on update. It only returns
  65. // errors that can be converted to api.Status. It will invoke update validation with the provided existing
  66. // and updated objects.
  67. func BeforeUpdate(strategy RESTUpdateStrategy, ctx api.Context, obj, old runtime.Object) error {
  68. objectMeta, kind, kerr := objectMetaAndKind(strategy, obj)
  69. if kerr != nil {
  70. return kerr
  71. }
  72. if strategy.NamespaceScoped() {
  73. if !api.ValidNamespace(ctx, objectMeta) {
  74. return errors.NewBadRequest("the namespace of the provided object does not match the namespace sent on the request")
  75. }
  76. } else {
  77. objectMeta.Namespace = api.NamespaceNone
  78. }
  79. // Ensure requests cannot update generation
  80. oldMeta, err := api.ObjectMetaFor(old)
  81. if err != nil {
  82. return err
  83. }
  84. objectMeta.Generation = oldMeta.Generation
  85. strategy.PrepareForUpdate(ctx, obj, old)
  86. // ClusterName is ignored and should not be saved
  87. objectMeta.ClusterName = ""
  88. // Ensure some common fields, like UID, are validated for all resources.
  89. errs, err := validateCommonFields(obj, old)
  90. if err != nil {
  91. return errors.NewInternalError(err)
  92. }
  93. errs = append(errs, strategy.ValidateUpdate(ctx, obj, old)...)
  94. if len(errs) > 0 {
  95. return errors.NewInvalid(kind.GroupKind(), objectMeta.Name, errs)
  96. }
  97. strategy.Canonicalize(obj)
  98. return nil
  99. }
  100. // TransformFunc is a function to transform and return newObj
  101. type TransformFunc func(ctx api.Context, newObj runtime.Object, oldObj runtime.Object) (transformedNewObj runtime.Object, err error)
  102. // defaultUpdatedObjectInfo implements UpdatedObjectInfo
  103. type defaultUpdatedObjectInfo struct {
  104. // obj is the updated object
  105. obj runtime.Object
  106. // copier makes a copy of the object before returning it.
  107. // this allows repeated calls to UpdatedObject() to return
  108. // pristine data, even if the returned value is mutated.
  109. copier runtime.ObjectCopier
  110. // transformers is an optional list of transforming functions that modify or
  111. // replace obj using information from the context, old object, or other sources.
  112. transformers []TransformFunc
  113. }
  114. // DefaultUpdatedObjectInfo returns an UpdatedObjectInfo impl based on the specified object.
  115. func DefaultUpdatedObjectInfo(obj runtime.Object, copier runtime.ObjectCopier, transformers ...TransformFunc) UpdatedObjectInfo {
  116. return &defaultUpdatedObjectInfo{obj, copier, transformers}
  117. }
  118. // Preconditions satisfies the UpdatedObjectInfo interface.
  119. func (i *defaultUpdatedObjectInfo) Preconditions() *api.Preconditions {
  120. // Attempt to get the UID out of the object
  121. accessor, err := meta.Accessor(i.obj)
  122. if err != nil {
  123. // If no UID can be read, no preconditions are possible
  124. return nil
  125. }
  126. // If empty, no preconditions needed
  127. uid := accessor.GetUID()
  128. if len(uid) == 0 {
  129. return nil
  130. }
  131. return &api.Preconditions{UID: &uid}
  132. }
  133. // UpdatedObject satisfies the UpdatedObjectInfo interface.
  134. // It returns a copy of the held obj, passed through any configured transformers.
  135. func (i *defaultUpdatedObjectInfo) UpdatedObject(ctx api.Context, oldObj runtime.Object) (runtime.Object, error) {
  136. var err error
  137. // Start with the configured object
  138. newObj := i.obj
  139. // If the original is non-nil (might be nil if the first transformer builds the object from the oldObj), make a copy,
  140. // so we don't return the original. BeforeUpdate can mutate the returned object, doing things like clearing ResourceVersion.
  141. // If we're re-called, we need to be able to return the pristine version.
  142. if newObj != nil {
  143. newObj, err = i.copier.Copy(newObj)
  144. if err != nil {
  145. return nil, err
  146. }
  147. }
  148. // Allow any configured transformers to update the new object
  149. for _, transformer := range i.transformers {
  150. newObj, err = transformer(ctx, newObj, oldObj)
  151. if err != nil {
  152. return nil, err
  153. }
  154. }
  155. return newObj, nil
  156. }
  157. // wrappedUpdatedObjectInfo allows wrapping an existing objInfo and
  158. // chaining additional transformations/checks on the result of UpdatedObject()
  159. type wrappedUpdatedObjectInfo struct {
  160. // obj is the updated object
  161. objInfo UpdatedObjectInfo
  162. // transformers is an optional list of transforming functions that modify or
  163. // replace obj using information from the context, old object, or other sources.
  164. transformers []TransformFunc
  165. }
  166. // WrapUpdatedObjectInfo returns an UpdatedObjectInfo impl that delegates to
  167. // the specified objInfo, then calls the passed transformers
  168. func WrapUpdatedObjectInfo(objInfo UpdatedObjectInfo, transformers ...TransformFunc) UpdatedObjectInfo {
  169. return &wrappedUpdatedObjectInfo{objInfo, transformers}
  170. }
  171. // Preconditions satisfies the UpdatedObjectInfo interface.
  172. func (i *wrappedUpdatedObjectInfo) Preconditions() *api.Preconditions {
  173. return i.objInfo.Preconditions()
  174. }
  175. // UpdatedObject satisfies the UpdatedObjectInfo interface.
  176. // It delegates to the wrapped objInfo and passes the result through any configured transformers.
  177. func (i *wrappedUpdatedObjectInfo) UpdatedObject(ctx api.Context, oldObj runtime.Object) (runtime.Object, error) {
  178. newObj, err := i.objInfo.UpdatedObject(ctx, oldObj)
  179. if err != nil {
  180. return newObj, err
  181. }
  182. // Allow any configured transformers to update the new object or error
  183. for _, transformer := range i.transformers {
  184. newObj, err = transformer(ctx, newObj, oldObj)
  185. if err != nil {
  186. return nil, err
  187. }
  188. }
  189. return newObj, nil
  190. }