validation_test.go 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796
  1. /*
  2. Copyright 2017 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. "strings"
  16. "testing"
  17. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  18. "k8s.io/kubernetes/pkg/apis/admissionregistration"
  19. )
  20. func strPtr(s string) *string { return &s }
  21. func int32Ptr(i int32) *int32 { return &i }
  22. func newValidatingWebhookConfiguration(hooks []admissionregistration.ValidatingWebhook, defaultAdmissionReviewVersions bool) *admissionregistration.ValidatingWebhookConfiguration {
  23. // If the test case did not specify an AdmissionReviewVersions, default it so the test passes as
  24. // this field will be defaulted in production code.
  25. for i := range hooks {
  26. if defaultAdmissionReviewVersions && len(hooks[i].AdmissionReviewVersions) == 0 {
  27. hooks[i].AdmissionReviewVersions = []string{"v1beta1"}
  28. }
  29. }
  30. return &admissionregistration.ValidatingWebhookConfiguration{
  31. ObjectMeta: metav1.ObjectMeta{
  32. Name: "config",
  33. },
  34. Webhooks: hooks,
  35. }
  36. }
  37. // TODO: Add TestValidateMutatingWebhookConfiguration to test validation for mutating webhooks.
  38. func TestValidateValidatingWebhookConfiguration(t *testing.T) {
  39. validClientConfig := admissionregistration.WebhookClientConfig{
  40. URL: strPtr("https://example.com"),
  41. }
  42. tests := []struct {
  43. name string
  44. config *admissionregistration.ValidatingWebhookConfiguration
  45. expectedError string
  46. }{
  47. {
  48. name: "should fail on bad AdmissionReviewVersion value",
  49. config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
  50. {
  51. Name: "webhook.k8s.io",
  52. ClientConfig: validClientConfig,
  53. AdmissionReviewVersions: []string{"0v"},
  54. },
  55. }, true),
  56. expectedError: `Invalid value: "0v": a DNS-1035 label`,
  57. },
  58. {
  59. name: "should pass on valid AdmissionReviewVersion",
  60. config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
  61. {
  62. Name: "webhook.k8s.io",
  63. ClientConfig: validClientConfig,
  64. AdmissionReviewVersions: []string{"v1beta1"},
  65. },
  66. }, true),
  67. expectedError: ``,
  68. },
  69. {
  70. name: "should pass on mix of accepted and unaccepted AdmissionReviewVersion",
  71. config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
  72. {
  73. Name: "webhook.k8s.io",
  74. ClientConfig: validClientConfig,
  75. AdmissionReviewVersions: []string{"v1beta1", "invalid-version"},
  76. },
  77. }, true),
  78. expectedError: ``,
  79. },
  80. {
  81. name: "should fail on invalid AdmissionReviewVersion",
  82. config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
  83. {
  84. Name: "webhook.k8s.io",
  85. ClientConfig: validClientConfig,
  86. AdmissionReviewVersions: []string{"invalidVersion"},
  87. },
  88. }, true),
  89. expectedError: `Invalid value: []string{"invalidVersion"}`,
  90. },
  91. {
  92. name: "should fail on duplicate AdmissionReviewVersion",
  93. config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
  94. {
  95. Name: "webhook.k8s.io",
  96. ClientConfig: validClientConfig,
  97. AdmissionReviewVersions: []string{"v1beta1", "v1beta1"},
  98. },
  99. }, true),
  100. expectedError: `Invalid value: "v1beta1": duplicate version`,
  101. },
  102. {
  103. name: "all Webhooks must have a fully qualified name",
  104. config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
  105. {
  106. Name: "webhook.k8s.io",
  107. ClientConfig: validClientConfig,
  108. },
  109. {
  110. Name: "k8s.io",
  111. ClientConfig: validClientConfig,
  112. },
  113. {
  114. Name: "",
  115. ClientConfig: validClientConfig,
  116. },
  117. }, true),
  118. expectedError: `webhooks[1].name: Invalid value: "k8s.io": should be a domain with at least three segments separated by dots, webhooks[2].name: Required value`,
  119. },
  120. {
  121. name: "Operations must not be empty or nil",
  122. config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
  123. {
  124. Name: "webhook.k8s.io",
  125. Rules: []admissionregistration.RuleWithOperations{
  126. {
  127. Operations: []admissionregistration.OperationType{},
  128. Rule: admissionregistration.Rule{
  129. APIGroups: []string{"a"},
  130. APIVersions: []string{"a"},
  131. Resources: []string{"a"},
  132. },
  133. },
  134. {
  135. Operations: nil,
  136. Rule: admissionregistration.Rule{
  137. APIGroups: []string{"a"},
  138. APIVersions: []string{"a"},
  139. Resources: []string{"a"},
  140. },
  141. },
  142. },
  143. },
  144. }, true),
  145. expectedError: `webhooks[0].rules[0].operations: Required value, webhooks[0].rules[1].operations: Required value`,
  146. },
  147. {
  148. name: "\"\" is NOT a valid operation",
  149. config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
  150. {
  151. Name: "webhook.k8s.io",
  152. Rules: []admissionregistration.RuleWithOperations{
  153. {
  154. Operations: []admissionregistration.OperationType{"CREATE", ""},
  155. Rule: admissionregistration.Rule{
  156. APIGroups: []string{"a"},
  157. APIVersions: []string{"a"},
  158. Resources: []string{"a"},
  159. },
  160. },
  161. },
  162. },
  163. }, true),
  164. expectedError: `Unsupported value: ""`,
  165. },
  166. {
  167. name: "operation must be either create/update/delete/connect",
  168. config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
  169. {
  170. Name: "webhook.k8s.io",
  171. Rules: []admissionregistration.RuleWithOperations{
  172. {
  173. Operations: []admissionregistration.OperationType{"PATCH"},
  174. Rule: admissionregistration.Rule{
  175. APIGroups: []string{"a"},
  176. APIVersions: []string{"a"},
  177. Resources: []string{"a"},
  178. },
  179. },
  180. },
  181. },
  182. }, true),
  183. expectedError: `Unsupported value: "PATCH"`,
  184. },
  185. {
  186. name: "wildcard operation cannot be mixed with other strings",
  187. config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
  188. {
  189. Name: "webhook.k8s.io",
  190. Rules: []admissionregistration.RuleWithOperations{
  191. {
  192. Operations: []admissionregistration.OperationType{"CREATE", "*"},
  193. Rule: admissionregistration.Rule{
  194. APIGroups: []string{"a"},
  195. APIVersions: []string{"a"},
  196. Resources: []string{"a"},
  197. },
  198. },
  199. },
  200. },
  201. }, true),
  202. expectedError: `if '*' is present, must not specify other operations`,
  203. },
  204. {
  205. name: `resource "*" can co-exist with resources that have subresources`,
  206. config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
  207. {
  208. Name: "webhook.k8s.io",
  209. ClientConfig: validClientConfig,
  210. Rules: []admissionregistration.RuleWithOperations{
  211. {
  212. Operations: []admissionregistration.OperationType{"CREATE"},
  213. Rule: admissionregistration.Rule{
  214. APIGroups: []string{"a"},
  215. APIVersions: []string{"a"},
  216. Resources: []string{"*", "a/b", "a/*", "*/b"},
  217. },
  218. },
  219. },
  220. },
  221. }, true),
  222. },
  223. {
  224. name: `resource "*" cannot mix with resources that don't have subresources`,
  225. config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
  226. {
  227. Name: "webhook.k8s.io",
  228. ClientConfig: validClientConfig,
  229. Rules: []admissionregistration.RuleWithOperations{
  230. {
  231. Operations: []admissionregistration.OperationType{"CREATE"},
  232. Rule: admissionregistration.Rule{
  233. APIGroups: []string{"a"},
  234. APIVersions: []string{"a"},
  235. Resources: []string{"*", "a"},
  236. },
  237. },
  238. },
  239. },
  240. }, true),
  241. expectedError: `if '*' is present, must not specify other resources without subresources`,
  242. },
  243. {
  244. name: "resource a/* cannot mix with a/x",
  245. config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
  246. {
  247. Name: "webhook.k8s.io",
  248. ClientConfig: validClientConfig,
  249. Rules: []admissionregistration.RuleWithOperations{
  250. {
  251. Operations: []admissionregistration.OperationType{"CREATE"},
  252. Rule: admissionregistration.Rule{
  253. APIGroups: []string{"a"},
  254. APIVersions: []string{"a"},
  255. Resources: []string{"a/*", "a/x"},
  256. },
  257. },
  258. },
  259. },
  260. }, true),
  261. expectedError: `webhooks[0].rules[0].resources[1]: Invalid value: "a/x": if 'a/*' is present, must not specify a/x`,
  262. },
  263. {
  264. name: "resource a/* can mix with a",
  265. config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
  266. {
  267. Name: "webhook.k8s.io",
  268. ClientConfig: validClientConfig,
  269. Rules: []admissionregistration.RuleWithOperations{
  270. {
  271. Operations: []admissionregistration.OperationType{"CREATE"},
  272. Rule: admissionregistration.Rule{
  273. APIGroups: []string{"a"},
  274. APIVersions: []string{"a"},
  275. Resources: []string{"a/*", "a"},
  276. },
  277. },
  278. },
  279. },
  280. }, true),
  281. },
  282. {
  283. name: "resource */a cannot mix with x/a",
  284. config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
  285. {
  286. Name: "webhook.k8s.io",
  287. ClientConfig: validClientConfig,
  288. Rules: []admissionregistration.RuleWithOperations{
  289. {
  290. Operations: []admissionregistration.OperationType{"CREATE"},
  291. Rule: admissionregistration.Rule{
  292. APIGroups: []string{"a"},
  293. APIVersions: []string{"a"},
  294. Resources: []string{"*/a", "x/a"},
  295. },
  296. },
  297. },
  298. },
  299. }, true),
  300. expectedError: `webhooks[0].rules[0].resources[1]: Invalid value: "x/a": if '*/a' is present, must not specify x/a`,
  301. },
  302. {
  303. name: "resource */* cannot mix with other resources",
  304. config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
  305. {
  306. Name: "webhook.k8s.io",
  307. ClientConfig: validClientConfig,
  308. Rules: []admissionregistration.RuleWithOperations{
  309. {
  310. Operations: []admissionregistration.OperationType{"CREATE"},
  311. Rule: admissionregistration.Rule{
  312. APIGroups: []string{"a"},
  313. APIVersions: []string{"a"},
  314. Resources: []string{"*/*", "a"},
  315. },
  316. },
  317. },
  318. },
  319. }, true),
  320. expectedError: `webhooks[0].rules[0].resources: Invalid value: []string{"*/*", "a"}: if '*/*' is present, must not specify other resources`,
  321. },
  322. {
  323. name: "FailurePolicy can only be \"Ignore\" or \"Fail\"",
  324. config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
  325. {
  326. Name: "webhook.k8s.io",
  327. ClientConfig: validClientConfig,
  328. FailurePolicy: func() *admissionregistration.FailurePolicyType {
  329. r := admissionregistration.FailurePolicyType("other")
  330. return &r
  331. }(),
  332. },
  333. }, true),
  334. expectedError: `webhooks[0].failurePolicy: Unsupported value: "other": supported values: "Fail", "Ignore"`,
  335. },
  336. {
  337. name: "SideEffects can only be \"Unknown\", \"None\", \"Some\", or \"NoneOnDryRun\"",
  338. config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
  339. {
  340. Name: "webhook.k8s.io",
  341. ClientConfig: validClientConfig,
  342. SideEffects: func() *admissionregistration.SideEffectClass {
  343. r := admissionregistration.SideEffectClass("other")
  344. return &r
  345. }(),
  346. },
  347. }, true),
  348. expectedError: `webhooks[0].sideEffects: Unsupported value: "other": supported values: "None", "NoneOnDryRun", "Some", "Unknown"`,
  349. },
  350. {
  351. name: "both service and URL missing",
  352. config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
  353. {
  354. Name: "webhook.k8s.io",
  355. ClientConfig: admissionregistration.WebhookClientConfig{},
  356. },
  357. }, true),
  358. expectedError: `exactly one of`,
  359. },
  360. {
  361. name: "both service and URL provided",
  362. config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
  363. {
  364. Name: "webhook.k8s.io",
  365. ClientConfig: admissionregistration.WebhookClientConfig{
  366. Service: &admissionregistration.ServiceReference{
  367. Namespace: "ns",
  368. Name: "n",
  369. Port: 443,
  370. },
  371. URL: strPtr("example.com/k8s/webhook"),
  372. },
  373. },
  374. }, true),
  375. expectedError: `[0].clientConfig: Required value: exactly one of url or service is required`,
  376. },
  377. {
  378. name: "blank URL",
  379. config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
  380. {
  381. Name: "webhook.k8s.io",
  382. ClientConfig: admissionregistration.WebhookClientConfig{
  383. URL: strPtr(""),
  384. },
  385. },
  386. }, true),
  387. expectedError: `[0].clientConfig.url: Invalid value: "": host must be provided`,
  388. },
  389. {
  390. name: "wrong scheme",
  391. config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
  392. {
  393. Name: "webhook.k8s.io",
  394. ClientConfig: admissionregistration.WebhookClientConfig{
  395. URL: strPtr("http://example.com"),
  396. },
  397. },
  398. }, true),
  399. expectedError: `https`,
  400. },
  401. {
  402. name: "missing host",
  403. config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
  404. {
  405. Name: "webhook.k8s.io",
  406. ClientConfig: admissionregistration.WebhookClientConfig{
  407. URL: strPtr("https:///fancy/webhook"),
  408. },
  409. },
  410. }, true),
  411. expectedError: `host must be provided`,
  412. },
  413. {
  414. name: "fragment",
  415. config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
  416. {
  417. Name: "webhook.k8s.io",
  418. ClientConfig: admissionregistration.WebhookClientConfig{
  419. URL: strPtr("https://example.com/#bookmark"),
  420. },
  421. },
  422. }, true),
  423. expectedError: `"bookmark": fragments are not permitted`,
  424. },
  425. {
  426. name: "query",
  427. config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
  428. {
  429. Name: "webhook.k8s.io",
  430. ClientConfig: admissionregistration.WebhookClientConfig{
  431. URL: strPtr("https://example.com?arg=value"),
  432. },
  433. },
  434. }, true),
  435. expectedError: `"arg=value": query parameters are not permitted`,
  436. },
  437. {
  438. name: "user",
  439. config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
  440. {
  441. Name: "webhook.k8s.io",
  442. ClientConfig: admissionregistration.WebhookClientConfig{
  443. URL: strPtr("https://harry.potter@example.com/"),
  444. },
  445. },
  446. }, true),
  447. expectedError: `"harry.potter": user information is not permitted`,
  448. },
  449. {
  450. name: "just totally wrong",
  451. config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
  452. {
  453. Name: "webhook.k8s.io",
  454. ClientConfig: admissionregistration.WebhookClientConfig{
  455. URL: strPtr("arg#backwards=thisis?html.index/port:host//:https"),
  456. },
  457. },
  458. }, true),
  459. expectedError: `host must be provided`,
  460. },
  461. {
  462. name: "path must start with slash",
  463. config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
  464. {
  465. Name: "webhook.k8s.io",
  466. ClientConfig: admissionregistration.WebhookClientConfig{
  467. Service: &admissionregistration.ServiceReference{
  468. Namespace: "ns",
  469. Name: "n",
  470. Path: strPtr("foo/"),
  471. Port: 443,
  472. },
  473. },
  474. },
  475. }, true),
  476. expectedError: `clientConfig.service.path: Invalid value: "foo/": must start with a '/'`,
  477. },
  478. {
  479. name: "path accepts slash",
  480. config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
  481. {
  482. Name: "webhook.k8s.io",
  483. ClientConfig: admissionregistration.WebhookClientConfig{
  484. Service: &admissionregistration.ServiceReference{
  485. Namespace: "ns",
  486. Name: "n",
  487. Path: strPtr("/"),
  488. Port: 443,
  489. },
  490. },
  491. },
  492. }, true),
  493. expectedError: ``,
  494. },
  495. {
  496. name: "path accepts no trailing slash",
  497. config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
  498. {
  499. Name: "webhook.k8s.io",
  500. ClientConfig: admissionregistration.WebhookClientConfig{
  501. Service: &admissionregistration.ServiceReference{
  502. Namespace: "ns",
  503. Name: "n",
  504. Path: strPtr("/foo"),
  505. Port: 443,
  506. },
  507. },
  508. },
  509. }, true),
  510. expectedError: ``,
  511. },
  512. {
  513. name: "path fails //",
  514. config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
  515. {
  516. Name: "webhook.k8s.io",
  517. ClientConfig: admissionregistration.WebhookClientConfig{
  518. Service: &admissionregistration.ServiceReference{
  519. Namespace: "ns",
  520. Name: "n",
  521. Path: strPtr("//"),
  522. Port: 443,
  523. },
  524. },
  525. },
  526. }, true),
  527. expectedError: `clientConfig.service.path: Invalid value: "//": segment[0] may not be empty`,
  528. },
  529. {
  530. name: "path no empty step",
  531. config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
  532. {
  533. Name: "webhook.k8s.io",
  534. ClientConfig: admissionregistration.WebhookClientConfig{
  535. Service: &admissionregistration.ServiceReference{
  536. Namespace: "ns",
  537. Name: "n",
  538. Path: strPtr("/foo//bar/"),
  539. Port: 443,
  540. },
  541. },
  542. },
  543. }, true),
  544. expectedError: `clientConfig.service.path: Invalid value: "/foo//bar/": segment[1] may not be empty`,
  545. }, {
  546. name: "path no empty step 2",
  547. config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
  548. {
  549. Name: "webhook.k8s.io",
  550. ClientConfig: admissionregistration.WebhookClientConfig{
  551. Service: &admissionregistration.ServiceReference{
  552. Namespace: "ns",
  553. Name: "n",
  554. Path: strPtr("/foo/bar//"),
  555. Port: 443,
  556. },
  557. },
  558. },
  559. }, true),
  560. expectedError: `clientConfig.service.path: Invalid value: "/foo/bar//": segment[2] may not be empty`,
  561. },
  562. {
  563. name: "path no non-subdomain",
  564. config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
  565. {
  566. Name: "webhook.k8s.io",
  567. ClientConfig: admissionregistration.WebhookClientConfig{
  568. Service: &admissionregistration.ServiceReference{
  569. Namespace: "ns",
  570. Name: "n",
  571. Path: strPtr("/apis/foo.bar/v1alpha1/--bad"),
  572. Port: 443,
  573. },
  574. },
  575. },
  576. }, true),
  577. expectedError: `clientConfig.service.path: Invalid value: "/apis/foo.bar/v1alpha1/--bad": segment[3]: a DNS-1123 subdomain`,
  578. },
  579. {
  580. name: "invalid port 0",
  581. config: newValidatingWebhookConfiguration(
  582. []admissionregistration.ValidatingWebhook{
  583. {
  584. Name: "webhook.k8s.io",
  585. ClientConfig: admissionregistration.WebhookClientConfig{
  586. Service: &admissionregistration.ServiceReference{
  587. Namespace: "ns",
  588. Name: "n",
  589. Path: strPtr("https://apis/foo.bar"),
  590. Port: 0,
  591. },
  592. },
  593. },
  594. }, true),
  595. expectedError: `Invalid value: 0: port is not valid: must be between 1 and 65535, inclusive`,
  596. },
  597. {
  598. name: "invalid port >65535",
  599. config: newValidatingWebhookConfiguration(
  600. []admissionregistration.ValidatingWebhook{
  601. {
  602. Name: "webhook.k8s.io",
  603. ClientConfig: admissionregistration.WebhookClientConfig{
  604. Service: &admissionregistration.ServiceReference{
  605. Namespace: "ns",
  606. Name: "n",
  607. Path: strPtr("https://apis/foo.bar"),
  608. Port: 65536,
  609. },
  610. },
  611. },
  612. }, true),
  613. expectedError: `Invalid value: 65536: port is not valid: must be between 1 and 65535, inclusive`,
  614. },
  615. {
  616. name: "timeout seconds cannot be greater than 30",
  617. config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
  618. {
  619. Name: "webhook.k8s.io",
  620. ClientConfig: validClientConfig,
  621. TimeoutSeconds: int32Ptr(31),
  622. },
  623. }, true),
  624. expectedError: `webhooks[0].timeoutSeconds: Invalid value: 31: the timeout value must be between 1 and 30 seconds`,
  625. },
  626. {
  627. name: "timeout seconds cannot be smaller than 1",
  628. config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
  629. {
  630. Name: "webhook.k8s.io",
  631. ClientConfig: validClientConfig,
  632. TimeoutSeconds: int32Ptr(0),
  633. },
  634. }, true),
  635. expectedError: `webhooks[0].timeoutSeconds: Invalid value: 0: the timeout value must be between 1 and 30 seconds`,
  636. },
  637. {
  638. name: "timeout seconds must be positive",
  639. config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
  640. {
  641. Name: "webhook.k8s.io",
  642. ClientConfig: validClientConfig,
  643. TimeoutSeconds: int32Ptr(-1),
  644. },
  645. }, true),
  646. expectedError: `webhooks[0].timeoutSeconds: Invalid value: -1: the timeout value must be between 1 and 30 seconds`,
  647. },
  648. {
  649. name: "valid timeout seconds",
  650. config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
  651. {
  652. Name: "webhook.k8s.io",
  653. ClientConfig: validClientConfig,
  654. TimeoutSeconds: int32Ptr(1),
  655. },
  656. {
  657. Name: "webhook2.k8s.io",
  658. ClientConfig: validClientConfig,
  659. TimeoutSeconds: int32Ptr(15),
  660. },
  661. {
  662. Name: "webhook3.k8s.io",
  663. ClientConfig: validClientConfig,
  664. TimeoutSeconds: int32Ptr(30),
  665. },
  666. }, true),
  667. },
  668. }
  669. for _, test := range tests {
  670. t.Run(test.name, func(t *testing.T) {
  671. errs := ValidateValidatingWebhookConfiguration(test.config)
  672. err := errs.ToAggregate()
  673. if err != nil {
  674. if e, a := test.expectedError, err.Error(); !strings.Contains(a, e) || e == "" {
  675. t.Errorf("expected to contain %s, got %s", e, a)
  676. }
  677. } else {
  678. if test.expectedError != "" {
  679. t.Errorf("unexpected no error, expected to contain %s", test.expectedError)
  680. }
  681. }
  682. })
  683. }
  684. }
  685. func TestValidateValidatingWebhookConfigurationUpdate(t *testing.T) {
  686. validClientConfig := admissionregistration.WebhookClientConfig{
  687. URL: strPtr("https://example.com"),
  688. }
  689. tests := []struct {
  690. name string
  691. oldconfig *admissionregistration.ValidatingWebhookConfiguration
  692. config *admissionregistration.ValidatingWebhookConfiguration
  693. expectedError string
  694. }{
  695. {
  696. name: "should pass on valid new AdmissionReviewVersion",
  697. config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
  698. {
  699. Name: "webhook.k8s.io",
  700. ClientConfig: validClientConfig,
  701. AdmissionReviewVersions: []string{"v1beta1"},
  702. },
  703. }, true),
  704. oldconfig: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
  705. {
  706. Name: "webhook.k8s.io",
  707. ClientConfig: validClientConfig,
  708. },
  709. }, true),
  710. expectedError: ``,
  711. },
  712. {
  713. name: "should pass on invalid AdmissionReviewVersion with invalid previous versions",
  714. config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
  715. {
  716. Name: "webhook.k8s.io",
  717. ClientConfig: validClientConfig,
  718. AdmissionReviewVersions: []string{"invalid-v1", "invalid-v2"},
  719. },
  720. }, true),
  721. oldconfig: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
  722. {
  723. Name: "webhook.k8s.io",
  724. ClientConfig: validClientConfig,
  725. AdmissionReviewVersions: []string{"invalid-v0"},
  726. },
  727. }, true),
  728. expectedError: ``,
  729. },
  730. {
  731. name: "should fail on invalid AdmissionReviewVersion with valid previous versions",
  732. config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
  733. {
  734. Name: "webhook.k8s.io",
  735. ClientConfig: validClientConfig,
  736. AdmissionReviewVersions: []string{"invalid-v1"},
  737. },
  738. }, true),
  739. oldconfig: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
  740. {
  741. Name: "webhook.k8s.io",
  742. ClientConfig: validClientConfig,
  743. AdmissionReviewVersions: []string{"v1beta1", "invalid-v1"},
  744. },
  745. }, true),
  746. expectedError: `Invalid value: []string{"invalid-v1"}`,
  747. },
  748. {
  749. name: "should fail on invalid AdmissionReviewVersion with missing previous versions",
  750. config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
  751. {
  752. Name: "webhook.k8s.io",
  753. ClientConfig: validClientConfig,
  754. AdmissionReviewVersions: []string{"invalid-v1"},
  755. },
  756. }, true),
  757. oldconfig: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
  758. {
  759. Name: "webhook.k8s.io",
  760. ClientConfig: validClientConfig,
  761. },
  762. }, false),
  763. expectedError: `Invalid value: []string{"invalid-v1"}`,
  764. },
  765. }
  766. for _, test := range tests {
  767. t.Run(test.name, func(t *testing.T) {
  768. errs := ValidateValidatingWebhookConfigurationUpdate(test.config, test.oldconfig)
  769. err := errs.ToAggregate()
  770. if err != nil {
  771. if e, a := test.expectedError, err.Error(); !strings.Contains(a, e) || e == "" {
  772. t.Errorf("expected to contain %s, got %s", e, a)
  773. }
  774. } else {
  775. if test.expectedError != "" {
  776. t.Errorf("unexpected no error, expected to contain %s", test.expectedError)
  777. }
  778. }
  779. })
  780. }
  781. }