create_clusterrole.go 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  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. "fmt"
  16. "strings"
  17. "github.com/spf13/cobra"
  18. rbacv1 "k8s.io/api/rbac/v1"
  19. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  20. "k8s.io/cli-runtime/pkg/genericclioptions"
  21. cliflag "k8s.io/component-base/cli/flag"
  22. cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
  23. "k8s.io/kubernetes/pkg/kubectl/util/i18n"
  24. "k8s.io/kubernetes/pkg/kubectl/util/templates"
  25. )
  26. var (
  27. clusterRoleLong = templates.LongDesc(i18n.T(`
  28. Create a ClusterRole.`))
  29. clusterRoleExample = templates.Examples(i18n.T(`
  30. # Create a ClusterRole named "pod-reader" that allows user to perform "get", "watch" and "list" on pods
  31. kubectl create clusterrole pod-reader --verb=get,list,watch --resource=pods
  32. # Create a ClusterRole named "pod-reader" with ResourceName specified
  33. kubectl create clusterrole pod-reader --verb=get --resource=pods --resource-name=readablepod --resource-name=anotherpod
  34. # Create a ClusterRole named "foo" with API Group specified
  35. kubectl create clusterrole foo --verb=get,list,watch --resource=rs.extensions
  36. # Create a ClusterRole named "foo" with SubResource specified
  37. kubectl create clusterrole foo --verb=get,list,watch --resource=pods,pods/status
  38. # Create a ClusterRole name "foo" with NonResourceURL specified
  39. kubectl create clusterrole "foo" --verb=get --non-resource-url=/logs/*
  40. # Create a ClusterRole name "monitoring" with AggregationRule specified
  41. kubectl create clusterrole monitoring --aggregation-rule="rbac.example.com/aggregate-to-monitoring=true"`))
  42. // Valid nonResource verb list for validation.
  43. validNonResourceVerbs = []string{"*", "get", "post", "put", "delete", "patch", "head", "options"}
  44. )
  45. // CreateClusterRoleOptions is returned by NewCmdCreateClusterRole
  46. type CreateClusterRoleOptions struct {
  47. *CreateRoleOptions
  48. NonResourceURLs []string
  49. AggregationRule map[string]string
  50. }
  51. // NewCmdCreateClusterRole initializes and returns new ClusterRoles command
  52. func NewCmdCreateClusterRole(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
  53. c := &CreateClusterRoleOptions{
  54. CreateRoleOptions: NewCreateRoleOptions(ioStreams),
  55. AggregationRule: map[string]string{},
  56. }
  57. cmd := &cobra.Command{
  58. Use: "clusterrole NAME --verb=verb --resource=resource.group [--resource-name=resourcename] [--dry-run]",
  59. DisableFlagsInUseLine: true,
  60. Short: clusterRoleLong,
  61. Long: clusterRoleLong,
  62. Example: clusterRoleExample,
  63. Run: func(cmd *cobra.Command, args []string) {
  64. cmdutil.CheckErr(c.Complete(f, cmd, args))
  65. cmdutil.CheckErr(c.Validate())
  66. cmdutil.CheckErr(c.RunCreateRole())
  67. },
  68. }
  69. c.PrintFlags.AddFlags(cmd)
  70. cmdutil.AddApplyAnnotationFlags(cmd)
  71. cmdutil.AddValidateFlags(cmd)
  72. cmdutil.AddDryRunFlag(cmd)
  73. cmd.Flags().StringSliceVar(&c.Verbs, "verb", c.Verbs, "Verb that applies to the resources contained in the rule")
  74. cmd.Flags().StringSliceVar(&c.NonResourceURLs, "non-resource-url", c.NonResourceURLs, "A partial url that user should have access to.")
  75. cmd.Flags().StringSlice("resource", []string{}, "Resource that the rule applies to")
  76. cmd.Flags().StringArrayVar(&c.ResourceNames, "resource-name", c.ResourceNames, "Resource in the white list that the rule applies to, repeat this flag for multiple items")
  77. cmd.Flags().Var(cliflag.NewMapStringString(&c.AggregationRule), "aggregation-rule", "An aggregation label selector for combining ClusterRoles.")
  78. return cmd
  79. }
  80. // Complete completes all the required options
  81. func (c *CreateClusterRoleOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
  82. // Remove duplicate nonResourceURLs
  83. nonResourceURLs := []string{}
  84. for _, n := range c.NonResourceURLs {
  85. if !arrayContains(nonResourceURLs, n) {
  86. nonResourceURLs = append(nonResourceURLs, n)
  87. }
  88. }
  89. c.NonResourceURLs = nonResourceURLs
  90. return c.CreateRoleOptions.Complete(f, cmd, args)
  91. }
  92. // Validate makes sure there is no discrepency in CreateClusterRoleOptions
  93. func (c *CreateClusterRoleOptions) Validate() error {
  94. if c.Name == "" {
  95. return fmt.Errorf("name must be specified")
  96. }
  97. if len(c.AggregationRule) > 0 {
  98. if len(c.NonResourceURLs) > 0 || len(c.Verbs) > 0 || len(c.Resources) > 0 || len(c.ResourceNames) > 0 {
  99. return fmt.Errorf("aggregation rule must be specified without nonResourceURLs, verbs, resources or resourceNames")
  100. }
  101. return nil
  102. }
  103. // validate verbs.
  104. if len(c.Verbs) == 0 {
  105. return fmt.Errorf("at least one verb must be specified")
  106. }
  107. if len(c.Resources) == 0 && len(c.NonResourceURLs) == 0 {
  108. return fmt.Errorf("one of resource or nonResourceURL must be specified")
  109. }
  110. // validate resources
  111. if len(c.Resources) > 0 {
  112. for _, v := range c.Verbs {
  113. if !arrayContains(validResourceVerbs, v) {
  114. return fmt.Errorf("invalid verb: '%s'", v)
  115. }
  116. }
  117. if err := c.validateResource(); err != nil {
  118. return err
  119. }
  120. }
  121. //validate non-resource-url
  122. if len(c.NonResourceURLs) > 0 {
  123. for _, v := range c.Verbs {
  124. if !arrayContains(validNonResourceVerbs, v) {
  125. return fmt.Errorf("invalid verb: '%s' for nonResourceURL", v)
  126. }
  127. }
  128. for _, nonResourceURL := range c.NonResourceURLs {
  129. if nonResourceURL == "*" {
  130. continue
  131. }
  132. if nonResourceURL == "" || !strings.HasPrefix(nonResourceURL, "/") {
  133. return fmt.Errorf("nonResourceURL should start with /")
  134. }
  135. if strings.ContainsRune(nonResourceURL[:len(nonResourceURL)-1], '*') {
  136. return fmt.Errorf("nonResourceURL only supports wildcard matches when '*' is at the end")
  137. }
  138. }
  139. }
  140. return nil
  141. }
  142. // RunCreateRole creates a new clusterRole
  143. func (c *CreateClusterRoleOptions) RunCreateRole() error {
  144. clusterRole := &rbacv1.ClusterRole{
  145. // this is ok because we know exactly how we want to be serialized
  146. TypeMeta: metav1.TypeMeta{APIVersion: rbacv1.SchemeGroupVersion.String(), Kind: "ClusterRole"},
  147. }
  148. clusterRole.Name = c.Name
  149. var err error
  150. if len(c.AggregationRule) == 0 {
  151. rules, err := generateResourcePolicyRules(c.Mapper, c.Verbs, c.Resources, c.ResourceNames, c.NonResourceURLs)
  152. if err != nil {
  153. return err
  154. }
  155. clusterRole.Rules = rules
  156. } else {
  157. clusterRole.AggregationRule = &rbacv1.AggregationRule{
  158. ClusterRoleSelectors: []metav1.LabelSelector{
  159. {
  160. MatchLabels: c.AggregationRule,
  161. },
  162. },
  163. }
  164. }
  165. // Create ClusterRole.
  166. if !c.DryRun {
  167. clusterRole, err = c.Client.ClusterRoles().Create(clusterRole)
  168. if err != nil {
  169. return err
  170. }
  171. }
  172. return c.PrintObj(clusterRole)
  173. }