k8s.io/kubernetes@v1.29.3/pkg/registry/apps/replicaset/strategy.go (about) 1 /* 2 Copyright 2016 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 // If you make changes to this file, you should also make the corresponding change in ReplicationController. 18 19 package replicaset 20 21 import ( 22 "context" 23 "fmt" 24 "strconv" 25 26 extensionsv1beta1 "k8s.io/api/extensions/v1beta1" 27 apiequality "k8s.io/apimachinery/pkg/api/equality" 28 apivalidation "k8s.io/apimachinery/pkg/api/validation" 29 "k8s.io/apimachinery/pkg/fields" 30 "k8s.io/apimachinery/pkg/labels" 31 "k8s.io/apimachinery/pkg/runtime" 32 "k8s.io/apimachinery/pkg/runtime/schema" 33 utilvalidation "k8s.io/apimachinery/pkg/util/validation" 34 "k8s.io/apimachinery/pkg/util/validation/field" 35 genericapirequest "k8s.io/apiserver/pkg/endpoints/request" 36 "k8s.io/apiserver/pkg/registry/generic" 37 "k8s.io/apiserver/pkg/registry/rest" 38 apistorage "k8s.io/apiserver/pkg/storage" 39 "k8s.io/apiserver/pkg/storage/names" 40 "k8s.io/kubernetes/pkg/api/legacyscheme" 41 "k8s.io/kubernetes/pkg/api/pod" 42 "k8s.io/kubernetes/pkg/apis/apps" 43 appsvalidation "k8s.io/kubernetes/pkg/apis/apps/validation" 44 "sigs.k8s.io/structured-merge-diff/v4/fieldpath" 45 ) 46 47 // rsStrategy implements verification logic for ReplicaSets. 48 type rsStrategy struct { 49 runtime.ObjectTyper 50 names.NameGenerator 51 } 52 53 // Strategy is the default logic that applies when creating and updating ReplicaSet objects. 54 var Strategy = rsStrategy{legacyscheme.Scheme, names.SimpleNameGenerator} 55 56 // Make sure we correctly implement the interface. 57 var _ = rest.GarbageCollectionDeleteStrategy(Strategy) 58 59 // DefaultGarbageCollectionPolicy returns DeleteDependents for all currently served versions. 60 func (rsStrategy) DefaultGarbageCollectionPolicy(ctx context.Context) rest.GarbageCollectionPolicy { 61 return rest.DeleteDependents 62 } 63 64 // NamespaceScoped returns true because all ReplicaSets need to be within a namespace. 65 func (rsStrategy) NamespaceScoped() bool { 66 return true 67 } 68 69 // GetResetFields returns the set of fields that get reset by the strategy 70 // and should not be modified by the user. 71 func (rsStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set { 72 fields := map[fieldpath.APIVersion]*fieldpath.Set{ 73 "apps/v1": fieldpath.NewSet( 74 fieldpath.MakePathOrDie("status"), 75 ), 76 } 77 78 return fields 79 } 80 81 // PrepareForCreate clears the status of a ReplicaSet before creation. 82 func (rsStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) { 83 rs := obj.(*apps.ReplicaSet) 84 rs.Status = apps.ReplicaSetStatus{} 85 86 rs.Generation = 1 87 88 pod.DropDisabledTemplateFields(&rs.Spec.Template, nil) 89 } 90 91 // PrepareForUpdate clears fields that are not allowed to be set by end users on update. 92 func (rsStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) { 93 newRS := obj.(*apps.ReplicaSet) 94 oldRS := old.(*apps.ReplicaSet) 95 // update is not allowed to set status 96 newRS.Status = oldRS.Status 97 98 pod.DropDisabledTemplateFields(&newRS.Spec.Template, &oldRS.Spec.Template) 99 100 // Any changes to the spec increment the generation number, any changes to the 101 // status should reflect the generation number of the corresponding object. We push 102 // the burden of managing the status onto the clients because we can't (in general) 103 // know here what version of spec the writer of the status has seen. It may seem like 104 // we can at first -- since obj contains spec -- but in the future we will probably make 105 // status its own object, and even if we don't, writes may be the result of a 106 // read-update-write loop, so the contents of spec may not actually be the spec that 107 // the ReplicaSet has *seen*. 108 if !apiequality.Semantic.DeepEqual(oldRS.Spec, newRS.Spec) { 109 newRS.Generation = oldRS.Generation + 1 110 } 111 } 112 113 // Validate validates a new ReplicaSet. 114 func (rsStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList { 115 rs := obj.(*apps.ReplicaSet) 116 opts := pod.GetValidationOptionsFromPodTemplate(&rs.Spec.Template, nil) 117 return appsvalidation.ValidateReplicaSet(rs, opts) 118 } 119 120 // WarningsOnCreate returns warnings for the creation of the given object. 121 func (rsStrategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string { 122 newRS := obj.(*apps.ReplicaSet) 123 var warnings []string 124 if msgs := utilvalidation.IsDNS1123Label(newRS.Name); len(msgs) != 0 { 125 warnings = append(warnings, fmt.Sprintf("metadata.name: this is used in Pod names and hostnames, which can result in surprising behavior; a DNS label is recommended: %v", msgs)) 126 } 127 warnings = append(warnings, pod.GetWarningsForPodTemplate(ctx, field.NewPath("spec", "template"), &newRS.Spec.Template, nil)...) 128 return warnings 129 } 130 131 // Canonicalize normalizes the object after validation. 132 func (rsStrategy) Canonicalize(obj runtime.Object) { 133 } 134 135 // AllowCreateOnUpdate is false for ReplicaSets; this means a POST is 136 // needed to create one. 137 func (rsStrategy) AllowCreateOnUpdate() bool { 138 return false 139 } 140 141 // ValidateUpdate is the default update validation for an end user. 142 func (rsStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList { 143 newReplicaSet := obj.(*apps.ReplicaSet) 144 oldReplicaSet := old.(*apps.ReplicaSet) 145 146 opts := pod.GetValidationOptionsFromPodTemplate(&newReplicaSet.Spec.Template, &oldReplicaSet.Spec.Template) 147 allErrs := appsvalidation.ValidateReplicaSet(obj.(*apps.ReplicaSet), opts) 148 allErrs = append(allErrs, appsvalidation.ValidateReplicaSetUpdate(newReplicaSet, oldReplicaSet, opts)...) 149 150 // Update is not allowed to set Spec.Selector for all groups/versions except extensions/v1beta1. 151 // If RequestInfo is nil, it is better to revert to old behavior (i.e. allow update to set Spec.Selector) 152 // to prevent unintentionally breaking users who may rely on the old behavior. 153 // TODO(#50791): after extensions/v1beta1 is removed, move selector immutability check inside ValidateReplicaSetUpdate(). 154 if requestInfo, found := genericapirequest.RequestInfoFrom(ctx); found { 155 groupVersion := schema.GroupVersion{Group: requestInfo.APIGroup, Version: requestInfo.APIVersion} 156 switch groupVersion { 157 case extensionsv1beta1.SchemeGroupVersion: 158 // no-op for compatibility 159 default: 160 // disallow mutation of selector 161 allErrs = append(allErrs, apivalidation.ValidateImmutableField(newReplicaSet.Spec.Selector, oldReplicaSet.Spec.Selector, field.NewPath("spec").Child("selector"))...) 162 } 163 } 164 165 return allErrs 166 } 167 168 // WarningsOnUpdate returns warnings for the given update. 169 func (rsStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string { 170 var warnings []string 171 newReplicaSet := obj.(*apps.ReplicaSet) 172 oldReplicaSet := old.(*apps.ReplicaSet) 173 if newReplicaSet.Generation != oldReplicaSet.Generation { 174 warnings = pod.GetWarningsForPodTemplate(ctx, field.NewPath("spec", "template"), &newReplicaSet.Spec.Template, &oldReplicaSet.Spec.Template) 175 } 176 return warnings 177 } 178 179 func (rsStrategy) AllowUnconditionalUpdate() bool { 180 return true 181 } 182 183 // ToSelectableFields returns a field set that represents the object. 184 func ToSelectableFields(rs *apps.ReplicaSet) fields.Set { 185 objectMetaFieldsSet := generic.ObjectMetaFieldsSet(&rs.ObjectMeta, true) 186 rsSpecificFieldsSet := fields.Set{ 187 "status.replicas": strconv.Itoa(int(rs.Status.Replicas)), 188 } 189 return generic.MergeFieldsSets(objectMetaFieldsSet, rsSpecificFieldsSet) 190 } 191 192 // GetAttrs returns labels and fields of a given object for filtering purposes. 193 func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) { 194 rs, ok := obj.(*apps.ReplicaSet) 195 if !ok { 196 return nil, nil, fmt.Errorf("given object is not a ReplicaSet") 197 } 198 return labels.Set(rs.ObjectMeta.Labels), ToSelectableFields(rs), nil 199 } 200 201 // MatchReplicaSet is the filter used by the generic etcd backend to route 202 // watch events from etcd to clients of the apiserver only interested in specific 203 // labels/fields. 204 func MatchReplicaSet(label labels.Selector, field fields.Selector) apistorage.SelectionPredicate { 205 return apistorage.SelectionPredicate{ 206 Label: label, 207 Field: field, 208 GetAttrs: GetAttrs, 209 } 210 } 211 212 type rsStatusStrategy struct { 213 rsStrategy 214 } 215 216 // StatusStrategy is the default logic invoked when updating object status. 217 var StatusStrategy = rsStatusStrategy{Strategy} 218 219 // GetResetFields returns the set of fields that get reset by the strategy 220 // and should not be modified by the user. 221 func (rsStatusStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set { 222 return map[fieldpath.APIVersion]*fieldpath.Set{ 223 "apps/v1": fieldpath.NewSet( 224 fieldpath.MakePathOrDie("spec"), 225 ), 226 } 227 } 228 229 func (rsStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) { 230 newRS := obj.(*apps.ReplicaSet) 231 oldRS := old.(*apps.ReplicaSet) 232 // update is not allowed to set spec 233 newRS.Spec = oldRS.Spec 234 } 235 236 func (rsStatusStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList { 237 return appsvalidation.ValidateReplicaSetStatusUpdate(obj.(*apps.ReplicaSet), old.(*apps.ReplicaSet)) 238 } 239 240 // WarningsOnUpdate returns warnings for the given update. 241 func (rsStatusStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string { 242 return nil 243 }