1
16
17 package validation
18
19 import (
20 "fmt"
21 "strings"
22
23 flowcontrolv1beta1 "k8s.io/api/flowcontrol/v1beta1"
24 flowcontrolv1beta2 "k8s.io/api/flowcontrol/v1beta2"
25 flowcontrolv1beta3 "k8s.io/api/flowcontrol/v1beta3"
26 apiequality "k8s.io/apimachinery/pkg/api/equality"
27 apimachineryvalidation "k8s.io/apimachinery/pkg/api/validation"
28 "k8s.io/apimachinery/pkg/runtime/schema"
29 "k8s.io/apimachinery/pkg/util/sets"
30 "k8s.io/apimachinery/pkg/util/validation/field"
31 "k8s.io/apiserver/pkg/util/shufflesharding"
32 apivalidation "k8s.io/kubernetes/pkg/apis/core/validation"
33 "k8s.io/kubernetes/pkg/apis/flowcontrol"
34 "k8s.io/kubernetes/pkg/apis/flowcontrol/internalbootstrap"
35 )
36
37
38 var ValidateFlowSchemaName = apimachineryvalidation.NameIsDNSSubdomain
39
40
41 var ValidatePriorityLevelConfigurationName = apimachineryvalidation.NameIsDNSSubdomain
42
43 var supportedDistinguisherMethods = sets.NewString(
44 string(flowcontrol.FlowDistinguisherMethodByNamespaceType),
45 string(flowcontrol.FlowDistinguisherMethodByUserType),
46 )
47
48 var priorityLevelConfigurationQueuingMaxQueues int32 = 10 * 1000 * 1000
49
50 var supportedVerbs = sets.NewString(
51 "get",
52 "list",
53 "create",
54 "update",
55 "delete",
56 "deletecollection",
57 "patch",
58 "watch",
59 "proxy",
60 )
61
62 var supportedSubjectKinds = sets.NewString(
63 string(flowcontrol.SubjectKindServiceAccount),
64 string(flowcontrol.SubjectKindGroup),
65 string(flowcontrol.SubjectKindUser),
66 )
67
68 var supportedPriorityLevelEnablement = sets.NewString(
69 string(flowcontrol.PriorityLevelEnablementExempt),
70 string(flowcontrol.PriorityLevelEnablementLimited),
71 )
72
73 var supportedLimitResponseType = sets.NewString(
74 string(flowcontrol.LimitResponseTypeQueue),
75 string(flowcontrol.LimitResponseTypeReject),
76 )
77
78
79 type PriorityLevelValidationOptions struct {
80
81
82
83 AllowZeroLimitedNominalConcurrencyShares bool
84 }
85
86
87 func ValidateFlowSchema(fs *flowcontrol.FlowSchema) field.ErrorList {
88 allErrs := apivalidation.ValidateObjectMeta(&fs.ObjectMeta, false, ValidateFlowSchemaName, field.NewPath("metadata"))
89 specPath := field.NewPath("spec")
90 allErrs = append(allErrs, ValidateFlowSchemaSpec(fs.Name, &fs.Spec, specPath)...)
91 if mand, ok := internalbootstrap.MandatoryFlowSchemas[fs.Name]; ok {
92
93
94
95
96 if !apiequality.Semantic.DeepEqual(fs.Spec, mand.Spec) {
97 allErrs = append(allErrs, field.Invalid(specPath, fs.Spec, fmt.Sprintf("spec of '%s' must equal the fixed value", fs.Name)))
98 }
99 }
100 allErrs = append(allErrs, ValidateFlowSchemaStatus(&fs.Status, field.NewPath("status"))...)
101 return allErrs
102 }
103
104
105 func ValidateFlowSchemaUpdate(old, fs *flowcontrol.FlowSchema) field.ErrorList {
106 return ValidateFlowSchema(fs)
107 }
108
109
110 func ValidateFlowSchemaSpec(fsName string, spec *flowcontrol.FlowSchemaSpec, fldPath *field.Path) field.ErrorList {
111 var allErrs field.ErrorList
112 if spec.MatchingPrecedence <= 0 {
113 allErrs = append(allErrs, field.Invalid(fldPath.Child("matchingPrecedence"), spec.MatchingPrecedence, "must be a positive value"))
114 }
115 if spec.MatchingPrecedence > flowcontrol.FlowSchemaMaxMatchingPrecedence {
116 allErrs = append(allErrs, field.Invalid(fldPath.Child("matchingPrecedence"), spec.MatchingPrecedence, fmt.Sprintf("must not be greater than %v", flowcontrol.FlowSchemaMaxMatchingPrecedence)))
117 }
118 if (spec.MatchingPrecedence == 1) && (fsName != flowcontrol.FlowSchemaNameExempt) {
119 allErrs = append(allErrs, field.Invalid(fldPath.Child("matchingPrecedence"), spec.MatchingPrecedence, "only the schema named 'exempt' may have matchingPrecedence 1"))
120 }
121 if spec.DistinguisherMethod != nil {
122 if !supportedDistinguisherMethods.Has(string(spec.DistinguisherMethod.Type)) {
123 allErrs = append(allErrs, field.NotSupported(fldPath.Child("distinguisherMethod").Child("type"), spec.DistinguisherMethod, supportedDistinguisherMethods.List()))
124 }
125 }
126 if len(spec.PriorityLevelConfiguration.Name) > 0 {
127 for _, msg := range ValidatePriorityLevelConfigurationName(spec.PriorityLevelConfiguration.Name, false) {
128 allErrs = append(allErrs, field.Invalid(fldPath.Child("priorityLevelConfiguration").Child("name"), spec.PriorityLevelConfiguration.Name, msg))
129 }
130 } else {
131 allErrs = append(allErrs, field.Required(fldPath.Child("priorityLevelConfiguration").Child("name"), "must reference a priority level"))
132 }
133 for i, rule := range spec.Rules {
134 allErrs = append(allErrs, ValidateFlowSchemaPolicyRulesWithSubjects(&rule, fldPath.Child("rules").Index(i))...)
135 }
136 return allErrs
137 }
138
139
140 func ValidateFlowSchemaPolicyRulesWithSubjects(rule *flowcontrol.PolicyRulesWithSubjects, fldPath *field.Path) field.ErrorList {
141 var allErrs field.ErrorList
142 if len(rule.Subjects) > 0 {
143 for i, subject := range rule.Subjects {
144 allErrs = append(allErrs, ValidateFlowSchemaSubject(&subject, fldPath.Child("subjects").Index(i))...)
145 }
146 } else {
147 allErrs = append(allErrs, field.Required(fldPath.Child("subjects"), "subjects must contain at least one value"))
148 }
149
150 if len(rule.ResourceRules) == 0 && len(rule.NonResourceRules) == 0 {
151 allErrs = append(allErrs, field.Required(fldPath, "at least one of resourceRules and nonResourceRules has to be non-empty"))
152 }
153 for i, resourceRule := range rule.ResourceRules {
154 allErrs = append(allErrs, ValidateFlowSchemaResourcePolicyRule(&resourceRule, fldPath.Child("resourceRules").Index(i))...)
155 }
156 for i, nonResourceRule := range rule.NonResourceRules {
157 allErrs = append(allErrs, ValidateFlowSchemaNonResourcePolicyRule(&nonResourceRule, fldPath.Child("nonResourceRules").Index(i))...)
158 }
159 return allErrs
160 }
161
162
163 func ValidateFlowSchemaSubject(subject *flowcontrol.Subject, fldPath *field.Path) field.ErrorList {
164 var allErrs field.ErrorList
165 switch subject.Kind {
166 case flowcontrol.SubjectKindServiceAccount:
167 allErrs = append(allErrs, ValidateServiceAccountSubject(subject.ServiceAccount, fldPath.Child("serviceAccount"))...)
168 if subject.User != nil {
169 allErrs = append(allErrs, field.Forbidden(fldPath.Child("user"), "user is forbidden when subject kind is not 'User'"))
170 }
171 if subject.Group != nil {
172 allErrs = append(allErrs, field.Forbidden(fldPath.Child("group"), "group is forbidden when subject kind is not 'Group'"))
173 }
174 case flowcontrol.SubjectKindUser:
175 allErrs = append(allErrs, ValidateUserSubject(subject.User, fldPath.Child("user"))...)
176 if subject.ServiceAccount != nil {
177 allErrs = append(allErrs, field.Forbidden(fldPath.Child("serviceAccount"), "serviceAccount is forbidden when subject kind is not 'ServiceAccount'"))
178 }
179 if subject.Group != nil {
180 allErrs = append(allErrs, field.Forbidden(fldPath.Child("group"), "group is forbidden when subject kind is not 'Group'"))
181 }
182 case flowcontrol.SubjectKindGroup:
183 allErrs = append(allErrs, ValidateGroupSubject(subject.Group, fldPath.Child("group"))...)
184 if subject.ServiceAccount != nil {
185 allErrs = append(allErrs, field.Forbidden(fldPath.Child("serviceAccount"), "serviceAccount is forbidden when subject kind is not 'ServiceAccount'"))
186 }
187 if subject.User != nil {
188 allErrs = append(allErrs, field.Forbidden(fldPath.Child("user"), "user is forbidden when subject kind is not 'User'"))
189 }
190 default:
191 allErrs = append(allErrs, field.NotSupported(fldPath.Child("kind"), subject.Kind, supportedSubjectKinds.List()))
192 }
193 return allErrs
194 }
195
196
197 func ValidateServiceAccountSubject(subject *flowcontrol.ServiceAccountSubject, fldPath *field.Path) field.ErrorList {
198 var allErrs field.ErrorList
199 if subject == nil {
200 return append(allErrs, field.Required(fldPath, "serviceAccount is required when subject kind is 'ServiceAccount'"))
201 }
202 if len(subject.Name) == 0 {
203 allErrs = append(allErrs, field.Required(fldPath.Child("name"), ""))
204 } else if subject.Name != flowcontrol.NameAll {
205 for _, msg := range apimachineryvalidation.ValidateServiceAccountName(subject.Name, false) {
206 allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), subject.Name, msg))
207 }
208 }
209
210 if len(subject.Namespace) > 0 {
211 for _, msg := range apimachineryvalidation.ValidateNamespaceName(subject.Namespace, false) {
212 allErrs = append(allErrs, field.Invalid(fldPath.Child("namespace"), subject.Namespace, msg))
213 }
214 } else {
215 allErrs = append(allErrs, field.Required(fldPath.Child("namespace"), "must specify namespace for service account"))
216 }
217
218 return allErrs
219 }
220
221
222 func ValidateUserSubject(subject *flowcontrol.UserSubject, fldPath *field.Path) field.ErrorList {
223 var allErrs field.ErrorList
224 if subject == nil {
225 return append(allErrs, field.Required(fldPath, "user is required when subject kind is 'User'"))
226 }
227 if len(subject.Name) == 0 {
228 allErrs = append(allErrs, field.Required(fldPath.Child("name"), ""))
229 }
230 return allErrs
231 }
232
233
234 func ValidateGroupSubject(subject *flowcontrol.GroupSubject, fldPath *field.Path) field.ErrorList {
235 var allErrs field.ErrorList
236 if subject == nil {
237 return append(allErrs, field.Required(fldPath, "group is required when subject kind is 'Group'"))
238 }
239 if len(subject.Name) == 0 {
240 allErrs = append(allErrs, field.Required(fldPath.Child("name"), ""))
241 }
242 return allErrs
243 }
244
245
246 func ValidateFlowSchemaNonResourcePolicyRule(rule *flowcontrol.NonResourcePolicyRule, fldPath *field.Path) field.ErrorList {
247 var allErrs field.ErrorList
248
249 if len(rule.Verbs) == 0 {
250 allErrs = append(allErrs, field.Required(fldPath.Child("verbs"), "verbs must contain at least one value"))
251 } else if hasWildcard(rule.Verbs) {
252 if len(rule.Verbs) > 1 {
253 allErrs = append(allErrs, field.Invalid(fldPath.Child("verbs"), rule.Verbs, "if '*' is present, must not specify other verbs"))
254 }
255 } else if !supportedVerbs.IsSuperset(sets.NewString(rule.Verbs...)) {
256
257 allErrs = append(allErrs, field.NotSupported(fldPath.Child("verbs"), rule.Verbs, supportedVerbs.List()))
258 }
259
260 if len(rule.NonResourceURLs) == 0 {
261 allErrs = append(allErrs, field.Required(fldPath.Child("nonResourceURLs"), "nonResourceURLs must contain at least one value"))
262 } else if hasWildcard(rule.NonResourceURLs) {
263 if len(rule.NonResourceURLs) > 1 {
264 allErrs = append(allErrs, field.Invalid(fldPath.Child("nonResourceURLs"), rule.NonResourceURLs, "if '*' is present, must not specify other non-resource URLs"))
265 }
266 } else {
267 for i, nonResourceURL := range rule.NonResourceURLs {
268 if err := ValidateNonResourceURLPath(nonResourceURL, fldPath.Child("nonResourceURLs").Index(i)); err != nil {
269 allErrs = append(allErrs, err)
270 }
271 }
272 }
273
274 return allErrs
275 }
276
277
278 func ValidateFlowSchemaResourcePolicyRule(rule *flowcontrol.ResourcePolicyRule, fldPath *field.Path) field.ErrorList {
279 var allErrs field.ErrorList
280
281 if len(rule.Verbs) == 0 {
282 allErrs = append(allErrs, field.Required(fldPath.Child("verbs"), "verbs must contain at least one value"))
283 } else if hasWildcard(rule.Verbs) {
284 if len(rule.Verbs) > 1 {
285 allErrs = append(allErrs, field.Invalid(fldPath.Child("verbs"), rule.Verbs, "if '*' is present, must not specify other verbs"))
286 }
287 } else if !supportedVerbs.IsSuperset(sets.NewString(rule.Verbs...)) {
288
289 allErrs = append(allErrs, field.NotSupported(fldPath.Child("verbs"), rule.Verbs, supportedVerbs.List()))
290 }
291
292 if len(rule.APIGroups) == 0 {
293 allErrs = append(allErrs, field.Required(fldPath.Child("apiGroups"), "resource rules must supply at least one api group"))
294 } else if len(rule.APIGroups) > 1 && hasWildcard(rule.APIGroups) {
295 allErrs = append(allErrs, field.Invalid(fldPath.Child("apiGroups"), rule.APIGroups, "if '*' is present, must not specify other api groups"))
296 }
297
298 if len(rule.Resources) == 0 {
299 allErrs = append(allErrs, field.Required(fldPath.Child("resources"), "resource rules must supply at least one resource"))
300 } else if len(rule.Resources) > 1 && hasWildcard(rule.Resources) {
301 allErrs = append(allErrs, field.Invalid(fldPath.Child("resources"), rule.Resources, "if '*' is present, must not specify other resources"))
302 }
303
304 if len(rule.Namespaces) == 0 && !rule.ClusterScope {
305 allErrs = append(allErrs, field.Required(fldPath.Child("namespaces"), "resource rules that are not cluster scoped must supply at least one namespace"))
306 } else if hasWildcard(rule.Namespaces) {
307 if len(rule.Namespaces) > 1 {
308 allErrs = append(allErrs, field.Invalid(fldPath.Child("namespaces"), rule.Namespaces, "if '*' is present, must not specify other namespaces"))
309 }
310 } else {
311 for idx, tgtNS := range rule.Namespaces {
312 for _, msg := range apimachineryvalidation.ValidateNamespaceName(tgtNS, false) {
313 allErrs = append(allErrs, field.Invalid(fldPath.Child("namespaces").Index(idx), tgtNS, nsErrIntro+msg))
314 }
315 }
316 }
317
318 return allErrs
319 }
320
321 const nsErrIntro = "each member of this list must be '*' or a DNS-1123 label; "
322
323
324 func ValidateFlowSchemaStatus(status *flowcontrol.FlowSchemaStatus, fldPath *field.Path) field.ErrorList {
325 var allErrs field.ErrorList
326 keys := sets.NewString()
327 for i, condition := range status.Conditions {
328 if keys.Has(string(condition.Type)) {
329 allErrs = append(allErrs, field.Duplicate(fldPath.Child("conditions").Index(i).Child("type"), condition.Type))
330 }
331 keys.Insert(string(condition.Type))
332 allErrs = append(allErrs, ValidateFlowSchemaCondition(&condition, fldPath.Child("conditions").Index(i))...)
333 }
334 return allErrs
335 }
336
337
338 func ValidateFlowSchemaStatusUpdate(old, fs *flowcontrol.FlowSchema) field.ErrorList {
339 return ValidateFlowSchemaStatus(&fs.Status, field.NewPath("status"))
340 }
341
342
343 func ValidateFlowSchemaCondition(condition *flowcontrol.FlowSchemaCondition, fldPath *field.Path) field.ErrorList {
344 var allErrs field.ErrorList
345 if len(condition.Type) == 0 {
346 allErrs = append(allErrs, field.Required(fldPath.Child("type"), "must not be empty"))
347 }
348 return allErrs
349 }
350
351
352 func ValidatePriorityLevelConfiguration(pl *flowcontrol.PriorityLevelConfiguration, requestGV schema.GroupVersion, opts PriorityLevelValidationOptions) field.ErrorList {
353 allErrs := apivalidation.ValidateObjectMeta(&pl.ObjectMeta, false, ValidatePriorityLevelConfigurationName, field.NewPath("metadata"))
354
355
356
357
358 if _, ok := pl.ObjectMeta.Annotations[flowcontrolv1beta3.PriorityLevelPreserveZeroConcurrencySharesKey]; ok {
359 allErrs = append(allErrs, field.Forbidden(field.NewPath("metadata").Child("annotations"), fmt.Sprintf("annotation '%s' is forbidden", flowcontrolv1beta3.PriorityLevelPreserveZeroConcurrencySharesKey)))
360 }
361
362 specPath := field.NewPath("spec")
363 allErrs = append(allErrs, ValidatePriorityLevelConfigurationSpec(&pl.Spec, requestGV, pl.Name, specPath, opts)...)
364 allErrs = append(allErrs, ValidateIfMandatoryPriorityLevelConfigurationObject(pl, specPath)...)
365 allErrs = append(allErrs, ValidatePriorityLevelConfigurationStatus(&pl.Status, field.NewPath("status"))...)
366 return allErrs
367 }
368
369 func ValidateIfMandatoryPriorityLevelConfigurationObject(pl *flowcontrol.PriorityLevelConfiguration, fldPath *field.Path) field.ErrorList {
370 var allErrs field.ErrorList
371 mand, ok := internalbootstrap.MandatoryPriorityLevelConfigurations[pl.Name]
372 if !ok {
373 return allErrs
374 }
375
376 if pl.Name == flowcontrol.PriorityLevelConfigurationNameExempt {
377
378
379
380 want := &mand.Spec
381 have := pl.Spec.DeepCopy()
382 have.Exempt = want.Exempt
383 if !apiequality.Semantic.DeepEqual(want, have) {
384 allErrs = append(allErrs, field.Invalid(fldPath, pl.Spec, fmt.Sprintf("spec of '%s' except the 'spec.exempt' field must equal the fixed value", pl.Name)))
385 }
386 return allErrs
387 }
388
389
390
391
392
393 if !apiequality.Semantic.DeepEqual(pl.Spec, mand.Spec) {
394 allErrs = append(allErrs, field.Invalid(fldPath, pl.Spec, fmt.Sprintf("spec of '%s' must equal the fixed value", pl.Name)))
395 }
396 return allErrs
397 }
398
399
400 func ValidatePriorityLevelConfigurationSpec(spec *flowcontrol.PriorityLevelConfigurationSpec, requestGV schema.GroupVersion, name string, fldPath *field.Path, opts PriorityLevelValidationOptions) field.ErrorList {
401 var allErrs field.ErrorList
402 if (name == flowcontrol.PriorityLevelConfigurationNameExempt) != (spec.Type == flowcontrol.PriorityLevelEnablementExempt) {
403 allErrs = append(allErrs, field.Invalid(fldPath.Child("type"), spec.Type, "type must be 'Exempt' if and only if name is 'exempt'"))
404 }
405 switch spec.Type {
406 case flowcontrol.PriorityLevelEnablementExempt:
407 if spec.Limited != nil {
408 allErrs = append(allErrs, field.Forbidden(fldPath.Child("limited"), "must be nil if the type is not Limited"))
409 }
410 if spec.Exempt != nil {
411 allErrs = append(allErrs, ValidateExemptPriorityLevelConfiguration(spec.Exempt, fldPath.Child("exempt"))...)
412 }
413 case flowcontrol.PriorityLevelEnablementLimited:
414 if spec.Exempt != nil {
415 allErrs = append(allErrs, field.Forbidden(fldPath.Child("exempt"), "must be nil if the type is Limited"))
416 }
417
418 if spec.Limited == nil {
419 allErrs = append(allErrs, field.Required(fldPath.Child("limited"), "must not be empty when type is Limited"))
420 } else {
421 allErrs = append(allErrs, ValidateLimitedPriorityLevelConfiguration(spec.Limited, requestGV, fldPath.Child("limited"), opts)...)
422 }
423 default:
424 allErrs = append(allErrs, field.NotSupported(fldPath.Child("type"), spec.Type, supportedPriorityLevelEnablement.List()))
425 }
426 return allErrs
427 }
428
429
430 func ValidateLimitedPriorityLevelConfiguration(lplc *flowcontrol.LimitedPriorityLevelConfiguration, requestGV schema.GroupVersion, fldPath *field.Path, opts PriorityLevelValidationOptions) field.ErrorList {
431 var allErrs field.ErrorList
432 if opts.AllowZeroLimitedNominalConcurrencyShares {
433 if lplc.NominalConcurrencyShares < 0 {
434 allErrs = append(allErrs, field.Invalid(fldPath.Child(getVersionedFieldNameForConcurrencyShares(requestGV)), lplc.NominalConcurrencyShares, "must be a non-negative integer"))
435 }
436 } else {
437 if lplc.NominalConcurrencyShares <= 0 {
438 allErrs = append(allErrs, field.Invalid(fldPath.Child(getVersionedFieldNameForConcurrencyShares(requestGV)), lplc.NominalConcurrencyShares, "must be positive"))
439 }
440 }
441 allErrs = append(allErrs, ValidateLimitResponse(lplc.LimitResponse, fldPath.Child("limitResponse"))...)
442
443 if lplc.LendablePercent != nil && !(*lplc.LendablePercent >= 0 && *lplc.LendablePercent <= 100) {
444 allErrs = append(allErrs, field.Invalid(fldPath.Child("lendablePercent"), *lplc.LendablePercent, "must be between 0 and 100, inclusive"))
445 }
446 if lplc.BorrowingLimitPercent != nil && *lplc.BorrowingLimitPercent < 0 {
447 allErrs = append(allErrs, field.Invalid(fldPath.Child("borrowingLimitPercent"), *lplc.BorrowingLimitPercent, "if specified, must be a non-negative integer"))
448 }
449
450 return allErrs
451 }
452
453 func ValidateExemptPriorityLevelConfiguration(eplc *flowcontrol.ExemptPriorityLevelConfiguration, fldPath *field.Path) field.ErrorList {
454 var allErrs field.ErrorList
455 if eplc.NominalConcurrencyShares != nil && *eplc.NominalConcurrencyShares < 0 {
456 allErrs = append(allErrs, field.Invalid(fldPath.Child("nominalConcurrencyShares"), *eplc.NominalConcurrencyShares, "must be a non-negative integer"))
457 }
458 if eplc.LendablePercent != nil && !(*eplc.LendablePercent >= 0 && *eplc.LendablePercent <= 100) {
459 allErrs = append(allErrs, field.Invalid(fldPath.Child("lendablePercent"), *eplc.LendablePercent, "must be between 0 and 100, inclusive"))
460 }
461 return allErrs
462 }
463
464 func getVersionedFieldNameForConcurrencyShares(requestGV schema.GroupVersion) string {
465 switch {
466 case requestGV == flowcontrolv1beta1.SchemeGroupVersion ||
467 requestGV == flowcontrolv1beta2.SchemeGroupVersion:
468 return "assuredConcurrencyShares"
469 default:
470 return "nominalConcurrencyShares"
471 }
472 }
473
474
475 func ValidateLimitResponse(lr flowcontrol.LimitResponse, fldPath *field.Path) field.ErrorList {
476 var allErrs field.ErrorList
477 switch lr.Type {
478 case flowcontrol.LimitResponseTypeReject:
479 if lr.Queuing != nil {
480 allErrs = append(allErrs, field.Forbidden(fldPath.Child("queuing"), "must be nil if limited.limitResponse.type is not Limited"))
481 }
482 case flowcontrol.LimitResponseTypeQueue:
483 if lr.Queuing == nil {
484 allErrs = append(allErrs, field.Required(fldPath.Child("queuing"), "must not be empty if limited.limitResponse.type is Limited"))
485 } else {
486 allErrs = append(allErrs, ValidatePriorityLevelQueuingConfiguration(lr.Queuing, fldPath.Child("queuing"))...)
487 }
488 default:
489 allErrs = append(allErrs, field.NotSupported(fldPath.Child("type"), lr.Type, supportedLimitResponseType.List()))
490 }
491 return allErrs
492 }
493
494
495 func ValidatePriorityLevelQueuingConfiguration(queuing *flowcontrol.QueuingConfiguration, fldPath *field.Path) field.ErrorList {
496 var allErrs field.ErrorList
497 if queuing.QueueLengthLimit <= 0 {
498 allErrs = append(allErrs, field.Invalid(fldPath.Child("queueLengthLimit"), queuing.QueueLengthLimit, "must be positive"))
499 }
500
501
502 if queuing.Queues <= 0 {
503 allErrs = append(allErrs, field.Invalid(fldPath.Child("queues"), queuing.Queues, "must be positive"))
504 } else if queuing.Queues > priorityLevelConfigurationQueuingMaxQueues {
505 allErrs = append(allErrs, field.Invalid(fldPath.Child("queues"), queuing.Queues,
506 fmt.Sprintf("must not be greater than %d", priorityLevelConfigurationQueuingMaxQueues)))
507 }
508
509 if queuing.HandSize <= 0 {
510 allErrs = append(allErrs, field.Invalid(fldPath.Child("handSize"), queuing.HandSize, "must be positive"))
511 } else if queuing.HandSize > queuing.Queues {
512 allErrs = append(allErrs, field.Invalid(fldPath.Child("handSize"), queuing.HandSize,
513 fmt.Sprintf("should not be greater than queues (%d)", queuing.Queues)))
514 } else if entropy := shufflesharding.RequiredEntropyBits(int(queuing.Queues), int(queuing.HandSize)); entropy > shufflesharding.MaxHashBits {
515 allErrs = append(allErrs, field.Invalid(fldPath.Child("handSize"), queuing.HandSize,
516 fmt.Sprintf("required entropy bits of deckSize %d and handSize %d should not be greater than %d", queuing.Queues, queuing.HandSize, shufflesharding.MaxHashBits)))
517 }
518 return allErrs
519 }
520
521
522 func ValidatePriorityLevelConfigurationStatus(status *flowcontrol.PriorityLevelConfigurationStatus, fldPath *field.Path) field.ErrorList {
523 var allErrs field.ErrorList
524 keys := sets.NewString()
525 for i, condition := range status.Conditions {
526 if keys.Has(string(condition.Type)) {
527 allErrs = append(allErrs, field.Duplicate(fldPath.Child("conditions").Index(i).Child("type"), condition.Type))
528 }
529 keys.Insert(string(condition.Type))
530 allErrs = append(allErrs, ValidatePriorityLevelConfigurationCondition(&condition, fldPath.Child("conditions").Index(i))...)
531 }
532 return allErrs
533 }
534
535
536 func ValidatePriorityLevelConfigurationStatusUpdate(old, pl *flowcontrol.PriorityLevelConfiguration) field.ErrorList {
537 return ValidatePriorityLevelConfigurationStatus(&pl.Status, field.NewPath("status"))
538 }
539
540
541 func ValidatePriorityLevelConfigurationCondition(condition *flowcontrol.PriorityLevelConfigurationCondition, fldPath *field.Path) field.ErrorList {
542 var allErrs field.ErrorList
543 if len(condition.Type) == 0 {
544 allErrs = append(allErrs, field.Required(fldPath.Child("type"), "must not be empty"))
545 }
546 return allErrs
547 }
548
549
550
551
552
553
554 func ValidateNonResourceURLPath(path string, fldPath *field.Path) *field.Error {
555 if len(path) == 0 {
556 return field.Invalid(fldPath, path, "must not be empty")
557 }
558 if path == "/" {
559 return nil
560 }
561
562 if !strings.HasPrefix(path, "/") {
563 return field.Invalid(fldPath, path, "must start with slash")
564 }
565 if strings.Contains(path, " ") {
566 return field.Invalid(fldPath, path, "must not contain white-space")
567 }
568 if strings.Contains(path, "//") {
569 return field.Invalid(fldPath, path, "must not contain double slash")
570 }
571 wildcardCount := strings.Count(path, "*")
572 if wildcardCount > 1 || (wildcardCount == 1 && path[len(path)-2:] != "/*") {
573 return field.Invalid(fldPath, path, "wildcard can only do suffix matching")
574 }
575 return nil
576 }
577
578 func hasWildcard(operations []string) bool {
579 return memberInList("*", operations...)
580 }
581
582 func memberInList(seek string, a ...string) bool {
583 for _, ai := range a {
584 if ai == seek {
585 return true
586 }
587 }
588 return false
589 }
590
View as plain text