mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-10-31 10:18:13 +00:00 
			
		
		
		
	Merge pull request #39852 from xingzhou/kube-39596
Automatic merge from submit-queue Added kubectl create role command Added `kubectl create role` command. Fixed part of #39596 **Release note**: ``` Added one new command `kubectl create role` to help user create a single role from command line. ```
This commit is contained in:
		| @@ -43,6 +43,7 @@ docs/man/man1/kubectl-create-deployment.1 | |||||||
| docs/man/man1/kubectl-create-namespace.1 | docs/man/man1/kubectl-create-namespace.1 | ||||||
| docs/man/man1/kubectl-create-poddisruptionbudget.1 | docs/man/man1/kubectl-create-poddisruptionbudget.1 | ||||||
| docs/man/man1/kubectl-create-quota.1 | docs/man/man1/kubectl-create-quota.1 | ||||||
|  | docs/man/man1/kubectl-create-role.1 | ||||||
| docs/man/man1/kubectl-create-rolebinding.1 | docs/man/man1/kubectl-create-rolebinding.1 | ||||||
| docs/man/man1/kubectl-create-secret-docker-registry.1 | docs/man/man1/kubectl-create-secret-docker-registry.1 | ||||||
| docs/man/man1/kubectl-create-secret-generic.1 | docs/man/man1/kubectl-create-secret-generic.1 | ||||||
| @@ -127,6 +128,7 @@ docs/user-guide/kubectl/kubectl_create_deployment.md | |||||||
| docs/user-guide/kubectl/kubectl_create_namespace.md | docs/user-guide/kubectl/kubectl_create_namespace.md | ||||||
| docs/user-guide/kubectl/kubectl_create_poddisruptionbudget.md | docs/user-guide/kubectl/kubectl_create_poddisruptionbudget.md | ||||||
| docs/user-guide/kubectl/kubectl_create_quota.md | docs/user-guide/kubectl/kubectl_create_quota.md | ||||||
|  | docs/user-guide/kubectl/kubectl_create_role.md | ||||||
| docs/user-guide/kubectl/kubectl_create_rolebinding.md | docs/user-guide/kubectl/kubectl_create_rolebinding.md | ||||||
| docs/user-guide/kubectl/kubectl_create_secret.md | docs/user-guide/kubectl/kubectl_create_secret.md | ||||||
| docs/user-guide/kubectl/kubectl_create_secret_docker-registry.md | docs/user-guide/kubectl/kubectl_create_secret_docker-registry.md | ||||||
|   | |||||||
							
								
								
									
										3
									
								
								docs/man/man1/kubectl-create-role.1
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								docs/man/man1/kubectl-create-role.1
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | |||||||
|  | This file is autogenerated, but we've stopped checking such files into the | ||||||
|  | repository to reduce the need for rebases. Please run hack/generate-docs.sh to | ||||||
|  | populate this file. | ||||||
							
								
								
									
										3
									
								
								docs/user-guide/kubectl/kubectl_create_role.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								docs/user-guide/kubectl/kubectl_create_role.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | |||||||
