Merge pull request #41538 from xingzhou/clusterrole

Automatic merge from submit-queue (batch tested with PRs 41146, 41486, 41482, 41538, 41784)

Added `kubectl create clusterrole` command.

Added `kubectl create clusterrole` command.

Fixed part of #39596 

**Special notes for your reviewer**:
@deads2k, please help to review this patch, thanks

**Release note**:
```
   Added one new command `kubectl create clusterrole` to help user create a single ClusterRole from command line.
```
This commit is contained in:
Kubernetes Submit Queue 2017-02-22 21:09:36 -08:00 committed by GitHub
commit c99ae4b436
10 changed files with 355 additions and 60 deletions

View File

@ -38,6 +38,7 @@ docs/man/man1/kubectl-config.1
docs/man/man1/kubectl-convert.1 docs/man/man1/kubectl-convert.1
docs/man/man1/kubectl-cordon.1 docs/man/man1/kubectl-cordon.1
docs/man/man1/kubectl-cp.1 docs/man/man1/kubectl-cp.1
docs/man/man1/kubectl-create-clusterrole.1
docs/man/man1/kubectl-create-clusterrolebinding.1 docs/man/man1/kubectl-create-clusterrolebinding.1
docs/man/man1/kubectl-create-configmap.1 docs/man/man1/kubectl-create-configmap.1
docs/man/man1/kubectl-create-deployment.1 docs/man/man1/kubectl-create-deployment.1
@ -124,6 +125,7 @@ docs/user-guide/kubectl/kubectl_convert.md
docs/user-guide/kubectl/kubectl_cordon.md docs/user-guide/kubectl/kubectl_cordon.md
docs/user-guide/kubectl/kubectl_cp.md docs/user-guide/kubectl/kubectl_cp.md
docs/user-guide/kubectl/kubectl_create.md docs/user-guide/kubectl/kubectl_create.md
docs/user-guide/kubectl/kubectl_create_clusterrole.md
docs/user-guide/kubectl/kubectl_create_clusterrolebinding.md docs/user-guide/kubectl/kubectl_create_clusterrolebinding.md
docs/user-guide/kubectl/kubectl_create_configmap.md docs/user-guide/kubectl/kubectl_create_configmap.md
docs/user-guide/kubectl/kubectl_create_deployment.md docs/user-guide/kubectl/kubectl_create_deployment.md

View 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.

View 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.

View File

