k8s.io/kubernetes@v1.29.3/pkg/registry/apps/statefulset/strategy.go (about) 1 /* 2 Copyright 2014 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 package statefulset 18 19 import ( 20 "context" 21 22 apiequality "k8s.io/apimachinery/pkg/api/equality" 23 "k8s.io/apimachinery/pkg/runtime" 24 "k8s.io/apimachinery/pkg/util/validation/field" 25 "k8s.io/apiserver/pkg/registry/rest" 26 "k8s.io/apiserver/pkg/storage/names" 27 utilfeature "k8s.io/apiserver/pkg/util/feature" 28 "k8s.io/kubernetes/pkg/api/legacyscheme" 29 pvcutil "k8s.io/kubernetes/pkg/api/persistentvolumeclaim" 30 "k8s.io/kubernetes/pkg/api/pod" 31 "k8s.io/kubernetes/pkg/apis/apps" 32 "k8s.io/kubernetes/pkg/apis/apps/validation" 33 "k8s.io/kubernetes/pkg/features" 34 "sigs.k8s.io/structured-merge-diff/v4/fieldpath" 35 ) 36 37 // statefulSetStrategy implements verification logic for Replication StatefulSets. 38 type statefulSetStrategy struct { 39 runtime.ObjectTyper 40 names.NameGenerator 41 } 42 43 // Strategy is the default logic that applies when creating and updating Replication StatefulSet objects. 44 var Strategy = statefulSetStrategy{legacyscheme.Scheme, names.SimpleNameGenerator} 45 46 // Make sure we correctly implement the interface. 47 var _ = rest.GarbageCollectionDeleteStrategy(Strategy) 48 49 // DefaultGarbageCollectionPolicy returns DeleteDependents for all currently served versions. 50 func (statefulSetStrategy) DefaultGarbageCollectionPolicy(ctx context.Context) rest.GarbageCollectionPolicy { 51 return rest.DeleteDependents 52 } 53 54 // NamespaceScoped returns true because all StatefulSet' need to be within a namespace. 55 func (statefulSetStrategy) NamespaceScoped() bool { 56 return true 57 } 58 59 // GetResetFields returns the set of fields that get reset by the strategy 60 // and should not be modified by the user. 61 func (statefulSetStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set { 62 fields := map[fieldpath.APIVersion]*fieldpath.Set{ 63 "apps/v1": fieldpath.NewSet( 64 fieldpath.MakePathOrDie("status"), 65 ), 66 } 67 68 return fields 69 } 70 71 // PrepareForCreate clears the status of an StatefulSet before creation. 72 func (statefulSetStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) { 73 statefulSet := obj.(*apps.StatefulSet) 74 // create cannot set status 75 statefulSet.Status = apps.StatefulSetStatus{} 76 77 statefulSet.Generation = 1 78 79 dropStatefulSetDisabledFields(statefulSet, nil) 80 pod.DropDisabledTemplateFields(&statefulSet.Spec.Template, nil) 81 } 82 83 // maxUnavailableInUse returns true if StatefulSet's maxUnavailable set(used) 84 func maxUnavailableInUse(statefulset *apps.StatefulSet) bool { 85 if statefulset == nil { 86 return false 87 } 88 if statefulset.Spec.UpdateStrategy.RollingUpdate == nil { 89 return false 90 } 91 92 return statefulset.Spec.UpdateStrategy.RollingUpdate.MaxUnavailable != nil 93 } 94 95 // PrepareForUpdate clears fields that are not allowed to be set by end users on update. 96 func (statefulSetStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) { 97 newStatefulSet := obj.(*apps.StatefulSet) 98 oldStatefulSet := old.(*apps.StatefulSet) 99 // Update is not allowed to set status 100 newStatefulSet.Status = oldStatefulSet.Status 101 102 dropStatefulSetDisabledFields(newStatefulSet, oldStatefulSet) 103 pod.DropDisabledTemplateFields(&newStatefulSet.Spec.Template, &oldStatefulSet.Spec.Template) 104 105 // Any changes to the spec increment the generation number, any changes to the 106 // status should reflect the generation number of the corresponding object. 107 // See metav1.ObjectMeta description for more information on Generation. 108 if !apiequality.Semantic.DeepEqual(oldStatefulSet.Spec, newStatefulSet.Spec) { 109 newStatefulSet.Generation = oldStatefulSet.Generation + 1 110 } 111 } 112 113 // dropStatefulSetDisabledFields drops fields that are not used if their associated feature gates 114 // are not enabled. 115 // The typical pattern is: 116 // 117 // if !utilfeature.DefaultFeatureGate.Enabled(features.MyFeature) && !myFeatureInUse(oldSvc) { 118 // newSvc.Spec.MyFeature = nil 119 // } 120 func dropStatefulSetDisabledFields(newSS *apps.StatefulSet, oldSS *apps.StatefulSet) { 121 if !utilfeature.DefaultFeatureGate.Enabled(features.StatefulSetAutoDeletePVC) { 122 if oldSS == nil || oldSS.Spec.PersistentVolumeClaimRetentionPolicy == nil { 123 newSS.Spec.PersistentVolumeClaimRetentionPolicy = nil 124 } 125 } 126 if !utilfeature.DefaultFeatureGate.Enabled(features.MaxUnavailableStatefulSet) && !maxUnavailableInUse(oldSS) { 127 if newSS.Spec.UpdateStrategy.RollingUpdate != nil { 128 newSS.Spec.UpdateStrategy.RollingUpdate.MaxUnavailable = nil 129 } 130 } 131 if !utilfeature.DefaultFeatureGate.Enabled(features.StatefulSetStartOrdinal) { 132 if oldSS == nil || oldSS.Spec.Ordinals == nil { 133 // Reset Spec.Ordinals to the default value (nil). 134 newSS.Spec.Ordinals = nil 135 } 136 } 137 } 138 139 // Validate validates a new StatefulSet. 140 func (statefulSetStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList { 141 statefulSet := obj.(*apps.StatefulSet) 142 opts := pod.GetValidationOptionsFromPodTemplate(&statefulSet.Spec.Template, nil) 143 return validation.ValidateStatefulSet(statefulSet, opts) 144 } 145 146 // WarningsOnCreate returns warnings for the creation of the given object. 147 func (statefulSetStrategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string { 148 newStatefulSet := obj.(*apps.StatefulSet) 149 warnings := pod.GetWarningsForPodTemplate(ctx, field.NewPath("spec", "template"), &newStatefulSet.Spec.Template, nil) 150 for i, pvc := range newStatefulSet.Spec.VolumeClaimTemplates { 151 warnings = append(warnings, pvcutil.GetWarningsForPersistentVolumeClaimSpec(field.NewPath("spec", "volumeClaimTemplates").Index(i), pvc.Spec)...) 152 } 153 return warnings 154 } 155 156 // Canonicalize normalizes the object after validation. 157 func (statefulSetStrategy) Canonicalize(obj runtime.Object) { 158 } 159 160 // AllowCreateOnUpdate is false for StatefulSet; this means POST is needed to create one. 161 func (statefulSetStrategy) AllowCreateOnUpdate() bool { 162 return false 163 } 164 165 // ValidateUpdate is the default update validation for an end user. 166 func (statefulSetStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList { 167 newStatefulSet := obj.(*apps.StatefulSet) 168 oldStatefulSet := old.(*apps.StatefulSet) 169 170 opts := pod.GetValidationOptionsFromPodTemplate(&newStatefulSet.Spec.Template, &oldStatefulSet.Spec.Template) 171 return validation.ValidateStatefulSetUpdate(newStatefulSet, oldStatefulSet, opts) 172 } 173 174 // WarningsOnUpdate returns warnings for the given update. 175 func (statefulSetStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string { 176 var warnings []string 177 newStatefulSet := obj.(*apps.StatefulSet) 178 oldStatefulSet := old.(*apps.StatefulSet) 179 if newStatefulSet.Generation != oldStatefulSet.Generation { 180 warnings = pod.GetWarningsForPodTemplate(ctx, field.NewPath("spec", "template"), &newStatefulSet.Spec.Template, &oldStatefulSet.Spec.Template) 181 } 182 for i, pvc := range newStatefulSet.Spec.VolumeClaimTemplates { 183 warnings = append(warnings, pvcutil.GetWarningsForPersistentVolumeClaimSpec(field.NewPath("spec", "volumeClaimTemplates").Index(i).Child("Spec"), pvc.Spec)...) 184 } 185 186 return warnings 187 } 188 189 // AllowUnconditionalUpdate is the default update policy for StatefulSet objects. 190 func (statefulSetStrategy) AllowUnconditionalUpdate() bool { 191 return true 192 } 193 194 type statefulSetStatusStrategy struct { 195 statefulSetStrategy 196 } 197 198 // StatusStrategy is the default logic invoked when updating object status. 199 var StatusStrategy = statefulSetStatusStrategy{Strategy} 200 201 // GetResetFields returns the set of fields that get reset by the strategy 202 // and should not be modified by the user. 203 func (statefulSetStatusStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set { 204 return map[fieldpath.APIVersion]*fieldpath.Set{ 205 "apps/v1": fieldpath.NewSet( 206 fieldpath.MakePathOrDie("spec"), 207 ), 208 } 209 } 210 211 // PrepareForUpdate clears fields that are not allowed to be set by end users on update of status 212 func (statefulSetStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) { 213 newStatefulSet := obj.(*apps.StatefulSet) 214 oldStatefulSet := old.(*apps.StatefulSet) 215 // status changes are not allowed to update spec 216 newStatefulSet.Spec = oldStatefulSet.Spec 217 } 218 219 // ValidateUpdate is the default update validation for an end user updating status 220 func (statefulSetStatusStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList { 221 // TODO: Validate status updates. 222 return validation.ValidateStatefulSetStatusUpdate(obj.(*apps.StatefulSet), old.(*apps.StatefulSet)) 223 } 224 225 // WarningsOnUpdate returns warnings for the given update. 226 func (statefulSetStatusStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string { 227 return nil 228 }