k8s.io/kubernetes@v1.29.3/pkg/registry/flowcontrol/prioritylevelconfiguration/strategy_test.go (about) 1 /* 2 Copyright 2023 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 "testing" 22 23 flowcontrolv1 "k8s.io/api/flowcontrol/v1" 24 flowcontrolv1beta3 "k8s.io/api/flowcontrol/v1beta3" 25 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 "k8s.io/apimachinery/pkg/runtime" 27 "k8s.io/apimachinery/pkg/util/validation/field" 28 "k8s.io/apiserver/pkg/features" 29 utilfeature "k8s.io/apiserver/pkg/util/feature" 30 featuregatetesting "k8s.io/component-base/featuregate/testing" 31 "k8s.io/kubernetes/pkg/apis/flowcontrol" 32 "k8s.io/utils/ptr" 33 34 "github.com/google/go-cmp/cmp" 35 ) 36 37 func TestPriorityLevelConfigurationValidation(t *testing.T) { 38 v1ObjFn := func(v *int32) *flowcontrolv1.PriorityLevelConfiguration { 39 return &flowcontrolv1.PriorityLevelConfiguration{ 40 ObjectMeta: metav1.ObjectMeta{ 41 Name: "foo", 42 }, 43 Spec: flowcontrolv1.PriorityLevelConfigurationSpec{ 44 Type: flowcontrolv1.PriorityLevelEnablementLimited, 45 Limited: &flowcontrolv1.LimitedPriorityLevelConfiguration{ 46 NominalConcurrencyShares: v, 47 LimitResponse: flowcontrolv1.LimitResponse{ 48 Type: flowcontrolv1.LimitResponseTypeReject}, 49 }, 50 }, 51 } 52 } 53 v1beta3ObjFn := func(v int32, isZero bool) *flowcontrolv1beta3.PriorityLevelConfiguration { 54 obj := &flowcontrolv1beta3.PriorityLevelConfiguration{ 55 ObjectMeta: metav1.ObjectMeta{ 56 Name: "foo", 57 }, 58 Spec: flowcontrolv1beta3.PriorityLevelConfigurationSpec{ 59 Type: flowcontrolv1beta3.PriorityLevelEnablementLimited, 60 Limited: &flowcontrolv1beta3.LimitedPriorityLevelConfiguration{ 61 NominalConcurrencyShares: v, 62 LimitResponse: flowcontrolv1beta3.LimitResponse{ 63 Type: flowcontrolv1beta3.LimitResponseTypeReject}, 64 }, 65 }, 66 } 67 if isZero && v == 0 { 68 obj.ObjectMeta.Annotations = map[string]string{} 69 obj.ObjectMeta.Annotations[flowcontrolv1beta3.PriorityLevelPreserveZeroConcurrencySharesKey] = "" 70 } 71 return obj 72 } 73 internalObjFn := func(v int32) *flowcontrol.PriorityLevelConfiguration { 74 return &flowcontrol.PriorityLevelConfiguration{ 75 ObjectMeta: metav1.ObjectMeta{ 76 Name: "foo", 77 }, 78 Spec: flowcontrol.PriorityLevelConfigurationSpec{ 79 Type: flowcontrol.PriorityLevelEnablementLimited, 80 Limited: &flowcontrol.LimitedPriorityLevelConfiguration{ 81 NominalConcurrencyShares: v, 82 LimitResponse: flowcontrol.LimitResponse{ 83 Type: flowcontrol.LimitResponseTypeReject}, 84 }, 85 }, 86 } 87 } 88 v1SchemeFn := func(t *testing.T) *runtime.Scheme { 89 scheme := runtime.NewScheme() 90 if err := flowcontrolv1.AddToScheme(scheme); err != nil { 91 t.Fatalf("Failed to add to scheme: %v", err) 92 } 93 return scheme 94 } 95 v1beta3SchemeFn := func(t *testing.T) *runtime.Scheme { 96 scheme := runtime.NewScheme() 97 if err := flowcontrolv1beta3.AddToScheme(scheme); err != nil { 98 t.Fatalf("Failed to add to scheme: %v", err) 99 } 100 return scheme 101 } 102 errExpectedFn := func(v int32, msg string) field.ErrorList { 103 return field.ErrorList{ 104 field.Invalid(field.NewPath("spec").Child("limited").Child("nominalConcurrencyShares"), int32(v), msg), 105 } 106 } 107 108 tests := []struct { 109 name string 110 obj runtime.Object 111 old *flowcontrol.PriorityLevelConfiguration // for UPDATE only 112 zeroFeatureEnabled bool 113 scheme *runtime.Scheme 114 errExpected field.ErrorList 115 }{ 116 { 117 name: "v1, feature disabled, create, zero value, error expected", 118 obj: v1ObjFn(ptr.To(int32(0))), 119 zeroFeatureEnabled: false, 120 scheme: v1SchemeFn(t), 121 errExpected: errExpectedFn(0, "must be positive"), 122 }, 123 { 124 name: "v1, feature disabled, create, unset, no error expected", 125 obj: v1ObjFn(nil), 126 zeroFeatureEnabled: false, 127 scheme: v1SchemeFn(t), 128 errExpected: nil, 129 }, 130 { 131 name: "v1, feature disabled, create, non-zero, no error expected", 132 obj: v1ObjFn(ptr.To(int32(1))), 133 zeroFeatureEnabled: false, 134 scheme: v1SchemeFn(t), 135 errExpected: nil, 136 }, 137 { 138 name: "v1, feature enabled, create, zero value, no error expected", 139 obj: v1ObjFn(ptr.To(int32(0))), 140 zeroFeatureEnabled: true, 141 scheme: v1SchemeFn(t), 142 errExpected: nil, 143 }, 144 { 145 name: "v1, feature enabled, create, unset, no error expected", 146 obj: v1ObjFn(nil), 147 zeroFeatureEnabled: true, 148 scheme: v1SchemeFn(t), 149 errExpected: nil, 150 }, 151 { 152 name: "v1, feature enabled, create, non-zero, no error expected", 153 obj: v1ObjFn(ptr.To(int32(1))), 154 zeroFeatureEnabled: true, 155 scheme: v1SchemeFn(t), 156 errExpected: nil, 157 }, 158 { 159 name: "v1beta3, feature disabled, create, zero value, error expected", 160 obj: v1beta3ObjFn(0, true), 161 zeroFeatureEnabled: false, 162 scheme: v1beta3SchemeFn(t), 163 errExpected: errExpectedFn(0, "must be positive"), 164 }, 165 { 166 name: "v1beta3, feature disabled, create, zero value without annotation, no error expected", 167 obj: v1beta3ObjFn(0, false), 168 zeroFeatureEnabled: false, 169 scheme: v1beta3SchemeFn(t), 170 errExpected: nil, 171 }, 172 { 173 name: "v1beta3, feature disabled, create, non-zero, no error expected", 174 obj: v1beta3ObjFn(1, false), 175 zeroFeatureEnabled: false, 176 scheme: v1beta3SchemeFn(t), 177 errExpected: nil, 178 }, 179 { 180 name: "v1beta3, feature enabled, create, zero value, no error expected", 181 obj: v1beta3ObjFn(0, true), 182 zeroFeatureEnabled: true, 183 scheme: v1beta3SchemeFn(t), 184 errExpected: nil, 185 }, 186 { 187 name: "v1beta3, feature enabled, create, zero value without annotation, no error expected", 188 obj: v1beta3ObjFn(0, false), 189 zeroFeatureEnabled: true, 190 scheme: v1beta3SchemeFn(t), 191 errExpected: nil, 192 }, 193 { 194 name: "v1beta3, feature enabled, create, non-zero, no error expected", 195 obj: v1beta3ObjFn(1, false), 196 zeroFeatureEnabled: true, 197 scheme: v1beta3SchemeFn(t), 198 errExpected: nil, 199 }, 200 201 // the following use cases cover UPDATE 202 { 203 name: "v1, feature disabled, update, zero value, existing has non-zero, error expected", 204 obj: v1ObjFn(ptr.To(int32(0))), 205 old: internalObjFn(1), 206 zeroFeatureEnabled: false, 207 scheme: v1SchemeFn(t), 208 errExpected: errExpectedFn(0, "must be positive"), 209 }, 210 { 211 name: "v1, feature disabled, update, zero value, existing has zero, no error expected", 212 obj: v1ObjFn(ptr.To(int32(0))), 213 old: internalObjFn(0), 214 zeroFeatureEnabled: false, 215 scheme: v1SchemeFn(t), 216 errExpected: nil, 217 }, 218 { 219 name: "v1, feature disabled, update, non-zero value, existing has zero, no error expected", 220 obj: v1ObjFn(ptr.To(int32(1))), 221 old: internalObjFn(0), 222 zeroFeatureEnabled: false, 223 scheme: v1SchemeFn(t), 224 errExpected: nil, 225 }, 226 { 227 name: "v1, feature enabled, update, zero value, existing has non-zero, no error expected", 228 obj: v1ObjFn(ptr.To(int32(0))), 229 old: internalObjFn(1), 230 zeroFeatureEnabled: true, 231 scheme: v1SchemeFn(t), 232 errExpected: nil, 233 }, 234 { 235 name: "v1, feature enabled, update, zero value, existing has zero, no error expected", 236 obj: v1ObjFn(ptr.To(int32(0))), 237 old: internalObjFn(0), 238 zeroFeatureEnabled: true, 239 scheme: v1SchemeFn(t), 240 errExpected: nil, 241 }, 242 { 243 name: "v1, feature enabled, update, non-zero value, existing has zero, no error expected", 244 obj: v1ObjFn(ptr.To(int32(1))), 245 old: internalObjFn(0), 246 zeroFeatureEnabled: true, 247 scheme: v1SchemeFn(t), 248 errExpected: nil, 249 }, 250 { 251 name: "v1beta3, feature disabled, update, zero value, existing has non-zero, error expected", 252 obj: v1beta3ObjFn(0, true), 253 old: internalObjFn(1), 254 zeroFeatureEnabled: false, 255 scheme: v1beta3SchemeFn(t), 256 errExpected: errExpectedFn(0, "must be positive"), 257 }, 258 { 259 name: "v1beta3, feature disabled, update, zero value, existing has zero, no error expected", 260 obj: v1beta3ObjFn(0, true), 261 old: internalObjFn(0), 262 zeroFeatureEnabled: false, 263 scheme: v1beta3SchemeFn(t), 264 errExpected: nil, 265 }, 266 { 267 name: "v1beta3, feature disabled, update, non-zero value, existing has zero, no error expected", 268 obj: v1beta3ObjFn(1, false), 269 old: internalObjFn(0), 270 zeroFeatureEnabled: false, 271 scheme: v1beta3SchemeFn(t), 272 errExpected: nil, 273 }, 274 { 275 name: "v1beta3, feature enabled, update, zero value, existing has non-zero, no error expected", 276 obj: v1beta3ObjFn(0, true), 277 old: internalObjFn(1), 278 zeroFeatureEnabled: true, 279 scheme: v1beta3SchemeFn(t), 280 errExpected: nil, 281 }, 282 { 283 name: "v1beta3, feature enabled, update, zero value, existing has zero, no error expected", 284 obj: v1beta3ObjFn(0, true), 285 old: internalObjFn(0), 286 zeroFeatureEnabled: true, 287 scheme: v1beta3SchemeFn(t), 288 errExpected: nil, 289 }, 290 { 291 name: "v1beta3, feature enabled, update, non-zero value, existing has zero, no error expected", 292 obj: v1beta3ObjFn(1, false), 293 old: internalObjFn(0), 294 zeroFeatureEnabled: true, 295 scheme: v1beta3SchemeFn(t), 296 errExpected: nil, 297 }, 298 } 299 300 for _, test := range tests { 301 t.Run(test.name, func(t *testing.T) { 302 defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ZeroLimitedNominalConcurrencyShares, test.zeroFeatureEnabled)() 303 304 scheme := test.scheme 305 scheme.Default(test.obj) 306 307 ctx := context.TODO() 308 internal := &flowcontrol.PriorityLevelConfiguration{} 309 if err := scheme.Convert(test.obj, internal, ctx); err != nil { 310 t.Errorf("Expected no error while converting to internal type: %v", err) 311 } 312 313 err := func(obj, old *flowcontrol.PriorityLevelConfiguration) field.ErrorList { 314 if old == nil { 315 return Strategy.Validate(ctx, obj) // for create operation 316 } 317 return Strategy.ValidateUpdate(ctx, obj, old) // for update operation 318 }(internal, test.old) 319 320 if !cmp.Equal(test.errExpected, err) { 321 t.Errorf("Expected error: %v, diff: %s", test.errExpected, cmp.Diff(test.errExpected, err)) 322 } 323 }) 324 } 325 }