k8s.io/kubernetes@v1.29.3/pkg/registry/flowcontrol/prioritylevelconfiguration/strategy.go (about) 1 /* 2 Copyright 2019 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 prioritylevelconfiguration 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/runtime/schema" 25 "k8s.io/apimachinery/pkg/util/validation/field" 26 genericapirequest "k8s.io/apiserver/pkg/endpoints/request" 27 "k8s.io/apiserver/pkg/features" 28 "k8s.io/apiserver/pkg/storage/names" 29 utilfeature "k8s.io/apiserver/pkg/util/feature" 30 "k8s.io/kubernetes/pkg/api/legacyscheme" 31 "k8s.io/kubernetes/pkg/apis/flowcontrol" 32 "k8s.io/kubernetes/pkg/apis/flowcontrol/validation" 33 "sigs.k8s.io/structured-merge-diff/v4/fieldpath" 34 ) 35 36 // priorityLevelConfigurationStrategy implements verification logic for priority level configurations. 37 type priorityLevelConfigurationStrategy struct { 38 runtime.ObjectTyper 39 names.NameGenerator 40 } 41 42 // Strategy is the default logic that applies when creating and updating priority level configuration objects. 43 var Strategy = priorityLevelConfigurationStrategy{legacyscheme.Scheme, names.SimpleNameGenerator} 44 45 // NamespaceScoped returns false because all PriorityClasses are global. 46 func (priorityLevelConfigurationStrategy) NamespaceScoped() bool { 47 return false 48 } 49 50 // GetResetFields returns the set of fields that get reset by the strategy 51 // and should not be modified by the user. 52 func (priorityLevelConfigurationStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set { 53 fields := map[fieldpath.APIVersion]*fieldpath.Set{ 54 "flowcontrol.apiserver.k8s.io/v1beta1": fieldpath.NewSet( 55 fieldpath.MakePathOrDie("status"), 56 ), 57 "flowcontrol.apiserver.k8s.io/v1beta2": fieldpath.NewSet( 58 fieldpath.MakePathOrDie("status"), 59 ), 60 "flowcontrol.apiserver.k8s.io/v1beta3": fieldpath.NewSet( 61 fieldpath.MakePathOrDie("status"), 62 ), 63 "flowcontrol.apiserver.k8s.io/v1": fieldpath.NewSet( 64 fieldpath.MakePathOrDie("status"), 65 ), 66 } 67 68 return fields 69 } 70 71 // PrepareForCreate clears the status of a priority-level-configuration before creation. 72 func (priorityLevelConfigurationStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) { 73 pl := obj.(*flowcontrol.PriorityLevelConfiguration) 74 pl.Status = flowcontrol.PriorityLevelConfigurationStatus{} 75 pl.Generation = 1 76 } 77 78 // PrepareForUpdate clears fields that are not allowed to be set by end users on update. 79 func (priorityLevelConfigurationStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) { 80 newPriorityLevelConfiguration := obj.(*flowcontrol.PriorityLevelConfiguration) 81 oldPriorityLevelConfiguration := old.(*flowcontrol.PriorityLevelConfiguration) 82 83 // Spec updates bump the generation so that we can distinguish between status updates. 84 if !apiequality.Semantic.DeepEqual(newPriorityLevelConfiguration.Spec, oldPriorityLevelConfiguration.Spec) { 85 newPriorityLevelConfiguration.Generation = oldPriorityLevelConfiguration.Generation + 1 86 } 87 newPriorityLevelConfiguration.Status = oldPriorityLevelConfiguration.Status 88 } 89 90 // Validate validates a new priority-level. 91 func (priorityLevelConfigurationStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList { 92 // 1.28 server is not aware of the roundtrip annotation, and will 93 // default any 0 value persisted (for the NominalConcurrencyShares 94 // field of a priority level configuration object) back to 30 when 95 // reading from etcd. 96 // That means we should not allow 0 values to be introduced, either 97 // via v1 or v1beta3(with the roundtrip annotation) until we know 98 // all servers are at 1.29+ and will honor the zero value correctly. 99 // 100 // TODO(121510): 1.29: don't allow a zero value, either via v1 or 101 // v1beta3 (with the roundtrip annotation) for the 102 // 'nominalConcurrencyShares' field of 'limited' for CREATE operation. 103 // 1:30: lift this restriction, allow zero value via v1 or v1beta3 104 opts := validation.PriorityLevelValidationOptions{ 105 AllowZeroLimitedNominalConcurrencyShares: utilfeature.DefaultFeatureGate.Enabled(features.ZeroLimitedNominalConcurrencyShares), 106 } 107 return validation.ValidatePriorityLevelConfiguration(obj.(*flowcontrol.PriorityLevelConfiguration), getRequestGroupVersion(ctx), opts) 108 } 109 110 // WarningsOnCreate returns warnings for the creation of the given object. 111 func (priorityLevelConfigurationStrategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string { 112 return nil 113 } 114 115 // Canonicalize normalizes the object after validation. 116 func (priorityLevelConfigurationStrategy) Canonicalize(obj runtime.Object) { 117 } 118 119 func (priorityLevelConfigurationStrategy) AllowUnconditionalUpdate() bool { 120 return true 121 } 122 123 // AllowCreateOnUpdate is false for priority-level-configurations; this means a POST is needed to create one. 124 func (priorityLevelConfigurationStrategy) AllowCreateOnUpdate() bool { 125 return false 126 } 127 128 // ValidateUpdate is the default update validation for an end user. 129 func (priorityLevelConfigurationStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList { 130 newPL := obj.(*flowcontrol.PriorityLevelConfiguration) 131 oldPL := old.(*flowcontrol.PriorityLevelConfiguration) 132 133 // 1.28 server is not aware of the roundtrip annotation, and will 134 // default any 0 value persisted (for the NominalConcurrencyShares 135 // field of a priority level configuration object) back to 30 when 136 // reading from etcd. 137 // That means we should not allow 0 values to be introduced, either 138 // via v1 or v1beta3(with the roundtrip annotation) until we know 139 // all servers are at 1.29+ and will honor the zero value correctly. 140 // 141 // TODO(121510): 1.29: only allow a zero value, either via v1 or 142 // v1beta3 (with the roundtrip annotation) for the 143 // 'nominalConcurrencyShares' field of 'limited' for UPDATE operation, 144 // only if the existing object already contains a zero value. 145 // 1:30: lift this restriction, allow zero value via v1 or v1beta3 146 opts := validation.PriorityLevelValidationOptions{ 147 AllowZeroLimitedNominalConcurrencyShares: utilfeature.DefaultFeatureGate.Enabled(features.ZeroLimitedNominalConcurrencyShares) || 148 hasZeroLimitedNominalConcurrencyShares(oldPL), 149 } 150 return validation.ValidatePriorityLevelConfiguration(newPL, getRequestGroupVersion(ctx), opts) 151 } 152 153 // WarningsOnUpdate returns warnings for the given update. 154 func (priorityLevelConfigurationStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string { 155 return nil 156 } 157 158 type priorityLevelConfigurationStatusStrategy struct { 159 priorityLevelConfigurationStrategy 160 } 161 162 // StatusStrategy is the default logic that applies when updating priority level configuration objects' status. 163 var StatusStrategy = priorityLevelConfigurationStatusStrategy{Strategy} 164 165 // GetResetFields returns the set of fields that get reset by the strategy 166 // and should not be modified by the user. 167 func (priorityLevelConfigurationStatusStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set { 168 fields := map[fieldpath.APIVersion]*fieldpath.Set{ 169 "flowcontrol.apiserver.k8s.io/v1beta1": fieldpath.NewSet( 170 fieldpath.MakePathOrDie("spec"), 171 fieldpath.MakePathOrDie("metadata"), 172 ), 173 "flowcontrol.apiserver.k8s.io/v1beta2": fieldpath.NewSet( 174 fieldpath.MakePathOrDie("spec"), 175 fieldpath.MakePathOrDie("metadata"), 176 ), 177 "flowcontrol.apiserver.k8s.io/v1beta3": fieldpath.NewSet( 178 fieldpath.MakePathOrDie("spec"), 179 fieldpath.MakePathOrDie("metadata"), 180 ), 181 "flowcontrol.apiserver.k8s.io/v1": fieldpath.NewSet( 182 fieldpath.MakePathOrDie("spec"), 183 fieldpath.MakePathOrDie("metadata"), 184 ), 185 } 186 187 return fields 188 } 189 190 func (priorityLevelConfigurationStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) { 191 newPriorityLevelConfiguration := obj.(*flowcontrol.PriorityLevelConfiguration) 192 oldPriorityLevelConfiguration := old.(*flowcontrol.PriorityLevelConfiguration) 193 194 // managedFields must be preserved since it's been modified to 195 // track changed fields in the status update. 196 managedFields := newPriorityLevelConfiguration.ManagedFields 197 newPriorityLevelConfiguration.ObjectMeta = oldPriorityLevelConfiguration.ObjectMeta 198 newPriorityLevelConfiguration.ManagedFields = managedFields 199 newPriorityLevelConfiguration.Spec = oldPriorityLevelConfiguration.Spec 200 } 201 202 func (priorityLevelConfigurationStatusStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList { 203 return validation.ValidatePriorityLevelConfigurationStatusUpdate(old.(*flowcontrol.PriorityLevelConfiguration), obj.(*flowcontrol.PriorityLevelConfiguration)) 204 } 205 206 // WarningsOnUpdate returns warnings for the given update. 207 func (priorityLevelConfigurationStatusStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string { 208 return nil 209 } 210 211 func getRequestGroupVersion(ctx context.Context) schema.GroupVersion { 212 if requestInfo, exists := genericapirequest.RequestInfoFrom(ctx); exists { 213 return schema.GroupVersion{Group: requestInfo.APIGroup, Version: requestInfo.APIVersion} 214 } 215 return schema.GroupVersion{} 216 } 217 218 func hasZeroLimitedNominalConcurrencyShares(obj *flowcontrol.PriorityLevelConfiguration) bool { 219 return obj != nil && obj.Spec.Limited != nil && obj.Spec.Limited.NominalConcurrencyShares == 0 220 }