validation.go 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511
  1. /*
  2. Copyright 2019 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package validation
  14. import (
  15. "fmt"
  16. "strings"
  17. apiequality "k8s.io/apimachinery/pkg/api/equality"
  18. apimachineryvalidation "k8s.io/apimachinery/pkg/api/validation"
  19. "k8s.io/apimachinery/pkg/util/sets"
  20. "k8s.io/apimachinery/pkg/util/validation/field"
  21. "k8s.io/apiserver/pkg/util/shufflesharding"
  22. apivalidation "k8s.io/kubernetes/pkg/apis/core/validation"
  23. "k8s.io/kubernetes/pkg/apis/flowcontrol"
  24. "k8s.io/kubernetes/pkg/apis/flowcontrol/internalbootstrap"
  25. )
  26. // ValidateFlowSchemaName validates name for flow-schema.
  27. var ValidateFlowSchemaName = apimachineryvalidation.NameIsDNSSubdomain
  28. // ValidatePriorityLevelConfigurationName validates name for priority-level-configuration.
  29. var ValidatePriorityLevelConfigurationName = apimachineryvalidation.NameIsDNSSubdomain
  30. var supportedDistinguisherMethods = sets.NewString(
  31. string(flowcontrol.FlowDistinguisherMethodByNamespaceType),
  32. string(flowcontrol.FlowDistinguisherMethodByUserType),
  33. )
  34. var priorityLevelConfigurationQueuingMaxQueues int32 = 10 * 1000 * 1000 // 10^7
  35. var supportedVerbs = sets.NewString(
  36. "get",
  37. "list",
  38. "create",
  39. "update",
  40. "delete",
  41. "deletecollection",
  42. "patch",
  43. "watch",
  44. "proxy",
  45. )
  46. var supportedSubjectKinds = sets.NewString(
  47. string(flowcontrol.SubjectKindServiceAccount),
  48. string(flowcontrol.SubjectKindGroup),
  49. string(flowcontrol.SubjectKindUser),
  50. )
  51. var supportedPriorityLevelEnablement = sets.NewString(
  52. string(flowcontrol.PriorityLevelEnablementExempt),
  53. string(flowcontrol.PriorityLevelEnablementLimited),
  54. )
  55. var supportedLimitResponseType = sets.NewString(
  56. string(flowcontrol.LimitResponseTypeQueue),
  57. string(flowcontrol.LimitResponseTypeReject),
  58. )
  59. // ValidateFlowSchema validates the content of flow-schema
  60. func ValidateFlowSchema(fs *flowcontrol.FlowSchema) field.ErrorList {
  61. allErrs := apivalidation.ValidateObjectMeta(&fs.ObjectMeta, false, ValidateFlowSchemaName, field.NewPath("metadata"))
  62. specPath := field.NewPath("spec")
  63. allErrs = append(allErrs, ValidateFlowSchemaSpec(fs.Name, &fs.Spec, specPath)...)
  64. if mand, ok := internalbootstrap.MandatoryFlowSchemas[fs.Name]; ok {
  65. // Check for almost exact equality. This is a pretty
  66. // strict test, and it is OK in this context because both
  67. // sides of this comparison are intended to ultimately
  68. // come from the same code.
  69. if !apiequality.Semantic.DeepEqual(fs.Spec, mand.Spec) {
  70. allErrs = append(allErrs, field.Invalid(specPath, fs.Spec, fmt.Sprintf("spec of '%s' must equal the fixed value", fs.Name)))
  71. }
  72. }
  73. allErrs = append(allErrs, ValidateFlowSchemaStatus(&fs.Status, field.NewPath("status"))...)
  74. return allErrs
  75. }
  76. // ValidateFlowSchemaUpdate validates the update of flow-schema
  77. func ValidateFlowSchemaUpdate(old, fs *flowcontrol.FlowSchema) field.ErrorList {
  78. return ValidateFlowSchema(fs)
  79. }
  80. // ValidateFlowSchemaSpec validates the content of flow-schema's spec
  81. func ValidateFlowSchemaSpec(fsName string, spec *flowcontrol.FlowSchemaSpec, fldPath *field.Path) field.ErrorList {
  82. var allErrs field.ErrorList
  83. if spec.MatchingPrecedence <= 0 {
  84. allErrs = append(allErrs, field.Invalid(fldPath.Child("matchingPrecedence"), spec.MatchingPrecedence, "must be a positive value"))
  85. }
  86. if spec.MatchingPrecedence > flowcontrol.FlowSchemaMaxMatchingPrecedence {
  87. allErrs = append(allErrs, field.Invalid(fldPath.Child("matchingPrecedence"), spec.MatchingPrecedence, fmt.Sprintf("must not be greater than %v", flowcontrol.FlowSchemaMaxMatchingPrecedence)))
  88. }
  89. if (spec.MatchingPrecedence == 1) && (fsName != flowcontrol.FlowSchemaNameExempt) {
  90. allErrs = append(allErrs, field.Invalid(fldPath.Child("matchingPrecedence"), spec.MatchingPrecedence, "only the schema named 'exempt' may have matchingPrecedence 1"))
  91. }
  92. if spec.DistinguisherMethod != nil {
  93. if !supportedDistinguisherMethods.Has(string(spec.DistinguisherMethod.Type)) {
  94. allErrs = append(allErrs, field.NotSupported(fldPath.Child("distinguisherMethod").Child("type"), spec.DistinguisherMethod, supportedDistinguisherMethods.List()))
  95. }
  96. }
  97. if len(spec.PriorityLevelConfiguration.Name) > 0 {
  98. for _, msg := range ValidatePriorityLevelConfigurationName(spec.PriorityLevelConfiguration.Name, false) {
  99. allErrs = append(allErrs, field.Invalid(fldPath.Child("priorityLevelConfiguration").Child("name"), spec.PriorityLevelConfiguration.Name, msg))
  100. }
  101. } else {
  102. allErrs = append(allErrs, field.Required(fldPath.Child("priorityLevelConfiguration").Child("name"), "must reference a priority level"))
  103. }
  104. for i, rule := range spec.Rules {
  105. allErrs = append(allErrs, ValidateFlowSchemaPolicyRulesWithSubjects(&rule, fldPath.Child("rules").Index(i))...)
  106. }
  107. return allErrs
  108. }
  109. // ValidateFlowSchemaPolicyRulesWithSubjects validates policy-rule-with-subjects object.
  110. func ValidateFlowSchemaPolicyRulesWithSubjects(rule *flowcontrol.PolicyRulesWithSubjects, fldPath *field.Path) field.ErrorList {
  111. var allErrs field.ErrorList
  112. if len(rule.Subjects) > 0 {
  113. for i, subject := range rule.Subjects {
  114. allErrs = append(allErrs, ValidateFlowSchemaSubject(&subject, fldPath.Child("subjects").Index(i))...)
  115. }
  116. } else {
  117. allErrs = append(allErrs, field.Required(fldPath.Child("subjects"), "subjects must contain at least one value"))
  118. }
  119. if len(rule.ResourceRules) == 0 && len(rule.NonResourceRules) == 0 {
  120. allErrs = append(allErrs, field.Required(fldPath, "at least one of resourceRules and nonResourceRules has to be non-empty"))
  121. }
  122. for i, resourceRule := range rule.ResourceRules {
  123. allErrs = append(allErrs, ValidateFlowSchemaResourcePolicyRule(&resourceRule, fldPath.Child("resourceRules").Index(i))...)
  124. }
  125. for i, nonResourceRule := range rule.NonResourceRules {
  126. allErrs = append(allErrs, ValidateFlowSchemaNonResourcePolicyRule(&nonResourceRule, fldPath.Child("nonResourceRules").Index(i))...)
  127. }
  128. return allErrs
  129. }
  130. // ValidateFlowSchemaSubject validates flow-schema's subject object.
  131. func ValidateFlowSchemaSubject(subject *flowcontrol.Subject, fldPath *field.Path) field.ErrorList {
  132. var allErrs field.ErrorList
  133. switch subject.Kind {
  134. case flowcontrol.SubjectKindServiceAccount:
  135. allErrs = append(allErrs, ValidateServiceAccountSubject(subject.ServiceAccount, fldPath.Child("serviceAccount"))...)
  136. if subject.User != nil {
  137. allErrs = append(allErrs, field.Forbidden(fldPath.Child("user"), "user is forbidden when subject kind is not 'User'"))
  138. }
  139. if subject.Group != nil {
  140. allErrs = append(allErrs, field.Forbidden(fldPath.Child("group"), "group is forbidden when subject kind is not 'Group'"))
  141. }
  142. case flowcontrol.SubjectKindUser:
  143. allErrs = append(allErrs, ValidateUserSubject(subject.User, fldPath.Child("user"))...)
  144. if subject.ServiceAccount != nil {
  145. allErrs = append(allErrs, field.Forbidden(fldPath.Child("serviceAccount"), "serviceAccount is forbidden when subject kind is not 'ServiceAccount'"))
  146. }
  147. if subject.Group != nil {
  148. allErrs = append(allErrs, field.Forbidden(fldPath.Child("group"), "group is forbidden when subject kind is not 'Group'"))
  149. }
  150. case flowcontrol.SubjectKindGroup:
  151. allErrs = append(allErrs, ValidateGroupSubject(subject.Group, fldPath.Child("group"))...)
  152. if subject.ServiceAccount != nil {
  153. allErrs = append(allErrs, field.Forbidden(fldPath.Child("serviceAccount"), "serviceAccount is forbidden when subject kind is not 'ServiceAccount'"))
  154. }
  155. if subject.User != nil {
  156. allErrs = append(allErrs, field.Forbidden(fldPath.Child("user"), "user is forbidden when subject kind is not 'User'"))
  157. }
  158. default:
  159. allErrs = append(allErrs, field.NotSupported(fldPath.Child("kind"), subject.Kind, supportedSubjectKinds.List()))
  160. }
  161. return allErrs
  162. }
  163. // ValidateServiceAccountSubject validates subject of "ServiceAccount" kind
  164. func ValidateServiceAccountSubject(subject *flowcontrol.ServiceAccountSubject, fldPath *field.Path) field.ErrorList {
  165. var allErrs field.ErrorList
  166. if subject == nil {
  167. return append(allErrs, field.Required(fldPath, "serviceAccount is required when subject kind is 'ServiceAccount'"))
  168. }
  169. if len(subject.Name) == 0 {
  170. allErrs = append(allErrs, field.Required(fldPath.Child("name"), ""))
  171. } else if subject.Name != flowcontrol.NameAll {
  172. for _, msg := range apimachineryvalidation.ValidateServiceAccountName(subject.Name, false) {
  173. allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), subject.Name, msg))
  174. }
  175. }
  176. if len(subject.Namespace) > 0 {
  177. for _, msg := range apimachineryvalidation.ValidateNamespaceName(subject.Namespace, false) {
  178. allErrs = append(allErrs, field.Invalid(fldPath.Child("namespace"), subject.Namespace, msg))
  179. }
  180. } else {
  181. allErrs = append(allErrs, field.Required(fldPath.Child("namespace"), "must specify namespace for service account"))
  182. }
  183. return allErrs
  184. }
  185. // ValidateUserSubject validates subject of "User" kind
  186. func ValidateUserSubject(subject *flowcontrol.UserSubject, fldPath *field.Path) field.ErrorList {
  187. var allErrs field.ErrorList
  188. if subject == nil {
  189. return append(allErrs, field.Required(fldPath, "user is required when subject kind is 'User'"))
  190. }
  191. if len(subject.Name) == 0 {
  192. allErrs = append(allErrs, field.Required(fldPath.Child("name"), ""))
  193. }
  194. return allErrs
  195. }
  196. // ValidateGroupSubject validates subject of "Group" kind
  197. func ValidateGroupSubject(subject *flowcontrol.GroupSubject, fldPath *field.Path) field.ErrorList {
  198. var allErrs field.ErrorList
  199. if subject == nil {
  200. return append(allErrs, field.Required(fldPath, "group is required when subject kind is 'Group'"))
  201. }
  202. if len(subject.Name) == 0 {
  203. allErrs = append(allErrs, field.Required(fldPath.Child("name"), ""))
  204. }
  205. return allErrs
  206. }
  207. // ValidateFlowSchemaNonResourcePolicyRule validates non-resource policy-rule in the flow-schema.
  208. func ValidateFlowSchemaNonResourcePolicyRule(rule *flowcontrol.NonResourcePolicyRule, fldPath *field.Path) field.ErrorList {
  209. var allErrs field.ErrorList
  210. if len(rule.Verbs) == 0 {
  211. allErrs = append(allErrs, field.Required(fldPath.Child("verbs"), "verbs must contain at least one value"))
  212. } else if hasWildcard(rule.Verbs) {
  213. if len(rule.Verbs) > 1 {
  214. allErrs = append(allErrs, field.Invalid(fldPath.Child("verbs"), rule.Verbs, "if '*' is present, must not specify other verbs"))
  215. }
  216. } else if !supportedVerbs.IsSuperset(sets.NewString(rule.Verbs...)) {
  217. // only supported verbs are allowed
  218. allErrs = append(allErrs, field.NotSupported(fldPath.Child("verbs"), rule.Verbs, supportedVerbs.List()))
  219. }
  220. if len(rule.NonResourceURLs) == 0 {
  221. allErrs = append(allErrs, field.Required(fldPath.Child("nonResourceURLs"), "nonResourceURLs must contain at least one value"))
  222. } else if hasWildcard(rule.NonResourceURLs) {
  223. if len(rule.NonResourceURLs) > 1 {
  224. allErrs = append(allErrs, field.Invalid(fldPath.Child("nonResourceURLs"), rule.NonResourceURLs, "if '*' is present, must not specify other non-resource URLs"))
  225. }
  226. } else {
  227. for i, nonResourceURL := range rule.NonResourceURLs {
  228. if err := ValidateNonResourceURLPath(nonResourceURL, fldPath.Child("nonResourceURLs").Index(i)); err != nil {
  229. allErrs = append(allErrs, err)
  230. }
  231. }
  232. }
  233. return allErrs
  234. }
  235. // ValidateFlowSchemaResourcePolicyRule validates resource policy-rule in the flow-schema.
  236. func ValidateFlowSchemaResourcePolicyRule(rule *flowcontrol.ResourcePolicyRule, fldPath *field.Path) field.ErrorList {
  237. var allErrs field.ErrorList
  238. if len(rule.Verbs) == 0 {
  239. allErrs = append(allErrs, field.Required(fldPath.Child("verbs"), "verbs must contain at least one value"))
  240. } else if hasWildcard(rule.Verbs) {
  241. if len(rule.Verbs) > 1 {
  242. allErrs = append(allErrs, field.Invalid(fldPath.Child("verbs"), rule.Verbs, "if '*' is present, must not specify other verbs"))
  243. }
  244. } else if !supportedVerbs.IsSuperset(sets.NewString(rule.Verbs...)) {
  245. // only supported verbs are allowed
  246. allErrs = append(allErrs, field.NotSupported(fldPath.Child("verbs"), rule.Verbs, supportedVerbs.List()))
  247. }
  248. if len(rule.APIGroups) == 0 {
  249. allErrs = append(allErrs, field.Required(fldPath.Child("apiGroups"), "resource rules must supply at least one api group"))
  250. } else if len(rule.APIGroups) > 1 && hasWildcard(rule.APIGroups) {
  251. allErrs = append(allErrs, field.Invalid(fldPath.Child("apiGroups"), rule.APIGroups, "if '*' is present, must not specify other api groups"))
  252. }
  253. if len(rule.Resources) == 0 {
  254. allErrs = append(allErrs, field.Required(fldPath.Child("resources"), "resource rules must supply at least one resource"))
  255. } else if len(rule.Resources) > 1 && hasWildcard(rule.Resources) {
  256. allErrs = append(allErrs, field.Invalid(fldPath.Child("resources"), rule.Resources, "if '*' is present, must not specify other resources"))
  257. }
  258. if len(rule.Namespaces) == 0 && !rule.ClusterScope {
  259. allErrs = append(allErrs, field.Required(fldPath.Child("namespaces"), "resource rules that are not cluster scoped must supply at least one namespace"))
  260. } else if hasWildcard(rule.Namespaces) {
  261. if len(rule.Namespaces) > 1 {
  262. allErrs = append(allErrs, field.Invalid(fldPath.Child("namespaces"), rule.Namespaces, "if '*' is present, must not specify other namespaces"))
  263. }
  264. } else {
  265. for idx, tgtNS := range rule.Namespaces {
  266. for _, msg := range apimachineryvalidation.ValidateNamespaceName(tgtNS, false) {
  267. allErrs = append(allErrs, field.Invalid(fldPath.Child("namespaces").Index(idx), tgtNS, nsErrIntro+msg))
  268. }
  269. }
  270. }
  271. return allErrs
  272. }
  273. const nsErrIntro = "each member of this list must be '*' or a DNS-1123 label; "
  274. // ValidateFlowSchemaStatus validates status for the flow-schema.
  275. func ValidateFlowSchemaStatus(status *flowcontrol.FlowSchemaStatus, fldPath *field.Path) field.ErrorList {
  276. var allErrs field.ErrorList
  277. keys := sets.NewString()
  278. for i, condition := range status.Conditions {
  279. if keys.Has(string(condition.Type)) {
  280. allErrs = append(allErrs, field.Duplicate(fldPath.Child("conditions").Index(i).Child("type"), condition.Type))
  281. }
  282. keys.Insert(string(condition.Type))
  283. allErrs = append(allErrs, ValidateFlowSchemaCondition(&condition, fldPath.Child("conditions").Index(i))...)
  284. }
  285. return allErrs
  286. }
  287. // ValidateFlowSchemaStatusUpdate validates the update of status for the flow-schema.
  288. func ValidateFlowSchemaStatusUpdate(old, fs *flowcontrol.FlowSchema) field.ErrorList {
  289. return ValidateFlowSchemaStatus(&fs.Status, field.NewPath("status"))
  290. }
  291. // ValidateFlowSchemaCondition validates condition in the flow-schema's status.
  292. func ValidateFlowSchemaCondition(condition *flowcontrol.FlowSchemaCondition, fldPath *field.Path) field.ErrorList {
  293. var allErrs field.ErrorList
  294. if len(condition.Type) == 0 {
  295. allErrs = append(allErrs, field.Required(fldPath.Child("type"), "must not be empty"))
  296. }
  297. return allErrs
  298. }
  299. // ValidatePriorityLevelConfiguration validates priority-level-configuration.
  300. func ValidatePriorityLevelConfiguration(pl *flowcontrol.PriorityLevelConfiguration) field.ErrorList {
  301. allErrs := apivalidation.ValidateObjectMeta(&pl.ObjectMeta, false, ValidatePriorityLevelConfigurationName, field.NewPath("metadata"))
  302. specPath := field.NewPath("spec")
  303. allErrs = append(allErrs, ValidatePriorityLevelConfigurationSpec(&pl.Spec, pl.Name, specPath)...)
  304. if mand, ok := internalbootstrap.MandatoryPriorityLevelConfigurations[pl.Name]; ok {
  305. // Check for almost exact equality. This is a pretty
  306. // strict test, and it is OK in this context because both
  307. // sides of this comparison are intended to ultimately
  308. // come from the same code.
  309. if !apiequality.Semantic.DeepEqual(pl.Spec, mand.Spec) {
  310. allErrs = append(allErrs, field.Invalid(specPath, pl.Spec, fmt.Sprintf("spec of '%s' must equal the fixed value", pl.Name)))
  311. }
  312. }
  313. allErrs = append(allErrs, ValidatePriorityLevelConfigurationStatus(&pl.Status, field.NewPath("status"))...)
  314. return allErrs
  315. }
  316. // ValidatePriorityLevelConfigurationUpdate validates the update of priority-level-configuration.
  317. func ValidatePriorityLevelConfigurationUpdate(old, pl *flowcontrol.PriorityLevelConfiguration) field.ErrorList {
  318. return ValidatePriorityLevelConfiguration(pl)
  319. }
  320. // ValidatePriorityLevelConfigurationSpec validates priority-level-configuration's spec.
  321. func ValidatePriorityLevelConfigurationSpec(spec *flowcontrol.PriorityLevelConfigurationSpec, name string, fldPath *field.Path) field.ErrorList {
  322. var allErrs field.ErrorList
  323. if (name == flowcontrol.PriorityLevelConfigurationNameExempt) != (spec.Type == flowcontrol.PriorityLevelEnablementExempt) {
  324. allErrs = append(allErrs, field.Invalid(fldPath.Child("type"), spec.Type, "type must be 'Exempt' if and only if name is 'exempt'"))
  325. }
  326. switch spec.Type {
  327. case flowcontrol.PriorityLevelEnablementExempt:
  328. if spec.Limited != nil {
  329. allErrs = append(allErrs, field.Forbidden(fldPath.Child("limited"), "must be nil if the type is not Limited"))
  330. }
  331. case flowcontrol.PriorityLevelEnablementLimited:
  332. if spec.Limited == nil {
  333. allErrs = append(allErrs, field.Required(fldPath.Child("limited"), "must not be empty when type is Limited"))
  334. } else {
  335. allErrs = append(allErrs, ValidateLimitedPriorityLevelConfiguration(spec.Limited, fldPath.Child("limited"))...)
  336. }
  337. default:
  338. allErrs = append(allErrs, field.NotSupported(fldPath.Child("type"), spec.Type, supportedPriorityLevelEnablement.List()))
  339. }
  340. return allErrs
  341. }
  342. // ValidateLimitedPriorityLevelConfiguration validates the configuration for an execution-limited priority level
  343. func ValidateLimitedPriorityLevelConfiguration(lplc *flowcontrol.LimitedPriorityLevelConfiguration, fldPath *field.Path) field.ErrorList {
  344. var allErrs field.ErrorList
  345. if lplc.AssuredConcurrencyShares <= 0 {
  346. allErrs = append(allErrs, field.Invalid(fldPath.Child("assuredConcurrencyShares"), lplc.AssuredConcurrencyShares, "must be positive"))
  347. }
  348. allErrs = append(allErrs, ValidateLimitResponse(lplc.LimitResponse, fldPath.Child("limitResponse"))...)
  349. return allErrs
  350. }
  351. // ValidateLimitResponse validates a LimitResponse
  352. func ValidateLimitResponse(lr flowcontrol.LimitResponse, fldPath *field.Path) field.ErrorList {
  353. var allErrs field.ErrorList
  354. switch lr.Type {
  355. case flowcontrol.LimitResponseTypeReject:
  356. if lr.Queuing != nil {
  357. allErrs = append(allErrs, field.Forbidden(fldPath.Child("queuing"), "must be nil if limited.limitResponse.type is not Limited"))
  358. }
  359. case flowcontrol.LimitResponseTypeQueue:
  360. if lr.Queuing == nil {
  361. allErrs = append(allErrs, field.Required(fldPath.Child("queuing"), "must not be empty if limited.limitResponse.type is Limited"))
  362. } else {
  363. allErrs = append(allErrs, ValidatePriorityLevelQueuingConfiguration(lr.Queuing, fldPath.Child("queuing"))...)
  364. }
  365. default:
  366. allErrs = append(allErrs, field.NotSupported(fldPath.Child("type"), lr.Type, supportedLimitResponseType.List()))
  367. }
  368. return allErrs
  369. }
  370. // ValidatePriorityLevelQueuingConfiguration validates queuing-configuration for a priority-level
  371. func ValidatePriorityLevelQueuingConfiguration(queuing *flowcontrol.QueuingConfiguration, fldPath *field.Path) field.ErrorList {
  372. var allErrs field.ErrorList
  373. if queuing.QueueLengthLimit <= 0 {
  374. allErrs = append(allErrs, field.Invalid(fldPath.Child("queueLengthLimit"), queuing.QueueLengthLimit, "must be positive"))
  375. }
  376. // validate input arguments for shuffle-sharding
  377. if queuing.Queues <= 0 {
  378. allErrs = append(allErrs, field.Invalid(fldPath.Child("queues"), queuing.Queues, "must be positive"))
  379. } else if queuing.Queues > priorityLevelConfigurationQueuingMaxQueues {
  380. allErrs = append(allErrs, field.Invalid(fldPath.Child("queues"), queuing.Queues,
  381. fmt.Sprintf("must not be greater than %d", priorityLevelConfigurationQueuingMaxQueues)))
  382. }
  383. if queuing.HandSize <= 0 {
  384. allErrs = append(allErrs, field.Invalid(fldPath.Child("handSize"), queuing.HandSize, "must be positive"))
  385. } else if queuing.HandSize > queuing.Queues {
  386. allErrs = append(allErrs, field.Invalid(fldPath.Child("handSize"), queuing.HandSize,
  387. fmt.Sprintf("should not be greater than queues (%d)", queuing.Queues)))
  388. } else if entropy := shufflesharding.RequiredEntropyBits(int(queuing.Queues), int(queuing.HandSize)); entropy > shufflesharding.MaxHashBits {
  389. allErrs = append(allErrs, field.Invalid(fldPath.Child("handSize"), queuing.HandSize,
  390. fmt.Sprintf("required entropy bits of deckSize %d and handSize %d should not be greater than %d", queuing.Queues, queuing.HandSize, shufflesharding.MaxHashBits)))
  391. }
  392. return allErrs
  393. }
  394. // ValidatePriorityLevelConfigurationStatus validates priority-level-configuration's status.
  395. func ValidatePriorityLevelConfigurationStatus(status *flowcontrol.PriorityLevelConfigurationStatus, fldPath *field.Path) field.ErrorList {
  396. var allErrs field.ErrorList
  397. keys := sets.NewString()
  398. for i, condition := range status.Conditions {
  399. if keys.Has(string(condition.Type)) {
  400. allErrs = append(allErrs, field.Duplicate(fldPath.Child("conditions").Index(i).Child("type"), condition.Type))
  401. }
  402. keys.Insert(string(condition.Type))
  403. allErrs = append(allErrs, ValidatePriorityLevelConfigurationCondition(&condition, fldPath.Child("conditions").Index(i))...)
  404. }
  405. return allErrs
  406. }
  407. // ValidatePriorityLevelConfigurationStatusUpdate validates the update of priority-level-configuration's status.
  408. func ValidatePriorityLevelConfigurationStatusUpdate(old, pl *flowcontrol.PriorityLevelConfiguration) field.ErrorList {
  409. return ValidatePriorityLevelConfigurationStatus(&pl.Status, field.NewPath("status"))
  410. }
  411. // ValidatePriorityLevelConfigurationCondition validates condition in priority-level-configuration's status.
  412. func ValidatePriorityLevelConfigurationCondition(condition *flowcontrol.PriorityLevelConfigurationCondition, fldPath *field.Path) field.ErrorList {
  413. var allErrs field.ErrorList
  414. if len(condition.Type) == 0 {
  415. allErrs = append(allErrs, field.Required(fldPath.Child("type"), "must not be empty"))
  416. }
  417. return allErrs
  418. }
  419. // ValidateNonResourceURLPath validates non-resource-url path by following rules:
  420. // 1. Slash must be the leading character of the path
  421. // 2. White-space is forbidden in the path
  422. // 3. Continuous/double slash is forbidden in the path
  423. // 4. Wildcard "*" should only do suffix glob matching. Note that wildcard also matches slashes.
  424. func ValidateNonResourceURLPath(path string, fldPath *field.Path) *field.Error {
  425. if len(path) == 0 {
  426. return field.Invalid(fldPath, path, "must not be empty")
  427. }
  428. if path == "/" { // root path
  429. return nil
  430. }
  431. if !strings.HasPrefix(path, "/") {
  432. return field.Invalid(fldPath, path, "must start with slash")
  433. }
  434. if strings.Contains(path, " ") {
  435. return field.Invalid(fldPath, path, "must not contain white-space")
  436. }
  437. if strings.Contains(path, "//") {
  438. return field.Invalid(fldPath, path, "must not contain double slash")
  439. }
  440. wildcardCount := strings.Count(path, "*")
  441. if wildcardCount > 1 || (wildcardCount == 1 && path[len(path)-2:] != "/*") {
  442. return field.Invalid(fldPath, path, "wildcard can only do suffix matching")
  443. }
  444. return nil
  445. }
  446. func hasWildcard(operations []string) bool {
  447. return memberInList("*", operations...)
  448. }
  449. func memberInList(seek string, a ...string) bool {
  450. for _, ai := range a {
  451. if ai == seek {
  452. return true
  453. }
  454. }
  455. return false
  456. }