...

Source file src/k8s.io/kubectl/pkg/cmd/create/create_clusterrole.go

Documentation: k8s.io/kubectl/pkg/cmd/create

     1  /*
     2  Copyright 2017 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package create
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"strings"
    23  
    24  	"github.com/spf13/cobra"
    25  
    26  	rbacv1 "k8s.io/api/rbac/v1"
    27  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    28  	"k8s.io/cli-runtime/pkg/genericiooptions"
    29  	cliflag "k8s.io/component-base/cli/flag"
    30  	cmdutil "k8s.io/kubectl/pkg/cmd/util"
    31  	"k8s.io/kubectl/pkg/scheme"
    32  	"k8s.io/kubectl/pkg/util"
    33  	"k8s.io/kubectl/pkg/util/i18n"
    34  	"k8s.io/kubectl/pkg/util/templates"
    35  )
    36  
    37  var (
    38  	clusterRoleLong = templates.LongDesc(i18n.T(`
    39  		Create a cluster role.`))
    40  
    41  	clusterRoleExample = templates.Examples(i18n.T(`
    42  		# Create a cluster role named "pod-reader" that allows user to perform "get", "watch" and "list" on pods
    43  		kubectl create clusterrole pod-reader --verb=get,list,watch --resource=pods
    44  
    45  		# Create a cluster role named "pod-reader" with ResourceName specified
    46  		kubectl create clusterrole pod-reader --verb=get --resource=pods --resource-name=readablepod --resource-name=anotherpod
    47  
    48  		# Create a cluster role named "foo" with API Group specified
    49  		kubectl create clusterrole foo --verb=get,list,watch --resource=rs.apps
    50  
    51  		# Create a cluster role named "foo" with SubResource specified
    52  		kubectl create clusterrole foo --verb=get,list,watch --resource=pods,pods/status
    53  
    54  		# Create a cluster role name "foo" with NonResourceURL specified
    55  		kubectl create clusterrole "foo" --verb=get --non-resource-url=/logs/*
    56  
    57  		# Create a cluster role name "monitoring" with AggregationRule specified
    58  		kubectl create clusterrole monitoring --aggregation-rule="rbac.example.com/aggregate-to-monitoring=true"`))
    59  
    60  	// Valid nonResource verb list for validation.
    61  	validNonResourceVerbs = []string{"*", "get", "post", "put", "delete", "patch", "head", "options"}
    62  )
    63  
    64  // CreateClusterRoleOptions is returned by NewCmdCreateClusterRole
    65  type CreateClusterRoleOptions struct {
    66  	*CreateRoleOptions
    67  	NonResourceURLs []string
    68  	AggregationRule map[string]string
    69  	FieldManager    string
    70  }
    71  
    72  // NewCmdCreateClusterRole initializes and returns new ClusterRoles command
    73  func NewCmdCreateClusterRole(f cmdutil.Factory, ioStreams genericiooptions.IOStreams) *cobra.Command {
    74  	c := &CreateClusterRoleOptions{
    75  		CreateRoleOptions: NewCreateRoleOptions(ioStreams),
    76  		AggregationRule:   map[string]string{},
    77  	}
    78  	cmd := &cobra.Command{
    79  		Use:                   "clusterrole NAME --verb=verb --resource=resource.group [--resource-name=resourcename] [--dry-run=server|client|none]",
    80  		DisableFlagsInUseLine: true,
    81  		Short:                 i18n.T("Create a cluster role"),
    82  		Long:                  clusterRoleLong,
    83  		Example:               clusterRoleExample,
    84  		Run: func(cmd *cobra.Command, args []string) {
    85  			cmdutil.CheckErr(c.Complete(f, cmd, args))
    86  			cmdutil.CheckErr(c.Validate())
    87  			cmdutil.CheckErr(c.RunCreateRole())
    88  		},
    89  	}
    90  
    91  	c.PrintFlags.AddFlags(cmd)
    92  
    93  	cmdutil.AddApplyAnnotationFlags(cmd)
    94  	cmdutil.AddValidateFlags(cmd)
    95  	cmdutil.AddDryRunFlag(cmd)
    96  	cmd.Flags().StringSliceVar(&c.Verbs, "verb", c.Verbs, "Verb that applies to the resources contained in the rule")
    97  	cmd.Flags().StringSliceVar(&c.NonResourceURLs, "non-resource-url", c.NonResourceURLs, "A partial url that user should have access to.")
    98  	cmd.Flags().StringSlice("resource", []string{}, "Resource that the rule applies to")
    99  	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")
   100  	cmd.Flags().Var(cliflag.NewMapStringString(&c.AggregationRule), "aggregation-rule", "An aggregation label selector for combining ClusterRoles.")
   101  	cmdutil.AddFieldManagerFlagVar(cmd, &c.FieldManager, "kubectl-create")
   102  
   103  	return cmd
   104  }
   105  
   106  // Complete completes all the required options
   107  func (c *CreateClusterRoleOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
   108  	// Remove duplicate nonResourceURLs
   109  	nonResourceURLs := []string{}
   110  	for _, n := range c.NonResourceURLs {
   111  		if !arrayContains(nonResourceURLs, n) {
   112  			nonResourceURLs = append(nonResourceURLs, n)
   113  		}
   114  	}
   115  	c.NonResourceURLs = nonResourceURLs
   116  
   117  	return c.CreateRoleOptions.Complete(f, cmd, args)
   118  }
   119  
   120  // Validate makes sure there is no discrepency in CreateClusterRoleOptions
   121  func (c *CreateClusterRoleOptions) Validate() error {
   122  	if c.Name == "" {
   123  		return fmt.Errorf("name must be specified")
   124  	}
   125  
   126  	if len(c.AggregationRule) > 0 {
   127  		if len(c.NonResourceURLs) > 0 || len(c.Verbs) > 0 || len(c.Resources) > 0 || len(c.ResourceNames) > 0 {
   128  			return fmt.Errorf("aggregation rule must be specified without nonResourceURLs, verbs, resources or resourceNames")
   129  		}
   130  		return nil
   131  	}
   132  
   133  	// validate verbs.
   134  	if len(c.Verbs) == 0 {
   135  		return fmt.Errorf("at least one verb must be specified")
   136  	}
   137  
   138  	if len(c.Resources) == 0 && len(c.NonResourceURLs) == 0 {
   139  		return fmt.Errorf("one of resource or nonResourceURL must be specified")
   140  	}
   141  
   142  	// validate resources
   143  	if len(c.Resources) > 0 {
   144  		for _, v := range c.Verbs {
   145  			if !arrayContains(validResourceVerbs, v) {
   146  				fmt.Fprintf(c.ErrOut, "Warning: '%s' is not a standard resource verb\n", v)
   147  			}
   148  		}
   149  		if err := c.validateResource(); err != nil {
   150  			return err
   151  		}
   152  	}
   153  
   154  	//validate non-resource-url
   155  	if len(c.NonResourceURLs) > 0 {
   156  		for _, v := range c.Verbs {
   157  			if !arrayContains(validNonResourceVerbs, v) {
   158  				return fmt.Errorf("invalid verb: '%s' for nonResourceURL", v)
   159  			}
   160  		}
   161  
   162  		for _, nonResourceURL := range c.NonResourceURLs {
   163  			if nonResourceURL == "*" {
   164  				continue
   165  			}
   166  
   167  			if nonResourceURL == "" || !strings.HasPrefix(nonResourceURL, "/") {
   168  				return fmt.Errorf("nonResourceURL should start with /")
   169  			}
   170  
   171  			if strings.ContainsRune(nonResourceURL[:len(nonResourceURL)-1], '*') {
   172  				return fmt.Errorf("nonResourceURL only supports wildcard matches when '*' is at the end")
   173  			}
   174  		}
   175  	}
   176  
   177  	return nil
   178  
   179  }
   180  
   181  // RunCreateRole creates a new clusterRole
   182  func (c *CreateClusterRoleOptions) RunCreateRole() error {
   183  	clusterRole := &rbacv1.ClusterRole{
   184  		// this is ok because we know exactly how we want to be serialized
   185  		TypeMeta: metav1.TypeMeta{APIVersion: rbacv1.SchemeGroupVersion.String(), Kind: "ClusterRole"},
   186  	}
   187  	clusterRole.Name = c.Name
   188  
   189  	var err error
   190  	if len(c.AggregationRule) == 0 {
   191  		rules, err := generateResourcePolicyRules(c.Mapper, c.Verbs, c.Resources, c.ResourceNames, c.NonResourceURLs)
   192  		if err != nil {
   193  			return err
   194  		}
   195  		clusterRole.Rules = rules
   196  	} else {
   197  		clusterRole.AggregationRule = &rbacv1.AggregationRule{
   198  			ClusterRoleSelectors: []metav1.LabelSelector{
   199  				{
   200  					MatchLabels: c.AggregationRule,
   201  				},
   202  			},
   203  		}
   204  	}
   205  
   206  	if err := util.CreateOrUpdateAnnotation(c.CreateAnnotation, clusterRole, scheme.DefaultJSONEncoder()); err != nil {
   207  		return err
   208  	}
   209  
   210  	// Create ClusterRole.
   211  	if c.DryRunStrategy != cmdutil.DryRunClient {
   212  		createOptions := metav1.CreateOptions{}
   213  		if c.FieldManager != "" {
   214  			createOptions.FieldManager = c.FieldManager
   215  		}
   216  		createOptions.FieldValidation = c.ValidationDirective
   217  		if c.DryRunStrategy == cmdutil.DryRunServer {
   218  			createOptions.DryRun = []string{metav1.DryRunAll}
   219  		}
   220  		clusterRole, err = c.Client.ClusterRoles().Create(context.TODO(), clusterRole, createOptions)
   221  		if err != nil {
   222  			return err
   223  		}
   224  	}
   225  
   226  	return c.PrintObj(clusterRole)
   227  }
   228  

View as plain text