strategy.go 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. /*
  2. Copyright 2016 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. // If you make changes to this file, you should also make the corresponding change in ReplicationController.
  14. package replicaset
  15. import (
  16. "context"
  17. "fmt"
  18. "strconv"
  19. appsv1beta2 "k8s.io/api/apps/v1beta2"
  20. extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
  21. apiequality "k8s.io/apimachinery/pkg/api/equality"
  22. apivalidation "k8s.io/apimachinery/pkg/api/validation"
  23. "k8s.io/apimachinery/pkg/fields"
  24. "k8s.io/apimachinery/pkg/labels"
  25. "k8s.io/apimachinery/pkg/runtime"
  26. "k8s.io/apimachinery/pkg/runtime/schema"
  27. "k8s.io/apimachinery/pkg/util/validation/field"
  28. genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
  29. "k8s.io/apiserver/pkg/registry/generic"
  30. "k8s.io/apiserver/pkg/registry/rest"
  31. apistorage "k8s.io/apiserver/pkg/storage"
  32. "k8s.io/apiserver/pkg/storage/names"
  33. "k8s.io/kubernetes/pkg/api/legacyscheme"
  34. "k8s.io/kubernetes/pkg/api/pod"
  35. "k8s.io/kubernetes/pkg/apis/apps"
  36. "k8s.io/kubernetes/pkg/apis/apps/validation"
  37. corevalidation "k8s.io/kubernetes/pkg/apis/core/validation"
  38. )
  39. // rsStrategy implements verification logic for ReplicaSets.
  40. type rsStrategy struct {
  41. runtime.ObjectTyper
  42. names.NameGenerator
  43. }
  44. // Strategy is the default logic that applies when creating and updating ReplicaSet objects.
  45. var Strategy = rsStrategy{legacyscheme.Scheme, names.SimpleNameGenerator}
  46. // DefaultGarbageCollectionPolicy returns OrphanDependents for extensions/v1beta1 and apps/v1beta2 for backwards compatibility,
  47. // and DeleteDependents for all other versions.
  48. func (rsStrategy) DefaultGarbageCollectionPolicy(ctx context.Context) rest.GarbageCollectionPolicy {
  49. var groupVersion schema.GroupVersion
  50. if requestInfo, found := genericapirequest.RequestInfoFrom(ctx); found {
  51. groupVersion = schema.GroupVersion{Group: requestInfo.APIGroup, Version: requestInfo.APIVersion}
  52. }
  53. switch groupVersion {
  54. case extensionsv1beta1.SchemeGroupVersion, appsv1beta2.SchemeGroupVersion:
  55. // for back compatibility
  56. return rest.OrphanDependents
  57. default:
  58. return rest.DeleteDependents
  59. }
  60. }
  61. // NamespaceScoped returns true because all ReplicaSets need to be within a namespace.
  62. func (rsStrategy) NamespaceScoped() bool {
  63. return true
  64. }
  65. // PrepareForCreate clears the status of a ReplicaSet before creation.
  66. func (rsStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
  67. rs := obj.(*apps.ReplicaSet)
  68. rs.Status = apps.ReplicaSetStatus{}
  69. rs.Generation = 1
  70. pod.DropDisabledTemplateFields(&rs.Spec.Template, nil)
  71. }
  72. // PrepareForUpdate clears fields that are not allowed to be set by end users on update.
  73. func (rsStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
  74. newRS := obj.(*apps.ReplicaSet)
  75. oldRS := old.(*apps.ReplicaSet)
  76. // update is not allowed to set status
  77. newRS.Status = oldRS.Status
  78. pod.DropDisabledTemplateFields(&newRS.Spec.Template, &oldRS.Spec.Template)
  79. // Any changes to the spec increment the generation number, any changes to the
  80. // status should reflect the generation number of the corresponding object. We push
  81. // the burden of managing the status onto the clients because we can't (in general)
  82. // know here what version of spec the writer of the status has seen. It may seem like
  83. // we can at first -- since obj contains spec -- but in the future we will probably make
  84. // status its own object, and even if we don't, writes may be the result of a
  85. // read-update-write loop, so the contents of spec may not actually be the spec that
  86. // the ReplicaSet has *seen*.
  87. if !apiequality.Semantic.DeepEqual(oldRS.Spec, newRS.Spec) {
  88. newRS.Generation = oldRS.Generation + 1
  89. }
  90. }
  91. // Validate validates a new ReplicaSet.
  92. func (rsStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
  93. rs := obj.(*apps.ReplicaSet)
  94. allErrs := validation.ValidateReplicaSet(rs)
  95. allErrs = append(allErrs, corevalidation.ValidateConditionalPodTemplate(&rs.Spec.Template, nil, field.NewPath("spec.template"))...)
  96. return allErrs
  97. }
  98. // Canonicalize normalizes the object after validation.
  99. func (rsStrategy) Canonicalize(obj runtime.Object) {
  100. }
  101. // AllowCreateOnUpdate is false for ReplicaSets; this means a POST is
  102. // needed to create one.
  103. func (rsStrategy) AllowCreateOnUpdate() bool {
  104. return false
  105. }
  106. // ValidateUpdate is the default update validation for an end user.
  107. func (rsStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
  108. newReplicaSet := obj.(*apps.ReplicaSet)
  109. oldReplicaSet := old.(*apps.ReplicaSet)
  110. allErrs := validation.ValidateReplicaSet(obj.(*apps.ReplicaSet))
  111. allErrs = append(allErrs, validation.ValidateReplicaSetUpdate(newReplicaSet, oldReplicaSet)...)
  112. allErrs = append(allErrs, corevalidation.ValidateConditionalPodTemplate(&newReplicaSet.Spec.Template, &oldReplicaSet.Spec.Template, field.NewPath("spec.template"))...)
  113. // Update is not allowed to set Spec.Selector for all groups/versions except extensions/v1beta1.
  114. // If RequestInfo is nil, it is better to revert to old behavior (i.e. allow update to set Spec.Selector)
  115. // to prevent unintentionally breaking users who may rely on the old behavior.
  116. // TODO(#50791): after extensions/v1beta1 is removed, move selector immutability check inside ValidateReplicaSetUpdate().
  117. if requestInfo, found := genericapirequest.RequestInfoFrom(ctx); found {
  118. groupVersion := schema.GroupVersion{Group: requestInfo.APIGroup, Version: requestInfo.APIVersion}
  119. switch groupVersion {
  120. case extensionsv1beta1.SchemeGroupVersion:
  121. // no-op for compatibility
  122. default:
  123. // disallow mutation of selector
  124. allErrs = append(allErrs, apivalidation.ValidateImmutableField(newReplicaSet.Spec.Selector, oldReplicaSet.Spec.Selector, field.NewPath("spec").Child("selector"))...)
  125. }
  126. }
  127. return allErrs
  128. }
  129. func (rsStrategy) AllowUnconditionalUpdate() bool {
  130. return true
  131. }
  132. // ToSelectableFields returns a field set that represents the object.
  133. func ToSelectableFields(rs *apps.ReplicaSet) fields.Set {
  134. objectMetaFieldsSet := generic.ObjectMetaFieldsSet(&rs.ObjectMeta, true)
  135. rsSpecificFieldsSet := fields.Set{
  136. "status.replicas": strconv.Itoa(int(rs.Status.Replicas)),
  137. }
  138. return generic.MergeFieldsSets(objectMetaFieldsSet, rsSpecificFieldsSet)
  139. }
  140. // GetAttrs returns labels and fields of a given object for filtering purposes.
  141. func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
  142. rs, ok := obj.(*apps.ReplicaSet)
  143. if !ok {
  144. return nil, nil, fmt.Errorf("given object is not a ReplicaSet")
  145. }
  146. return labels.Set(rs.ObjectMeta.Labels), ToSelectableFields(rs), nil
  147. }
  148. // MatchReplicaSet is the filter used by the generic etcd backend to route
  149. // watch events from etcd to clients of the apiserver only interested in specific
  150. // labels/fields.
  151. func MatchReplicaSet(label labels.Selector, field fields.Selector) apistorage.SelectionPredicate {
  152. return apistorage.SelectionPredicate{
  153. Label: label,
  154. Field: field,
  155. GetAttrs: GetAttrs,
  156. }
  157. }
  158. type rsStatusStrategy struct {
  159. rsStrategy
  160. }
  161. // StatusStrategy is the default logic invoked when updating object status.
  162. var StatusStrategy = rsStatusStrategy{Strategy}
  163. func (rsStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
  164. newRS := obj.(*apps.ReplicaSet)
  165. oldRS := old.(*apps.ReplicaSet)
  166. // update is not allowed to set spec
  167. newRS.Spec = oldRS.Spec
  168. }
  169. func (rsStatusStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
  170. return validation.ValidateReplicaSetStatusUpdate(obj.(*apps.ReplicaSet), old.(*apps.ReplicaSet))
  171. }