|  | This file is autogenerated, but we've stopped checking such files into the | ||||||
|  | repository to reduce the need for rebases. Please run hack/generate-docs.sh to | ||||||
|  | populate this file. | ||||||
| @@ -57,6 +57,7 @@ pods="pods" | |||||||
| podtemplates="podtemplates" | podtemplates="podtemplates" | ||||||
| replicasets="replicasets" | replicasets="replicasets" | ||||||
| replicationcontrollers="replicationcontrollers" | replicationcontrollers="replicationcontrollers" | ||||||
|  | roles="roles" | ||||||
| secrets="secrets" | secrets="secrets" | ||||||
| serviceaccounts="serviceaccounts" | serviceaccounts="serviceaccounts" | ||||||
| services="services" | services="services" | ||||||
| @@ -2817,6 +2818,22 @@ runTests() { | |||||||
|     kube::test::get_object_assert rolebinding/sarole "{{range.subjects}}{{.namespace}}:{{end}}" 'otherns:' |     kube::test::get_object_assert rolebinding/sarole "{{range.subjects}}{{.namespace}}:{{end}}" 'otherns:' | ||||||
|     kube::test::get_object_assert rolebinding/sarole "{{range.subjects}}{{.name}}:{{end}}" 'sa-name:' |     kube::test::get_object_assert rolebinding/sarole "{{range.subjects}}{{.name}}:{{end}}" 'sa-name:' | ||||||
|   fi |   fi | ||||||
|  |    | ||||||
|  |   if kube::test::if_supports_resource "${roles}" ; then | ||||||
|  |     kubectl create "${kube_flags[@]}" role pod-admin --verb=* --resource=pods | ||||||
|  |     kube::test::get_object_assert role/pod-admin "{{range.rules}}{{range.verbs}}{{.}}:{{end}}{{end}}" '\*:' | ||||||
|  |     kube::test::get_object_assert role/pod-admin "{{range.rules}}{{range.resources}}{{.}}:{{end}}{{end}}" 'pods:' | ||||||
|  |     kube::test::get_object_assert role/pod-admin "{{range.rules}}{{range.apiGroups}}{{.}}:{{end}}{{end}}" ':' | ||||||
|  |     kubectl create "${kube_flags[@]}" role resource-reader --verb=get,list --resource=pods,deployments.extensions | ||||||
|  |     kube::test::get_object_assert role/resource-reader "{{range.rules}}{{range.verbs}}{{.}}:{{end}}{{end}}" 'get:list:get:list:' | ||||||
|  |     kube::test::get_object_assert role/resource-reader "{{range.rules}}{{range.resources}}{{.}}:{{end}}{{end}}" 'pods:deployments:' | ||||||
|  |     kube::test::get_object_assert role/resource-reader "{{range.rules}}{{range.apiGroups}}{{.}}:{{end}}{{end}}" ':extensions:' | ||||||
|  |   	kubectl create "${kube_flags[@]}" role resourcename-reader --verb=get,list --resource=pods --resource-name=foo | ||||||
|  |     kube::test::get_object_assert role/resourcename-reader "{{range.rules}}{{range.verbs}}{{.}}:{{end}}{{end}}" 'get:list:' | ||||||
|  |     kube::test::get_object_assert role/resourcename-reader "{{range.rules}}{{range.resources}}{{.}}:{{end}}{{end}}" 'pods:' | ||||||
|  |     kube::test::get_object_assert role/resourcename-reader "{{range.rules}}{{range.apiGroups}}{{.}}:{{end}}{{end}}" ':' | ||||||
|  |     kube::test::get_object_assert role/resourcename-reader "{{range.rules}}{{range.resourceNames}}{{.}}:{{end}}{{end}}" 'foo:' | ||||||
|  |   fi | ||||||
|  |  | ||||||
|   ######################### |   ######################### | ||||||
|   # Assert short name     # |   # Assert short name     # | ||||||
|   | |||||||
| @@ -519,7 +519,9 @@ requestheader-username-headers | |||||||
| require-kubeconfig | require-kubeconfig | ||||||
| required-contexts | required-contexts | ||||||
| resolv-conf | resolv-conf | ||||||
|  | resource | ||||||
| resource-container | resource-container | ||||||
|  | resource-name | ||||||
| resource-quota-sync-period | resource-quota-sync-period | ||||||
| resource-version | resource-version | ||||||
| results-dir | results-dir | ||||||
| @@ -623,6 +625,7 @@ use-service-account-credentials | |||||||
| use-kubernetes-cluster-service | use-kubernetes-cluster-service | ||||||
| use-kubernetes-version | use-kubernetes-version | ||||||
| user-whitelist | user-whitelist | ||||||
|  | verb | ||||||
| verify-only | verify-only | ||||||
| versioned-clientset-package | versioned-clientset-package | ||||||
| viper-config | viper-config | ||||||
|   | |||||||
| @@ -30,6 +30,7 @@ go_library( | |||||||
|         "create_namespace.go", |         "create_namespace.go", | ||||||
|         "create_pdb.go", |         "create_pdb.go", | ||||||
|         "create_quota.go", |         "create_quota.go", | ||||||
|  |         "create_role.go", | ||||||
|         "create_rolebinding.go", |         "create_rolebinding.go", | ||||||
|         "create_secret.go", |         "create_secret.go", | ||||||
|         "create_service.go", |         "create_service.go", | ||||||
| @@ -70,6 +71,7 @@ go_library( | |||||||
|         "//pkg/apis/certificates:go_default_library", |         "//pkg/apis/certificates:go_default_library", | ||||||
|         "//pkg/apis/extensions/v1beta1:go_default_library", |         "//pkg/apis/extensions/v1beta1:go_default_library", | ||||||
|         "//pkg/apis/policy:go_default_library", |         "//pkg/apis/policy:go_default_library", | ||||||
|  |         "//pkg/apis/rbac:go_default_library", | ||||||
|         "//pkg/client/clientset_generated/internalclientset:go_default_library", |         "//pkg/client/clientset_generated/internalclientset:go_default_library", | ||||||
|         "//pkg/client/clientset_generated/internalclientset/typed/core/internalversion:go_default_library", |         "//pkg/client/clientset_generated/internalclientset/typed/core/internalversion:go_default_library", | ||||||
|         "//pkg/client/unversioned:go_default_library", |         "//pkg/client/unversioned:go_default_library", | ||||||
| @@ -143,6 +145,7 @@ go_test( | |||||||
|         "create_deployment_test.go", |         "create_deployment_test.go", | ||||||
|         "create_namespace_test.go", |         "create_namespace_test.go", | ||||||
|         "create_quota_test.go", |         "create_quota_test.go", | ||||||
|  |         "create_role_test.go", | ||||||
|         "create_secret_test.go", |         "create_secret_test.go", | ||||||
|         "create_service_test.go", |         "create_service_test.go", | ||||||
|         "create_serviceaccount_test.go", |         "create_serviceaccount_test.go", | ||||||
| @@ -185,6 +188,7 @@ go_test( | |||||||
|         "//pkg/apis/batch:go_default_library", |         "//pkg/apis/batch:go_default_library", | ||||||
|         "//pkg/apis/extensions:go_default_library", |         "//pkg/apis/extensions:go_default_library", | ||||||
|         "//pkg/apis/policy:go_default_library", |         "//pkg/apis/policy:go_default_library", | ||||||
|  |         "//pkg/apis/rbac:go_default_library", | ||||||
|         "//pkg/kubectl:go_default_library", |         "//pkg/kubectl:go_default_library", | ||||||
|         "//pkg/kubectl/cmd/testing:go_default_library", |         "//pkg/kubectl/cmd/testing:go_default_library", | ||||||
|         "//pkg/kubectl/cmd/util:go_default_library", |         "//pkg/kubectl/cmd/util:go_default_library", | ||||||
|   | |||||||
| @@ -96,6 +96,7 @@ func NewCmdCreate(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command { | |||||||
| 	cmd.AddCommand(NewCmdCreateService(f, out, errOut)) | 	cmd.AddCommand(NewCmdCreateService(f, out, errOut)) | ||||||
| 	cmd.AddCommand(NewCmdCreateDeployment(f, out)) | 	cmd.AddCommand(NewCmdCreateDeployment(f, out)) | ||||||
| 	cmd.AddCommand(NewCmdCreateClusterRoleBinding(f, out)) | 	cmd.AddCommand(NewCmdCreateClusterRoleBinding(f, out)) | ||||||
|  | 	cmd.AddCommand(NewCmdCreateRole(f, out)) | ||||||
| 	cmd.AddCommand(NewCmdCreateRoleBinding(f, out)) | 	cmd.AddCommand(NewCmdCreateRoleBinding(f, out)) | ||||||
| 	cmd.AddCommand(NewCmdCreatePodDisruptionBudget(f, out)) | 	cmd.AddCommand(NewCmdCreatePodDisruptionBudget(f, out)) | ||||||
| 	return cmd | 	return cmd | ||||||
|   | |||||||
							
								
								
									
										237
									
								
								pkg/kubectl/cmd/create_role.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										237
									
								
								pkg/kubectl/cmd/create_role.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,237 @@ | |||||||
|  | /* | ||||||
|  | Copyright 2017 The Kubernetes Authors. | ||||||
|  |  | ||||||
|  | Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  | you may not use this file except in compliance with the License. | ||||||
|  | You may obtain a copy of the License at | ||||||
|  |  | ||||||
|  |     http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  | ||||||
|  | Unless required by applicable law or agreed to in writing, software | ||||||
|  | distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  | See the License for the specific language governing permissions and | ||||||
|  | limitations under the License. | ||||||
|  | */ | ||||||
|  |  | ||||||
|  | package cmd | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"io" | ||||||
|  | 	"strings" | ||||||
|  |  | ||||||
|  | 	"github.com/spf13/cobra" | ||||||
|  |  | ||||||
|  | 	"k8s.io/apimachinery/pkg/runtime/schema" | ||||||
|  | 	"k8s.io/apimachinery/pkg/util/sets" | ||||||
|  | 	"k8s.io/kubernetes/pkg/apis/rbac" | ||||||
|  | 	"k8s.io/kubernetes/pkg/kubectl/cmd/templates" | ||||||
|  | 	cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | var ( | ||||||
|  | 	roleLong = templates.LongDesc(` | ||||||
|  | 		Create a role with single rule.`) | ||||||
|  |  | ||||||
|  | 	roleExample = templates.Examples(` | ||||||
|  | 		# Create a Role named "pod-reader" that allows user to perform "get", "watch" and "list" on pods | ||||||
|  | 		kubectl create role pod-reader --verb=get --verb=list --verb=watch --resource=pods | ||||||
|  |  | ||||||
|  | 		# Create a Role named "pod-reader" with ResourceName specified | ||||||
|  | 		kubectl create role pod-reader --verb=get --verg=list --verb=watch --resource=pods --resource-name=readablepod`) | ||||||
|  |  | ||||||
|  | 	// Valid resource verb list for validation. | ||||||
|  | 	validResourceVerbs = []string{"*", "get", "delete", "list", "create", "update", "patch", "watch", "proxy", "redirect", "deletecollection"} | ||||||
|  |  | ||||||
|  | 	// Valid non-resource verb list for validation. | ||||||
|  | 	validNonResourceVerbs = []string{"get", "post", "put", "delete"} | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type CreateRoleOptions struct { | ||||||
|  | 	Name          string | ||||||
|  | 	Verbs         []string | ||||||
|  | 	Resources     []schema.GroupVersionResource | ||||||
|  | 	ResourceNames []string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Role is a command to ease creating Roles. | ||||||
|  | func NewCmdCreateRole(f cmdutil.Factory, cmdOut io.Writer) *cobra.Command { | ||||||
|  | 	c := &CreateRoleOptions{} | ||||||
|  | 	cmd := &cobra.Command{ | ||||||
|  | 		Use:     "role NAME --verb=verb --resource=resource.group [--resource-name=resourcename] [--dry-run]", | ||||||
|  | 		Short:   roleLong, | ||||||
|  | 		Long:    roleLong, | ||||||
|  | 		Example: roleExample, | ||||||
|  | 		Run: func(cmd *cobra.Command, args []string) { | ||||||
|  | 			cmdutil.CheckErr(c.Complete(cmd, args)) | ||||||
|  | 			cmdutil.CheckErr(c.Validate(f)) | ||||||
|  | 			cmdutil.CheckErr(c.RunCreateRole(f, cmdOut, cmd, args)) | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 	cmdutil.AddApplyAnnotationFlags(cmd) | ||||||
|  | 	cmdutil.AddValidateFlags(cmd) | ||||||
|  | 	cmdutil.AddPrinterFlags(cmd) | ||||||
|  | 	cmdutil.AddDryRunFlag(cmd) | ||||||
|  | 	cmd.Flags().StringSliceVar(&c.Verbs, "verb", []string{}, "verb that applies to the resources contained in the rule") | ||||||
|  | 	cmd.Flags().StringSlice("resource", []string{}, "resource that the rule applies to") | ||||||
|  | 	cmd.Flags().StringSliceVar(&c.ResourceNames, "resource-name", []string{}, "resource in the white list that the rule applies to") | ||||||
|  |  | ||||||
|  | 	return cmd | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c *CreateRoleOptions) Complete(cmd *cobra.Command, args []string) error { | ||||||
|  | 	name, err := NameFromCommandArgs(cmd, args) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	c.Name = name | ||||||
|  |  | ||||||
|  | 	// Remove duplicate verbs. | ||||||
|  | 	verbs := []string{} | ||||||
|  | 	for _, v := range c.Verbs { | ||||||
|  | 		// VerbAll respresents all kinds of verbs. | ||||||
|  | 		if v == "*" { | ||||||
|  | 			verbs = []string{"*"} | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 		if !arrayContains(verbs, v) { | ||||||
|  | 			verbs = append(verbs, v) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	c.Verbs = verbs | ||||||
|  |  | ||||||
|  | 	// Support resource.group pattern. If no API Group specified, use "" as core API Group. | ||||||
|  | 	// e.g. --resource=pods,deployments.extensions | ||||||
|  | 	resources := cmdutil.GetFlagStringSlice(cmd, "resource") | ||||||
|  | 	for _, r := range resources { | ||||||
|  | 		sections := strings.Split(r, ".") | ||||||
|  |  | ||||||
|  | 		if len(sections) == 1 { | ||||||
|  | 			c.Resources = append(c.Resources, schema.GroupVersionResource{Resource: r}) | ||||||
|  | 		} else { | ||||||
|  | 			c.Resources = append(c.Resources, schema.GroupVersionResource{Resource: sections[0], Group: strings.Join(sections[1:], ".")}) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Remove duplicate resource names. | ||||||
|  | 	resourceNames := []string{} | ||||||
|  | 	for _, n := range c.ResourceNames { | ||||||
|  | 		if !arrayContains(resourceNames, n) { | ||||||
|  | 			resourceNames = append(resourceNames, n) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	c.ResourceNames = resourceNames | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c *CreateRoleOptions) Validate(f cmdutil.Factory) error { | ||||||
|  | 	if c.Name == "" { | ||||||
|  | 		return fmt.Errorf("name must be specified") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// validate verbs. | ||||||
|  | 	if len(c.Verbs) == 0 { | ||||||
|  | 		return fmt.Errorf("at least one verb must be specified") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, v := range c.Verbs { | ||||||
|  | 		if !arrayContains(validResourceVerbs, v) && !arrayContains(validNonResourceVerbs, v) { | ||||||
|  | 			return fmt.Errorf("invalid verb: '%s'", v) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// validate resources. | ||||||
|  | 	mapper, _ := f.Object() | ||||||
|  |  | ||||||
|  | 	if len(c.Resources) == 0 { | ||||||
|  | 		return fmt.Errorf("at least one resource must be specified") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, r := range c.Resources { | ||||||
|  | 		_, err := mapper.ResourceFor(r) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// validate resource names, can not apply resource names to multiple resources. | ||||||
|  | 	if len(c.ResourceNames) > 0 && len(c.Resources) > 1 { | ||||||
|  | 		return fmt.Errorf("resource name(s) can not be applied to multiple resources") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c *CreateRoleOptions) RunCreateRole(f cmdutil.Factory, cmdOut io.Writer, cmd *cobra.Command, args []string) error { | ||||||
|  | 	mapper, _ := f.Object() | ||||||
|  | 	dryRun, outputFormat := cmdutil.GetDryRunFlag(cmd), cmdutil.GetFlagString(cmd, "output") | ||||||
|  |  | ||||||
|  | 	// groupResourceMapping is a apigroup-resource map. The key of this map is api group, while the value | ||||||
|  | 	// is a string array of resources under this api group. | ||||||
|  | 	// E.g.  groupResourceMapping = {"extensions": ["replicasets", "deployments"], "batch":["jobs"]} | ||||||
|  | 	groupResourceMapping := map[string][]string{} | ||||||
|  |  | ||||||
|  | 	// This loop does the following work: | ||||||
|  | 	// 1. Constructs groupResourceMapping based on input resources. | ||||||
|  | 	// 2. Prevents pointing to non-existent resources. | ||||||
|  | 	// 3. Transfers resource short name to long name. E.g. rs.extensions is transferred to replicasets.extensions | ||||||
|  | 	for _, r := range c.Resources { | ||||||
|  | 		resource, err := mapper.ResourceFor(r) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 		if !arrayContains(groupResourceMapping[resource.Group], resource.Resource) { | ||||||
|  | 			groupResourceMapping[resource.Group] = append(groupResourceMapping[resource.Group], resource.Resource) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	role := &rbac.Role{} | ||||||
|  |  | ||||||
|  | 	// Create separate rule for each of the api group. | ||||||
|  | 	rules := []rbac.PolicyRule{} | ||||||
|  | 	for _, g := range sets.StringKeySet(groupResourceMapping).List() { | ||||||
|  | 		rule := rbac.PolicyRule{} | ||||||
|  | 		rule.Verbs = c.Verbs | ||||||
|  | 		rule.Resources = groupResourceMapping[g] | ||||||
|  | 		rule.APIGroups = []string{g} | ||||||
|  | 		rule.ResourceNames = c.ResourceNames | ||||||
|  | 		rules = append(rules, rule) | ||||||
|  | 	} | ||||||
|  | 	role.Name = c.Name | ||||||
|  | 	role.Rules = rules | ||||||
|  |  | ||||||
|  | 	// Create role. | ||||||
|  | 	namespace, _, err := f.DefaultNamespace() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if !dryRun { | ||||||
|  | 		client, err := f.ClientSet() | ||||||
|  | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 		_, err = client.Rbac().Roles(namespace).Create(role) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if useShortOutput := outputFormat == "name"; useShortOutput || len(outputFormat) == 0 { | ||||||
|  | 		cmdutil.PrintSuccess(mapper, useShortOutput, cmdOut, "roles", c.Name, dryRun, "created") | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return f.PrintObject(cmd, mapper, role, cmdOut) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func arrayContains(s []string, e string) bool { | ||||||
|  | 	for _, a := range s { | ||||||
|  | 		if a == e { | ||||||
|  | 			return true | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return false | ||||||
|  | } | ||||||
							
								
								
									
										359
									
								
								pkg/kubectl/cmd/create_role_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										359
									
								
								pkg/kubectl/cmd/create_role_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,359 @@ | |||||||
|  | /* | ||||||
|  | Copyright 2017 The Kubernetes Authors. | ||||||
|  |  | ||||||
|  | Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  | you may not use this file except in compliance with the License. | ||||||
|  | You may obtain a copy of the License at | ||||||
|  |  | ||||||
|  |     http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  | ||||||
|  | Unless required by applicable law or agreed to in writing, software | ||||||
|  | distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  | See the License for the specific language governing permissions and | ||||||
|  | limitations under the License. | ||||||
|  | */ | ||||||
|  |  | ||||||
|  | package cmd | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"bytes" | ||||||
|  | 	"io" | ||||||
|  | 	"reflect" | ||||||
|  | 	"testing" | ||||||
|  |  | ||||||
|  | 	"k8s.io/apimachinery/pkg/apis/meta/v1" | ||||||
|  | 	"k8s.io/apimachinery/pkg/runtime" | ||||||
|  | 	"k8s.io/apimachinery/pkg/runtime/schema" | ||||||
|  | 	"k8s.io/kubernetes/pkg/apis/rbac" | ||||||
|  | 	cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type testRolePrinter struct { | ||||||
|  | 	CachedRole *rbac.Role | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (t *testRolePrinter) PrintObj(obj runtime.Object, out io.Writer) error { | ||||||
|  | 	t.CachedRole = obj.(*rbac.Role) | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (t *testRolePrinter) AfterPrint(output io.Writer, res string) error { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (t *testRolePrinter) HandledResources() []string { | ||||||
|  | 	return []string{} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestCreateRole(t *testing.T) { | ||||||
|  | 	roleName := "my-role" | ||||||
|  |  | ||||||
|  | 	f, tf, _, _ := cmdtesting.NewAPIFactory() | ||||||
|  | 	printer := &testRolePrinter{} | ||||||
|  | 	tf.Printer = printer | ||||||
|  | 	tf.Namespace = "test" | ||||||
|  |  | ||||||
|  | 	tests := map[string]struct { | ||||||
|  | 		verbs         string | ||||||
|  | 		resources     string | ||||||
|  | 		resourceNames string | ||||||
|  | 		expectedRole  *rbac.Role | ||||||
|  | 	}{ | ||||||
|  | 		"test-duplicate-resources": { | ||||||
|  | 			verbs:     "get,watch,list", | ||||||
|  | 			resources: "pods,pods", | ||||||
|  | 			expectedRole: &rbac.Role{ | ||||||
|  | 				ObjectMeta: v1.ObjectMeta{ | ||||||
|  | 					Name: roleName, | ||||||
|  | 				}, | ||||||
|  | 				Rules: []rbac.PolicyRule{ | ||||||
|  | 					{ | ||||||
|  | 						Verbs:         []string{"get", "watch", "list"}, | ||||||
|  | 						Resources:     []string{"pods"}, | ||||||
|  | 						APIGroups:     []string{""}, | ||||||
|  | 						ResourceNames: []string{}, | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 		"test-valid-case-with-multiple-apigroups": { | ||||||
|  | 			verbs:     "get,watch,list", | ||||||
|  | 			resources: "pods,deployments.extensions", | ||||||
|  | 			expectedRole: &rbac.Role{ | ||||||
|  | 				ObjectMeta: v1.ObjectMeta{ | ||||||
|  | 					Name: roleName, | ||||||
|  | 				}, | ||||||
|  | 				Rules: []rbac.PolicyRule{ | ||||||
|  | 					{ | ||||||
|  | 						Verbs:         []string{"get", "watch", "list"}, | ||||||
|  | 						Resources:     []string{"pods"}, | ||||||
|  | 						APIGroups:     []string{""}, | ||||||
|  | 						ResourceNames: []string{}, | ||||||
|  | 					}, | ||||||
|  | 					{ | ||||||
|  | 						Verbs:         []string{"get", "watch", "list"}, | ||||||
|  | 						Resources:     []string{"deployments"}, | ||||||
|  | 						APIGroups:     []string{"extensions"}, | ||||||
|  | 						ResourceNames: []string{}, | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for name, test := range tests { | ||||||
|  | 		buf := bytes.NewBuffer([]byte{}) | ||||||
|  | 		cmd := NewCmdCreateRole(f, buf) | ||||||
|  | 		cmd.Flags().Set("dry-run", "true") | ||||||
|  | 		cmd.Flags().Set("output", "object") | ||||||
|  | 		cmd.Flags().Set("verb", test.verbs) | ||||||
|  | 		cmd.Flags().Set("resource", test.resources) | ||||||
|  | 		if test.resourceNames != "" { | ||||||
|  | 			cmd.Flags().Set("resource-name", test.resourceNames) | ||||||
|  | 		} | ||||||
|  | 		cmd.Run(cmd, []string{roleName}) | ||||||
|  | 		if !reflect.DeepEqual(test.expectedRole, printer.CachedRole) { | ||||||
|  | 			t.Errorf("%s:\nexpected:\n%#v\nsaw:\n%#v", name, test.expectedRole, printer.CachedRole) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestValidate(t *testing.T) { | ||||||
|  | 	f, tf, _, _ := cmdtesting.NewAPIFactory() | ||||||
|  | 	tf.Printer = &testPrinter{} | ||||||
|  | 	tf.Namespace = "test" | ||||||
|  |  | ||||||
|  | 	tests := map[string]struct { | ||||||
|  | 		roleOptions *CreateRoleOptions | ||||||
|  | 		expectErr   bool | ||||||
|  | 	}{ | ||||||
|  | 		"test-missing-name": { | ||||||
|  | 			roleOptions: &CreateRoleOptions{}, | ||||||
|  | 			expectErr:   true, | ||||||
|  | 		}, | ||||||
|  | 		"test-missing-verb": { | ||||||
|  | 			roleOptions: &CreateRoleOptions{ | ||||||
|  | 				Name: "my-role", | ||||||
|  | 			}, | ||||||
|  | 			expectErr: true, | ||||||
|  | 		}, | ||||||
|  | 		"test-missing-resource": { | ||||||
|  | 			roleOptions: &CreateRoleOptions{ | ||||||
|  | 				Name:  "my-role", | ||||||
|  | 				Verbs: []string{"get"}, | ||||||
|  | 			}, | ||||||
|  | 			expectErr: true, | ||||||
|  | 		}, | ||||||
|  | 		"test-invalid-verb": { | ||||||
|  | 			roleOptions: &CreateRoleOptions{ | ||||||
|  | 				Name:  "my-role", | ||||||
|  | 				Verbs: []string{"invalid-verb"}, | ||||||
|  | 				Resources: []schema.GroupVersionResource{ | ||||||
|  | 					{ | ||||||
|  | 						Resource: "pods", | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 			expectErr: true, | ||||||
|  | 		}, | ||||||
|  | 		"test-invalid-resource": { | ||||||
|  | 			roleOptions: &CreateRoleOptions{ | ||||||
|  | 				Name:  "my-role", | ||||||
|  | 				Verbs: []string{"get"}, | ||||||
|  | 				Resources: []schema.GroupVersionResource{ | ||||||
|  | 					{ | ||||||
|  | 						Resource: "invalid-resource", | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 			expectErr: true, | ||||||
|  | 		}, | ||||||
|  | 		"test-resource-name-with-multiple-resources": { | ||||||
|  | 			roleOptions: &CreateRoleOptions{ | ||||||
|  | 				Name:  "my-role", | ||||||
|  | 				Verbs: []string{"get"}, | ||||||
|  | 				Resources: []schema.GroupVersionResource{ | ||||||
|  | 					{ | ||||||
|  | 						Resource: "pods", | ||||||
|  | 					}, | ||||||
|  | 					{ | ||||||
|  | 						Resource: "deployments", | ||||||
|  | 						Group:    "extensions", | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 				ResourceNames: []string{"foo"}, | ||||||
|  | 			}, | ||||||
|  | 			expectErr: true, | ||||||
|  | 		}, | ||||||
|  | 		"test-valid-case": { | ||||||
|  | 			roleOptions: &CreateRoleOptions{ | ||||||
|  | 				Name:  "my-role", | ||||||
|  | 				Verbs: []string{"get", "list"}, | ||||||
|  | 				Resources: []schema.GroupVersionResource{ | ||||||
|  | 					{ | ||||||
|  | 						Resource: "pods", | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 				ResourceNames: []string{"foo"}, | ||||||
|  | 			}, | ||||||
|  | 			expectErr: false, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for name, test := range tests { | ||||||
|  | 		err := test.roleOptions.Validate(f) | ||||||
|  | 		if test.expectErr && err != nil { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		if !test.expectErr && err != nil { | ||||||
|  | 			t.Errorf("%s: unexpected error: %v", name, err) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestComplete(t *testing.T) { | ||||||
|  | 	roleName := "my-role" | ||||||
|  |  | ||||||
|  | 	f, tf, _, _ := cmdtesting.NewAPIFactory() | ||||||
|  | 	tf.Printer = &testPrinter{} | ||||||
|  | 	tf.Namespace = "test" | ||||||
|  |  | ||||||
|  | 	buf := bytes.NewBuffer([]byte{}) | ||||||
|  | 	cmd := NewCmdCreateRole(f, buf) | ||||||
|  | 	cmd.Flags().Set("resource", "pods,deployments.extensions") | ||||||
|  |  | ||||||
|  | 	tests := map[string]struct { | ||||||
|  | 		params      []string | ||||||
|  | 		roleOptions *CreateRoleOptions | ||||||
|  | 		expected    *CreateRoleOptions | ||||||
|  | 		expectErr   bool | ||||||
|  | 	}{ | ||||||
|  | 		"test-missing-name": { | ||||||
|  | 			params:      []string{}, | ||||||
|  | 			roleOptions: &CreateRoleOptions{}, | ||||||
|  | 			expectErr:   true, | ||||||
|  | 		}, | ||||||
|  | 		"test-duplicate-verbs": { | ||||||
|  | 			params: []string{roleName}, | ||||||
|  | 			roleOptions: &CreateRoleOptions{ | ||||||
|  | 				Name: roleName, | ||||||
|  | 				Verbs: []string{ | ||||||
|  | 					"get", | ||||||
|  | 					"watch", | ||||||
|  | 					"list", | ||||||
|  | 					"get", | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 			expected: &CreateRoleOptions{ | ||||||
|  | 				Name: roleName, | ||||||
|  | 				Verbs: []string{ | ||||||
|  | 					"get", | ||||||
|  | 					"watch", | ||||||
|  | 					"list", | ||||||
|  | 				}, | ||||||
|  | 				Resources: []schema.GroupVersionResource{ | ||||||
|  | 					{ | ||||||
|  | 						Resource: "pods", | ||||||
|  | 						Group:    "", | ||||||
|  | 					}, | ||||||
|  | 					{ | ||||||
|  | 						Resource: "deployments", | ||||||
|  | 						Group:    "extensions", | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 				ResourceNames: []string{}, | ||||||
|  | 			}, | ||||||
|  | 			expectErr: false, | ||||||
|  | 		}, | ||||||
|  | 		"test-verball": { | ||||||
|  | 			params: []string{roleName}, | ||||||
|  | 			roleOptions: &CreateRoleOptions{ | ||||||
|  | 				Name: roleName, | ||||||
|  | 				Verbs: []string{ | ||||||
|  | 					"get", | ||||||
|  | 					"watch", | ||||||
|  | 					"list", | ||||||
|  | 					"*", | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 			expected: &CreateRoleOptions{ | ||||||
|  | 				Name:  roleName, | ||||||
|  | 				Verbs: []string{"*"}, | ||||||
|  | 				Resources: []schema.GroupVersionResource{ | ||||||
|  | 					{ | ||||||
|  | 						Resource: "pods", | ||||||
|  | 						Group:    "", | ||||||
|  | 					}, | ||||||
|  | 					{ | ||||||
|  | 						Resource: "deployments", | ||||||
|  | 						Group:    "extensions", | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 				ResourceNames: []string{}, | ||||||
|  | 			}, | ||||||
|  | 			expectErr: false, | ||||||
|  | 		}, | ||||||
|  | 		"test-duplicate-resourcenames": { | ||||||
|  | 			params: []string{roleName}, | ||||||
|  | 			roleOptions: &CreateRoleOptions{ | ||||||
|  | 				Name:          roleName, | ||||||
|  | 				Verbs:         []string{"*"}, | ||||||
|  | 				ResourceNames: []string{"foo", "foo"}, | ||||||
|  | 			}, | ||||||
|  | 			expected: &CreateRoleOptions{ | ||||||
|  | 				Name:  roleName, | ||||||
|  | 				Verbs: []string{"*"}, | ||||||
|  | 				Resources: []schema.GroupVersionResource{ | ||||||
|  | 					{ | ||||||
|  | 						Resource: "pods", | ||||||
|  | 						Group:    "", | ||||||
|  | 					}, | ||||||
|  | 					{ | ||||||
|  | 						Resource: "deployments", | ||||||
|  | 						Group:    "extensions", | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 				ResourceNames: []string{"foo"}, | ||||||
|  | 			}, | ||||||
|  | 			expectErr: false, | ||||||
|  | 		}, | ||||||
|  | 		"test-valid-complete-case": { | ||||||
|  | 			params: []string{roleName}, | ||||||
|  | 			roleOptions: &CreateRoleOptions{ | ||||||
|  | 				Name:          roleName, | ||||||
|  | 				Verbs:         []string{"*"}, | ||||||
|  | 				ResourceNames: []string{"foo"}, | ||||||
|  | 			}, | ||||||
|  | 			expected: &CreateRoleOptions{ | ||||||
|  | 				Name:  roleName, | ||||||
|  | 				Verbs: []string{"*"}, | ||||||
|  | 				Resources: []schema.GroupVersionResource{ | ||||||
|  | 					{ | ||||||
|  | 						Resource: "pods", | ||||||
|  | 						Group:    "", | ||||||
|  | 					}, | ||||||
|  | 					{ | ||||||
|  | 						Resource: "deployments", | ||||||
|  | 						Group:    "extensions", | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 				ResourceNames: []string{"foo"}, | ||||||
|  | 			}, | ||||||
|  | 			expectErr: false, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for name, test := range tests { | ||||||
|  | 		err := test.roleOptions.Complete(cmd, test.params) | ||||||
|  | 		if !test.expectErr && err != nil { | ||||||
|  | 			t.Errorf("%s: unexpected error: %v", name, err) | ||||||
|  | 		} | ||||||
|  | 		if test.expectErr && err != nil { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		if !reflect.DeepEqual(test.roleOptions, test.expected) { | ||||||
|  | 			t.Errorf("%s:\nexpected:\n%#v\nsaw:\n%#v", name, test.expected, test.roleOptions) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user
	 Kubernetes Submit Queue
					Kubernetes Submit Queue