@ -2803,6 +2803,23 @@ runTests() {
# make sure the server was properly bootstrapped with clusterroles and bindings # make sure the server was properly bootstrapped with clusterroles and bindings
kube::test::get_object_assert clusterroles/cluster-admin "{{.metadata.name}}" 'cluster-admin' kube::test::get_object_assert clusterroles/cluster-admin "{{.metadata.name}}" 'cluster-admin'
kube::test::get_object_assert clusterrolebindings/cluster-admin "{{.metadata.name}}" 'cluster-admin' kube::test::get_object_assert clusterrolebindings/cluster-admin "{{.metadata.name}}" 'cluster-admin'
# test `kubectl create clusterrole`
kubectl create "${kube_flags[@]}" clusterrole pod-admin --verb=* --resource=pods
kube::test::get_object_assert clusterrole/pod-admin "{{range.rules}}{{range.verbs}}{{.}}:{{end}}{{end}}" '\*:'
kube::test::get_object_assert clusterrole/pod-admin "{{range.rules}}{{range.resources}}{{.}}:{{end}}{{end}}" 'pods:'
kube::test::get_object_assert clusterrole/pod-admin "{{range.rules}}{{range.apiGroups}}{{.}}:{{end}}{{end}}" ':'
kubectl create "${kube_flags[@]}" clusterrole resource-reader --verb=get,list --resource=pods,deployments.extensions
kube::test::get_object_assert clusterrole/resource-reader "{{range.rules}}{{range.verbs}}{{.}}:{{end}}{{end}}" 'get:list:get:list:'
kube::test::get_object_assert clusterrole/resource-reader "{{range.rules}}{{range.resources}}{{.}}:{{end}}{{end}}" 'pods:deployments:'
kube::test::get_object_assert clusterrole/resource-reader "{{range.rules}}{{range.apiGroups}}{{.}}:{{end}}{{end}}" ':extensions:'
kubectl create "${kube_flags[@]}" clusterrole resourcename-reader --verb=get,list --resource=pods --resource-name=foo
kube::test::get_object_assert clusterrole/resourcename-reader "{{range.rules}}{{range.verbs}}{{.}}:{{end}}{{end}}" 'get:list:'
kube::test::get_object_assert clusterrole/resourcename-reader "{{range.rules}}{{range.resources}}{{.}}:{{end}}{{end}}" 'pods:'
kube::test::get_object_assert clusterrole/resourcename-reader "{{range.rules}}{{range.apiGroups}}{{.}}:{{end}}{{end}}" ':'
kube::test::get_object_assert clusterrole/resourcename-reader "{{range.rules}}{{range.resourceNames}}{{.}}:{{end}}{{end}}" 'foo:'
# test `kubectl create clusterrolebinding`
kubectl create "${kube_flags[@]}" clusterrolebinding super-admin --clusterrole=admin --user=super-admin kubectl create "${kube_flags[@]}" clusterrolebinding super-admin --clusterrole=admin --user=super-admin
kube::test::get_object_assert clusterrolebinding/super-admin "{{range.subjects}}{{.name}}:{{end}}" 'super-admin:' kube::test::get_object_assert clusterrolebinding/super-admin "{{range.subjects}}{{.name}}:{{end}}" 'super-admin:'
kubectl create "${kube_flags[@]}" clusterrolebinding super-group --clusterrole=admin --group=the-group kubectl create "${kube_flags[@]}" clusterrolebinding super-group --clusterrole=admin --group=the-group

View File

@ -25,6 +25,7 @@ go_library(
"convert.go", "convert.go",
"cp.go", "cp.go",
"create.go", "create.go",
"create_clusterrole.go",
"create_clusterrolebinding.go", "create_clusterrolebinding.go",
"create_configmap.go", "create_configmap.go",
"create_deployment.go", "create_deployment.go",
@ -75,6 +76,7 @@ go_library(
"//pkg/apis/rbac: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/clientset_generated/internalclientset/typed/rbac/internalversion:go_default_library",
"//pkg/client/unversioned:go_default_library", "//pkg/client/unversioned:go_default_library",
"//pkg/client/unversioned/remotecommand:go_default_library", "//pkg/client/unversioned/remotecommand:go_default_library",
"//pkg/kubectl:go_default_library", "//pkg/kubectl:go_default_library",
@ -143,6 +145,7 @@ go_test(
"clusterinfo_dump_test.go", "clusterinfo_dump_test.go",
"cmd_test.go", "cmd_test.go",
"cp_test.go", "cp_test.go",
"create_clusterrole_test.go",
"create_configmap_test.go", "create_configmap_test.go",
"create_deployment_test.go", "create_deployment_test.go",
"create_namespace_test.go", "create_namespace_test.go",

View File

@ -95,6 +95,7 @@ func NewCmdCreate(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command {
cmd.AddCommand(NewCmdCreateServiceAccount(f, out)) cmd.AddCommand(NewCmdCreateServiceAccount(f, out))
cmd.AddCommand(NewCmdCreateService(f, out, errOut)) cmd.AddCommand(NewCmdCreateService(f, out, errOut))
cmd.AddCommand(NewCmdCreateDeployment(f, out)) cmd.AddCommand(NewCmdCreateDeployment(f, out))
cmd.AddCommand(NewCmdCreateClusterRole(f, out))
cmd.AddCommand(NewCmdCreateClusterRoleBinding(f, out)) cmd.AddCommand(NewCmdCreateClusterRoleBinding(f, out))
cmd.AddCommand(NewCmdCreateRole(f, out)) cmd.AddCommand(NewCmdCreateRole(f, out))
cmd.AddCommand(NewCmdCreateRoleBinding(f, out)) cmd.AddCommand(NewCmdCreateRoleBinding(f, out))

View File

@ -0,0 +1,97 @@
/*
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 (
"io"
"github.com/spf13/cobra"
"k8s.io/kubernetes/pkg/apis/rbac"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
)
var (
clusterRoleLong = templates.LongDesc(`
Create a ClusterRole.`)
clusterRoleExample = templates.Examples(`
# Create a ClusterRole named "pod-reader" that allows user to perform "get", "watch" and "list" on pods
kubectl create clusterrole pod-reader --verb=get,list,watch --resource=pods
# Create a ClusterRole named "pod-reader" with ResourceName specified
kubectl create clusterrole pod-reader --verb=get,list,watch --resource=pods --resource-name=readablepod`)
)
type CreateClusterRoleOptions struct {
*CreateRoleOptions
}
// ClusterRole is a command to ease creating ClusterRoles.
func NewCmdCreateClusterRole(f cmdutil.Factory, cmdOut io.Writer) *cobra.Command {
c := &CreateClusterRoleOptions{
CreateRoleOptions: &CreateRoleOptions{
Out: cmdOut,
},
}
cmd := &cobra.Command{
Use: "clusterrole NAME --verb=verb --resource=resource.group [--resource-name=resourcename] [--dry-run]",
Short: clusterRoleLong,
Long: clusterRoleLong,
Example: clusterRoleExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(c.Complete(f, cmd, args))
cmdutil.CheckErr(c.Validate())
cmdutil.CheckErr(c.RunCreateRole())
},
}
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 *CreateClusterRoleOptions) RunCreateRole() error {
clusterRole := &rbac.ClusterRole{}
clusterRole.Name = c.Name
rules, err := generateResourcePolicyRules(c.Mapper, c.Verbs, c.Resources, c.ResourceNames)
if err != nil {
return err
}
clusterRole.Rules = rules
// Create ClusterRole.
if !c.DryRun {
_, err = c.Client.ClusterRoles().Create(clusterRole)
if err != nil {
return err
}
}
if useShortOutput := c.OutputFormat == "name"; useShortOutput || len(c.OutputFormat) == 0 {
cmdutil.PrintSuccess(c.Mapper, useShortOutput, c.Out, "clusterroles", c.Name, c.DryRun, "created")
return nil
}
return c.PrintObject(clusterRole)
}

View File

@ -0,0 +1,122 @@
/*
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/client-go/rest/fake"
"k8s.io/kubernetes/pkg/apis/rbac"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
)
type testClusterRolePrinter struct {
CachedClusterRole *rbac.ClusterRole
}
func (t *testClusterRolePrinter) PrintObj(obj runtime.Object, out io.Writer) error {
t.CachedClusterRole = obj.(*rbac.ClusterRole)
return nil
}
func (t *testClusterRolePrinter) AfterPrint(output io.Writer, res string) error {
return nil
}
func (t *testClusterRolePrinter) HandledResources() []string {
return []string{}
}
func TestCreateClusterRole(t *testing.T) {
clusterRoleName := "my-cluster-role"
f, tf, _, _ := cmdtesting.NewAPIFactory()
printer := &testClusterRolePrinter{}
tf.Printer = printer
tf.Namespace = "test"
tf.Client = &fake.RESTClient{}
tf.ClientConfig = defaultClientConfig()
tests := map[string]struct {
verbs string
resources string
resourceNames string
expectedClusterRole *rbac.ClusterRole
}{
"test-duplicate-resources": {
verbs: "get,watch,list",
resources: "pods,pods",
expectedClusterRole: &rbac.ClusterRole{
ObjectMeta: v1.ObjectMeta{
Name: clusterRoleName,
},
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",
expectedClusterRole: &rbac.ClusterRole{
ObjectMeta: v1.ObjectMeta{
Name: clusterRoleName,
},
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 := NewCmdCreateClusterRole(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{clusterRoleName})
if !reflect.DeepEqual(test.expectedClusterRole, printer.CachedClusterRole) {
t.Errorf("%s:\nexpected:\n%#v\nsaw:\n%#v", name, test.expectedClusterRole, printer.CachedClusterRole)
}
}
}

View File

@ -23,9 +23,12 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/sets"
"k8s.io/kubernetes/pkg/apis/rbac" "k8s.io/kubernetes/pkg/apis/rbac"
internalversionrbac "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/rbac/internalversion"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates" "k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
) )
@ -50,20 +53,30 @@ type CreateRoleOptions struct {
Verbs []string Verbs []string
Resources []schema.GroupVersionResource Resources []schema.GroupVersionResource
ResourceNames []string ResourceNames []string
DryRun bool
OutputFormat string
Namespace string
Client internalversionrbac.RbacInterface
Mapper meta.RESTMapper
Out io.Writer
PrintObject func(obj runtime.Object) error
} }
// Role is a command to ease creating Roles. // Role is a command to ease creating Roles.
func NewCmdCreateRole(f cmdutil.Factory, cmdOut io.Writer) *cobra.Command { func NewCmdCreateRole(f cmdutil.Factory, cmdOut io.Writer) *cobra.Command {
c := &CreateRoleOptions{} c := &CreateRoleOptions{
Out: cmdOut,
}
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "role NAME --verb=verb --resource=resource.group [--resource-name=resourcename] [--dry-run]", Use: "role NAME --verb=verb --resource=resource.group [--resource-name=resourcename] [--dry-run]",
Short: roleLong, Short: roleLong,
Long: roleLong, Long: roleLong,
Example: roleExample, Example: roleExample,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(c.Complete(cmd, args)) cmdutil.CheckErr(c.Complete(f, cmd, args))
cmdutil.CheckErr(c.Validate(f)) cmdutil.CheckErr(c.Validate())
cmdutil.CheckErr(c.RunCreateRole(f, cmdOut, cmd, args)) cmdutil.CheckErr(c.RunCreateRole())
}, },
} }
cmdutil.AddApplyAnnotationFlags(cmd) cmdutil.AddApplyAnnotationFlags(cmd)
@ -77,7 +90,7 @@ func NewCmdCreateRole(f cmdutil.Factory, cmdOut io.Writer) *cobra.Command {
return cmd return cmd
} }
func (c *CreateRoleOptions) Complete(cmd *cobra.Command, args []string) error { func (c *CreateRoleOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
name, err := NameFromCommandArgs(cmd, args) name, err := NameFromCommandArgs(cmd, args)
if err != nil { if err != nil {
return err return err
@ -120,10 +133,31 @@ func (c *CreateRoleOptions) Complete(cmd *cobra.Command, args []string) error {
} }
c.ResourceNames = resourceNames c.ResourceNames = resourceNames
// Complete other options for Run.
c.Mapper, _ = f.Object()
c.DryRun = cmdutil.GetDryRunFlag(cmd)
c.OutputFormat = cmdutil.GetFlagString(cmd, "output")
c.Namespace, _, err = f.DefaultNamespace()
if err != nil {
return err
}
c.PrintObject = func(obj runtime.Object) error {
return f.PrintObject(cmd, c.Mapper, obj, c.Out)
}
clientSet, err := f.ClientSet()
if err != nil {
return err
}
c.Client = clientSet.Rbac()
return nil return nil
} }
func (c *CreateRoleOptions) Validate(f cmdutil.Factory) error { func (c *CreateRoleOptions) Validate() error {
if c.Name == "" { if c.Name == "" {
return fmt.Errorf("name must be specified") return fmt.Errorf("name must be specified")
} }
@ -140,14 +174,12 @@ func (c *CreateRoleOptions) Validate(f cmdutil.Factory) error {
} }
// validate resources. // validate resources.
mapper, _ := f.Object()
if len(c.Resources) == 0 { if len(c.Resources) == 0 {
return fmt.Errorf("at least one resource must be specified") return fmt.Errorf("at least one resource must be specified")
} }
for _, r := range c.Resources { for _, r := range c.Resources {
_, err := mapper.ResourceFor(r) _, err := c.Mapper.ResourceFor(r)
if err != nil { if err != nil {
return err return err
} }
@ -161,67 +193,29 @@ func (c *CreateRoleOptions) Validate(f cmdutil.Factory) error {
return nil return nil
} }
func (c *CreateRoleOptions) RunCreateRole(f cmdutil.Factory, cmdOut io.Writer, cmd *cobra.Command, args []string) error { func (c *CreateRoleOptions) RunCreateRole() 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{} 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.Name = c.Name
role.Rules = rules rules, err := generateResourcePolicyRules(c.Mapper, c.Verbs, c.Resources, c.ResourceNames)
// Create role.
namespace, _, err := f.DefaultNamespace()
if err != nil { if err != nil {
return err return err
} }
role.Rules = rules
if !dryRun { // Create role.
client, err := f.ClientSet() if !c.DryRun {
if err != nil { _, err = c.Client.Roles(c.Namespace).Create(role)
return err
}
_, err = client.Rbac().Roles(namespace).Create(role)
if err != nil { if err != nil {
return err return err
} }
} }
if useShortOutput := outputFormat == "name"; useShortOutput || len(outputFormat) == 0 { if useShortOutput := c.OutputFormat == "name"; useShortOutput || len(c.OutputFormat) == 0 {
cmdutil.PrintSuccess(mapper, useShortOutput, cmdOut, "roles", c.Name, dryRun, "created") cmdutil.PrintSuccess(c.Mapper, useShortOutput, c.Out, "roles", c.Name, c.DryRun, "created")
return nil return nil
} }
return f.PrintObject(cmd, mapper, role, cmdOut) return c.PrintObject(role)
} }
func arrayContains(s []string, e string) bool { func arrayContains(s []string, e string) bool {
@ -232,3 +226,37 @@ func arrayContains(s []string, e string) bool {
} }
return false return false
} }
func generateResourcePolicyRules(mapper meta.RESTMapper, verbs []string, resources []schema.GroupVersionResource, resourceNames []string) ([]rbac.PolicyRule, error) {
// 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 resources {
resource, err := mapper.ResourceFor(r)
if err != nil {
return []rbac.PolicyRule{}, err
}
if !arrayContains(groupResourceMapping[resource.Group], resource.Resource) {
groupResourceMapping[resource.Group] = append(groupResourceMapping[resource.Group], resource.Resource)
}
}
// Create separate rule for each of the api group.
rules := []rbac.PolicyRule{}
for _, g := range sets.StringKeySet(groupResourceMapping).List() {
rule := rbac.PolicyRule{}
rule.Verbs = verbs
rule.Resources = groupResourceMapping[g]
rule.APIGroups = []string{g}
rule.ResourceNames = resourceNames
rules = append(rules, rule)
}
return rules, nil
}

View File

@ -25,6 +25,7 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/rest/fake"
"k8s.io/kubernetes/pkg/apis/rbac" "k8s.io/kubernetes/pkg/apis/rbac"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing" cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
) )
@ -53,6 +54,8 @@ func TestCreateRole(t *testing.T) {
printer := &testRolePrinter{} printer := &testRolePrinter{}
tf.Printer = printer tf.Printer = printer
tf.Namespace = "test" tf.Namespace = "test"
tf.Client = &fake.RESTClient{}
tf.ClientConfig = defaultClientConfig()
tests := map[string]struct { tests := map[string]struct {
verbs string verbs string
@ -214,7 +217,8 @@ func TestValidate(t *testing.T) {
} }
for name, test := range tests { for name, test := range tests {
err := test.roleOptions.Validate(f) test.roleOptions.Mapper, _ = f.Object()
err := test.roleOptions.Validate()
if test.expectErr && err != nil { if test.expectErr && err != nil {
continue continue
} }
@ -230,6 +234,8 @@ func TestComplete(t *testing.T) {
f, tf, _, _ := cmdtesting.NewAPIFactory() f, tf, _, _ := cmdtesting.NewAPIFactory()
tf.Printer = &testPrinter{} tf.Printer = &testPrinter{}
tf.Namespace = "test" tf.Namespace = "test"
tf.Client = &fake.RESTClient{}
tf.ClientConfig = defaultClientConfig()
buf := bytes.NewBuffer([]byte{}) buf := bytes.NewBuffer([]byte{})
cmd := NewCmdCreateRole(f, buf) cmd := NewCmdCreateRole(f, buf)
@ -357,15 +363,28 @@ func TestComplete(t *testing.T) {
} }
for name, test := range tests { for name, test := range tests {
err := test.roleOptions.Complete(cmd, test.params) err := test.roleOptions.Complete(f, cmd, test.params)
if !test.expectErr && err != nil { if !test.expectErr && err != nil {
t.Errorf("%s: unexpected error: %v", name, err) t.Errorf("%s: unexpected error: %v", name, err)
} }
if test.expectErr && err != nil { if test.expectErr && err != nil {
continue continue
} }
if !reflect.DeepEqual(test.roleOptions, test.expected) {
t.Errorf("%s:\nexpected:\n%#v\nsaw:\n%#v", name, test.expected, test.roleOptions) if test.roleOptions.Name != test.expected.Name {
t.Errorf("%s:\nexpected name:\n%#v\nsaw name:\n%#v", name, test.expected.Name, test.roleOptions.Name)
}
if !reflect.DeepEqual(test.roleOptions.Verbs, test.expected.Verbs) {
t.Errorf("%s:\nexpected verbs:\n%#v\nsaw verbs:\n%#v", name, test.expected.Verbs, test.roleOptions.Verbs)
}
if !reflect.DeepEqual(test.roleOptions.Resources, test.expected.Resources) {
t.Errorf("%s:\nexpected resources:\n%#v\nsaw resources:\n%#v", name, test.expected.Resources, test.roleOptions.Resources)
}
if !reflect.DeepEqual(test.roleOptions.ResourceNames, test.expected.ResourceNames) {
t.Errorf("%s:\nexpected resource names:\n%#v\nsaw resource names:\n%#v", name, test.expected.ResourceNames, test.roleOptions.ResourceNames)
} }
} }
} }