create_role_test.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674
  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 create
  14. import (
  15. "reflect"
  16. "testing"
  17. rbac "k8s.io/api/rbac/v1"
  18. "k8s.io/apimachinery/pkg/api/equality"
  19. "k8s.io/apimachinery/pkg/apis/meta/v1"
  20. "k8s.io/apimachinery/pkg/runtime"
  21. "k8s.io/apimachinery/pkg/util/diff"
  22. "k8s.io/cli-runtime/pkg/genericclioptions"
  23. "k8s.io/client-go/rest/fake"
  24. cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
  25. "k8s.io/kubernetes/pkg/kubectl/scheme"
  26. )
  27. func TestCreateRole(t *testing.T) {
  28. roleName := "my-role"
  29. tf := cmdtesting.NewTestFactory().WithNamespace("test")
  30. defer tf.Cleanup()
  31. tf.Client = &fake.RESTClient{}
  32. tf.ClientConfigVal = cmdtesting.DefaultClientConfig()
  33. tests := map[string]struct {
  34. verbs string
  35. resources string
  36. resourceNames string
  37. expectedRole *rbac.Role
  38. }{
  39. "test-duplicate-resources": {
  40. verbs: "get,watch,list",
  41. resources: "pods,pods",
  42. expectedRole: &rbac.Role{
  43. TypeMeta: v1.TypeMeta{APIVersion: "rbac.authorization.k8s.io/v1", Kind: "Role"},
  44. ObjectMeta: v1.ObjectMeta{
  45. Name: roleName,
  46. },
  47. Rules: []rbac.PolicyRule{
  48. {
  49. Verbs: []string{"get", "watch", "list"},
  50. Resources: []string{"pods"},
  51. APIGroups: []string{""},
  52. ResourceNames: []string{},
  53. },
  54. },
  55. },
  56. },
  57. "test-subresources": {
  58. verbs: "get,watch,list",
  59. resources: "replicasets/scale",
  60. expectedRole: &rbac.Role{
  61. TypeMeta: v1.TypeMeta{APIVersion: "rbac.authorization.k8s.io/v1", Kind: "Role"},
  62. ObjectMeta: v1.ObjectMeta{
  63. Name: roleName,
  64. },
  65. Rules: []rbac.PolicyRule{
  66. {
  67. Verbs: []string{"get", "watch", "list"},
  68. Resources: []string{"replicasets/scale"},
  69. APIGroups: []string{"extensions"},
  70. ResourceNames: []string{},
  71. },
  72. },
  73. },
  74. },
  75. "test-subresources-with-apigroup": {
  76. verbs: "get,watch,list",
  77. resources: "replicasets.extensions/scale",
  78. expectedRole: &rbac.Role{
  79. TypeMeta: v1.TypeMeta{APIVersion: "rbac.authorization.k8s.io/v1", Kind: "Role"},
  80. ObjectMeta: v1.ObjectMeta{
  81. Name: roleName,
  82. },
  83. Rules: []rbac.PolicyRule{
  84. {
  85. Verbs: []string{"get", "watch", "list"},
  86. Resources: []string{"replicasets/scale"},
  87. APIGroups: []string{"extensions"},
  88. ResourceNames: []string{},
  89. },
  90. },
  91. },
  92. },
  93. "test-valid-case-with-multiple-apigroups": {
  94. verbs: "get,watch,list",
  95. resources: "pods,deployments.extensions",
  96. expectedRole: &rbac.Role{
  97. TypeMeta: v1.TypeMeta{APIVersion: "rbac.authorization.k8s.io/v1", Kind: "Role"},
  98. ObjectMeta: v1.ObjectMeta{
  99. Name: roleName,
  100. },
  101. Rules: []rbac.PolicyRule{
  102. {
  103. Verbs: []string{"get", "watch", "list"},
  104. Resources: []string{"pods"},
  105. APIGroups: []string{""},
  106. ResourceNames: []string{},
  107. },
  108. {
  109. Verbs: []string{"get", "watch", "list"},
  110. Resources: []string{"deployments"},
  111. APIGroups: []string{"extensions"},
  112. ResourceNames: []string{},
  113. },
  114. },
  115. },
  116. },
  117. }
  118. for name, test := range tests {
  119. t.Run(name, func(t *testing.T) {
  120. ioStreams, _, buf, _ := genericclioptions.NewTestIOStreams()
  121. cmd := NewCmdCreateRole(tf, ioStreams)
  122. cmd.Flags().Set("dry-run", "true")
  123. cmd.Flags().Set("output", "yaml")
  124. cmd.Flags().Set("verb", test.verbs)
  125. cmd.Flags().Set("resource", test.resources)
  126. if test.resourceNames != "" {
  127. cmd.Flags().Set("resource-name", test.resourceNames)
  128. }
  129. cmd.Run(cmd, []string{roleName})
  130. actual := &rbac.Role{}
  131. if err := runtime.DecodeInto(scheme.Codecs.UniversalDecoder(), buf.Bytes(), actual); err != nil {
  132. t.Log(string(buf.Bytes()))
  133. t.Fatal(err)
  134. }
  135. if !equality.Semantic.DeepEqual(test.expectedRole, actual) {
  136. t.Errorf("%s", diff.ObjectReflectDiff(test.expectedRole, actual))
  137. }
  138. })
  139. }
  140. }
  141. func TestValidate(t *testing.T) {
  142. tf := cmdtesting.NewTestFactory().WithNamespace("test")
  143. defer tf.Cleanup()
  144. tests := map[string]struct {
  145. roleOptions *CreateRoleOptions
  146. expectErr bool
  147. }{
  148. "test-missing-name": {
  149. roleOptions: &CreateRoleOptions{},
  150. expectErr: true,
  151. },
  152. "test-missing-verb": {
  153. roleOptions: &CreateRoleOptions{
  154. Name: "my-role",
  155. },
  156. expectErr: true,
  157. },
  158. "test-missing-resource": {
  159. roleOptions: &CreateRoleOptions{
  160. Name: "my-role",
  161. Verbs: []string{"get"},
  162. },
  163. expectErr: true,
  164. },
  165. "test-missing-resource-existing-apigroup": {
  166. roleOptions: &CreateRoleOptions{
  167. Name: "my-role",
  168. Verbs: []string{"get"},
  169. Resources: []ResourceOptions{
  170. {
  171. Group: "extensions",
  172. },
  173. },
  174. },
  175. expectErr: true,
  176. },
  177. "test-missing-resource-existing-subresource": {
  178. roleOptions: &CreateRoleOptions{
  179. Name: "my-role",
  180. Verbs: []string{"get"},
  181. Resources: []ResourceOptions{
  182. {
  183. SubResource: "scale",
  184. },
  185. },
  186. },
  187. expectErr: true,
  188. },
  189. "test-invalid-verb": {
  190. roleOptions: &CreateRoleOptions{
  191. Name: "my-role",
  192. Verbs: []string{"invalid-verb"},
  193. Resources: []ResourceOptions{
  194. {
  195. Resource: "pods",
  196. },
  197. },
  198. },
  199. expectErr: true,
  200. },
  201. "test-nonresource-verb": {
  202. roleOptions: &CreateRoleOptions{
  203. Name: "my-role",
  204. Verbs: []string{"post"},
  205. Resources: []ResourceOptions{
  206. {
  207. Resource: "pods",
  208. },
  209. },
  210. },
  211. expectErr: true,
  212. },
  213. "test-special-verb": {
  214. roleOptions: &CreateRoleOptions{
  215. Name: "my-role",
  216. Verbs: []string{"use"},
  217. Resources: []ResourceOptions{
  218. {
  219. Resource: "pods",
  220. },
  221. },
  222. },
  223. expectErr: true,
  224. },
  225. "test-mix-verbs": {
  226. roleOptions: &CreateRoleOptions{
  227. Name: "my-role",
  228. Verbs: []string{"impersonate", "use"},
  229. Resources: []ResourceOptions{
  230. {
  231. Resource: "userextras",
  232. SubResource: "scopes",
  233. },
  234. },
  235. },
  236. expectErr: true,
  237. },
  238. "test-special-verb-with-wrong-apigroup": {
  239. roleOptions: &CreateRoleOptions{
  240. Name: "my-role",
  241. Verbs: []string{"impersonate"},
  242. Resources: []ResourceOptions{
  243. {
  244. Resource: "userextras",
  245. SubResource: "scopes",
  246. Group: "extensions",
  247. },
  248. },
  249. },
  250. expectErr: true,
  251. },
  252. "test-invalid-resource": {
  253. roleOptions: &CreateRoleOptions{
  254. Name: "my-role",
  255. Verbs: []string{"get"},
  256. Resources: []ResourceOptions{
  257. {
  258. Resource: "invalid-resource",
  259. },
  260. },
  261. },
  262. expectErr: true,
  263. },
  264. "test-resource-name-with-multiple-resources": {
  265. roleOptions: &CreateRoleOptions{
  266. Name: "my-role",
  267. Verbs: []string{"get"},
  268. Resources: []ResourceOptions{
  269. {
  270. Resource: "pods",
  271. },
  272. {
  273. Resource: "deployments",
  274. Group: "extensions",
  275. },
  276. },
  277. ResourceNames: []string{"foo"},
  278. },
  279. expectErr: false,
  280. },
  281. "test-valid-case": {
  282. roleOptions: &CreateRoleOptions{
  283. Name: "role-binder",
  284. Verbs: []string{"get", "list", "bind"},
  285. Resources: []ResourceOptions{
  286. {
  287. Resource: "roles",
  288. Group: "rbac.authorization.k8s.io",
  289. },
  290. },
  291. ResourceNames: []string{"foo"},
  292. },
  293. expectErr: false,
  294. },
  295. "test-valid-case-with-subresource": {
  296. roleOptions: &CreateRoleOptions{
  297. Name: "my-role",
  298. Verbs: []string{"get", "list"},
  299. Resources: []ResourceOptions{
  300. {
  301. Resource: "replicasets",
  302. SubResource: "scale",
  303. },
  304. },
  305. ResourceNames: []string{"bar"},
  306. },
  307. expectErr: false,
  308. },
  309. "test-valid-case-with-additional-resource": {
  310. roleOptions: &CreateRoleOptions{
  311. Name: "my-role",
  312. Verbs: []string{"impersonate"},
  313. Resources: []ResourceOptions{
  314. {
  315. Resource: "userextras",
  316. SubResource: "scopes",
  317. Group: "authentication.k8s.io",
  318. },
  319. },
  320. },
  321. expectErr: false,
  322. },
  323. }
  324. for name, test := range tests {
  325. var err error
  326. test.roleOptions.Mapper, err = tf.ToRESTMapper()
  327. if err != nil {
  328. t.Fatal(err)
  329. }
  330. err = test.roleOptions.Validate()
  331. if test.expectErr && err == nil {
  332. t.Errorf("%s: expect error happens but validate passes.", name)
  333. }
  334. if !test.expectErr && err != nil {
  335. t.Errorf("%s: unexpected error: %v", name, err)
  336. }
  337. }
  338. }
  339. func TestComplete(t *testing.T) {
  340. roleName := "my-role"
  341. tf := cmdtesting.NewTestFactory().WithNamespace("test")
  342. defer tf.Cleanup()
  343. tf.Client = &fake.RESTClient{}
  344. tf.ClientConfigVal = cmdtesting.DefaultClientConfig()
  345. defaultTestResources := "pods,deployments.extensions"
  346. tests := map[string]struct {
  347. params []string
  348. resources string
  349. roleOptions *CreateRoleOptions
  350. expected *CreateRoleOptions
  351. expectErr bool
  352. }{
  353. "test-missing-name": {
  354. params: []string{},
  355. resources: defaultTestResources,
  356. roleOptions: &CreateRoleOptions{
  357. PrintFlags: genericclioptions.NewPrintFlags("created").WithTypeSetter(scheme.Scheme),
  358. },
  359. expectErr: true,
  360. },
  361. "test-duplicate-verbs": {
  362. params: []string{roleName},
  363. resources: defaultTestResources,
  364. roleOptions: &CreateRoleOptions{
  365. PrintFlags: genericclioptions.NewPrintFlags("created").WithTypeSetter(scheme.Scheme),
  366. Name: roleName,
  367. Verbs: []string{
  368. "get",
  369. "watch",
  370. "list",
  371. "get",
  372. },
  373. },
  374. expected: &CreateRoleOptions{
  375. Name: roleName,
  376. Verbs: []string{
  377. "get",
  378. "watch",
  379. "list",
  380. },
  381. Resources: []ResourceOptions{
  382. {
  383. Resource: "pods",
  384. Group: "",
  385. },
  386. {
  387. Resource: "deployments",
  388. Group: "extensions",
  389. },
  390. },
  391. ResourceNames: []string{},
  392. },
  393. expectErr: false,
  394. },
  395. "test-verball": {
  396. params: []string{roleName},
  397. resources: defaultTestResources,
  398. roleOptions: &CreateRoleOptions{
  399. PrintFlags: genericclioptions.NewPrintFlags("created").WithTypeSetter(scheme.Scheme),
  400. Name: roleName,
  401. Verbs: []string{
  402. "get",
  403. "watch",
  404. "list",
  405. "*",
  406. },
  407. },
  408. expected: &CreateRoleOptions{
  409. Name: roleName,
  410. Verbs: []string{"*"},
  411. Resources: []ResourceOptions{
  412. {
  413. Resource: "pods",
  414. Group: "",
  415. },
  416. {
  417. Resource: "deployments",
  418. Group: "extensions",
  419. },
  420. },
  421. ResourceNames: []string{},
  422. },
  423. expectErr: false,
  424. },
  425. "test-allresource": {
  426. params: []string{roleName},
  427. resources: "*,pods",
  428. roleOptions: &CreateRoleOptions{
  429. PrintFlags: genericclioptions.NewPrintFlags("created"),
  430. Name: roleName,
  431. Verbs: []string{"*"},
  432. },
  433. expected: &CreateRoleOptions{
  434. Name: roleName,
  435. Verbs: []string{"*"},
  436. Resources: []ResourceOptions{
  437. {
  438. Resource: "*",
  439. },
  440. },
  441. ResourceNames: []string{},
  442. },
  443. expectErr: false,
  444. },
  445. "test-allresource-subresource": {
  446. params: []string{roleName},
  447. resources: "*/scale,pods",
  448. roleOptions: &CreateRoleOptions{
  449. PrintFlags: genericclioptions.NewPrintFlags("created"),
  450. Name: roleName,
  451. Verbs: []string{"*"},
  452. },
  453. expected: &CreateRoleOptions{
  454. Name: roleName,
  455. Verbs: []string{"*"},
  456. Resources: []ResourceOptions{
  457. {
  458. Resource: "*",
  459. SubResource: "scale",
  460. },
  461. {
  462. Resource: "pods",
  463. },
  464. },
  465. ResourceNames: []string{},
  466. },
  467. expectErr: false,
  468. },
  469. "test-allresrouce-allgroup": {
  470. params: []string{roleName},
  471. resources: "*.*,pods",
  472. roleOptions: &CreateRoleOptions{
  473. PrintFlags: genericclioptions.NewPrintFlags("created"),
  474. Name: roleName,
  475. Verbs: []string{"*"},
  476. },
  477. expected: &CreateRoleOptions{
  478. Name: roleName,
  479. Verbs: []string{"*"},
  480. Resources: []ResourceOptions{
  481. {
  482. Resource: "*",
  483. Group: "*",
  484. },
  485. {
  486. Resource: "pods",
  487. },
  488. },
  489. ResourceNames: []string{},
  490. },
  491. expectErr: false,
  492. },
  493. "test-allresource-allgroup-subresource": {
  494. params: []string{roleName},
  495. resources: "*.*/scale,pods",
  496. roleOptions: &CreateRoleOptions{
  497. PrintFlags: genericclioptions.NewPrintFlags("created"),
  498. Name: roleName,
  499. Verbs: []string{"*"},
  500. },
  501. expected: &CreateRoleOptions{
  502. Name: roleName,
  503. Verbs: []string{"*"},
  504. Resources: []ResourceOptions{
  505. {
  506. Resource: "*",
  507. Group: "*",
  508. SubResource: "scale",
  509. },
  510. {
  511. Resource: "pods",
  512. },
  513. },
  514. ResourceNames: []string{},
  515. },
  516. expectErr: false,
  517. },
  518. "test-allresource-specificgroup": {
  519. params: []string{roleName},
  520. resources: "*.extensions,pods",
  521. roleOptions: &CreateRoleOptions{
  522. PrintFlags: genericclioptions.NewPrintFlags("created"),
  523. Name: roleName,
  524. Verbs: []string{"*"},
  525. },
  526. expected: &CreateRoleOptions{
  527. Name: roleName,
  528. Verbs: []string{"*"},
  529. Resources: []ResourceOptions{
  530. {
  531. Resource: "*",
  532. Group: "extensions",
  533. },
  534. {
  535. Resource: "pods",
  536. },
  537. },
  538. ResourceNames: []string{},
  539. },
  540. expectErr: false,
  541. },
  542. "test-allresource-specificgroup-subresource": {
  543. params: []string{roleName},
  544. resources: "*.extensions/scale,pods",
  545. roleOptions: &CreateRoleOptions{
  546. PrintFlags: genericclioptions.NewPrintFlags("created"),
  547. Name: roleName,
  548. Verbs: []string{"*"},
  549. },
  550. expected: &CreateRoleOptions{
  551. Name: roleName,
  552. Verbs: []string{"*"},
  553. Resources: []ResourceOptions{
  554. {
  555. Resource: "*",
  556. Group: "extensions",
  557. SubResource: "scale",
  558. },
  559. {
  560. Resource: "pods",
  561. },
  562. },
  563. ResourceNames: []string{},
  564. },
  565. expectErr: false,
  566. },
  567. "test-duplicate-resourcenames": {
  568. params: []string{roleName},
  569. resources: defaultTestResources,
  570. roleOptions: &CreateRoleOptions{
  571. PrintFlags: genericclioptions.NewPrintFlags("created").WithTypeSetter(scheme.Scheme),
  572. Name: roleName,
  573. Verbs: []string{"*"},
  574. ResourceNames: []string{"foo", "foo"},
  575. },
  576. expected: &CreateRoleOptions{
  577. Name: roleName,
  578. Verbs: []string{"*"},
  579. Resources: []ResourceOptions{
  580. {
  581. Resource: "pods",
  582. Group: "",
  583. },
  584. {
  585. Resource: "deployments",
  586. Group: "extensions",
  587. },
  588. },
  589. ResourceNames: []string{"foo"},
  590. },
  591. expectErr: false,
  592. },
  593. "test-valid-complete-case": {
  594. params: []string{roleName},
  595. resources: defaultTestResources,
  596. roleOptions: &CreateRoleOptions{
  597. PrintFlags: genericclioptions.NewPrintFlags("created").WithTypeSetter(scheme.Scheme),
  598. Name: roleName,
  599. Verbs: []string{"*"},
  600. ResourceNames: []string{"foo"},
  601. },
  602. expected: &CreateRoleOptions{
  603. Name: roleName,
  604. Verbs: []string{"*"},
  605. Resources: []ResourceOptions{
  606. {
  607. Resource: "pods",
  608. Group: "",
  609. },
  610. {
  611. Resource: "deployments",
  612. Group: "extensions",
  613. },
  614. },
  615. ResourceNames: []string{"foo"},
  616. },
  617. expectErr: false,
  618. },
  619. }
  620. for name, test := range tests {
  621. cmd := NewCmdCreateRole(tf, genericclioptions.NewTestIOStreamsDiscard())
  622. cmd.Flags().Set("resource", test.resources)
  623. err := test.roleOptions.Complete(tf, cmd, test.params)
  624. if !test.expectErr && err != nil {
  625. t.Errorf("%s: unexpected error: %v", name, err)
  626. }
  627. if test.expectErr {
  628. if err != nil {
  629. continue
  630. } else {
  631. t.Errorf("%s: expect error happens but test passes.", name)
  632. }
  633. }
  634. if test.roleOptions.Name != test.expected.Name {
  635. t.Errorf("%s:\nexpected name:\n%#v\nsaw name:\n%#v", name, test.expected.Name, test.roleOptions.Name)
  636. }
  637. if !reflect.DeepEqual(test.roleOptions.Verbs, test.expected.Verbs) {
  638. t.Errorf("%s:\nexpected verbs:\n%#v\nsaw verbs:\n%#v", name, test.expected.Verbs, test.roleOptions.Verbs)
  639. }
  640. if !reflect.DeepEqual(test.roleOptions.Resources, test.expected.Resources) {
  641. t.Errorf("%s:\nexpected resources:\n%#v\nsaw resources:\n%#v", name, test.expected.Resources, test.roleOptions.Resources)
  642. }
  643. if !reflect.DeepEqual(test.roleOptions.ResourceNames, test.expected.ResourceNames) {
  644. t.Errorf("%s:\nexpected resource names:\n%#v\nsaw resource names:\n%#v", name, test.expected.ResourceNames, test.roleOptions.ResourceNames)
  645. }
  646. }
  647. }