Merge pull request #63851 from WanLinghao/ctl_create_aggegated_rules

Automatic merge from submit-queue (batch tested with PRs 62025, 63851, 64077, 63967, 63991). If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>.

Make kubectl could create clusterrole with aggregation rules

**What this PR does / why we need it**:
The clusterrole aggregation rule features are available since v1.9:
https://kubernetes.io/docs/admin/authorization/rbac/#aggregated-clusterroles
This patch makes kubectl could create clusterrole with aggregation rules.
**Which issue(s) this PR fixes** *(optional, in `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close the issue(s) when PR gets merged)*:
Fixes #

**Special notes for your reviewer**:

**Release note**:

```release-note
NONE
```
pull/8/head
Kubernetes Submit Queue 2018-05-22 08:40:20 -07:00 committed by GitHub
commit 007e936c6d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 100 additions and 5 deletions

View File

@ -3802,6 +3802,8 @@ run_clusterroles_tests() {
kubectl create "${kube_flags[@]}" clusterrole url-reader --verb=get --non-resource-url=/logs/* --non-resource-url=/healthz/*
kube::test::get_object_assert clusterrole/url-reader "{{range.rules}}{{range.verbs}}{{.}}:{{end}}{{end}}" 'get:'
kube::test::get_object_assert clusterrole/url-reader "{{range.rules}}{{range.nonResourceURLs}}{{.}}:{{end}}{{end}}" '/logs/\*:/healthz/\*:'
kubectl create "${kube_flags[@]}" clusterrole aggregation-reader --aggregation-rule="foo1=foo2"
kube::test::get_object_assert clusterrole/aggregation-reader "{{$id_field}}" 'aggregation-reader'
# test `kubectl create clusterrolebinding`
# test `kubectl set subject clusterrolebinding`

View File

@ -46,6 +46,7 @@ go_library(
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//vendor/k8s.io/apiserver/pkg/util/flag:go_default_library",
"//vendor/k8s.io/client-go/dynamic:go_default_library",
"//vendor/k8s.io/client-go/kubernetes/typed/batch/v1:go_default_library",
"//vendor/k8s.io/client-go/kubernetes/typed/rbac/v1:go_default_library",

View File

@ -24,6 +24,7 @@ import (
rbacv1 "k8s.io/api/rbac/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
utilflag "k8s.io/apiserver/pkg/util/flag"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
@ -48,7 +49,10 @@ var (
kubectl create clusterrole foo --verb=get,list,watch --resource=pods,pods/status
# Create a ClusterRole name "foo" with NonResourceURL specified
kubectl create clusterrole "foo" --verb=get --non-resource-url=/logs/*`))
kubectl create clusterrole "foo" --verb=get --non-resource-url=/logs/*
# Create a ClusterRole name "monitoring" with AggregationRule specified
kubectl create clusterrole monitoring --aggregation-rule="rbac.example.com/aggregate-to-monitoring=true"`))
// Valid nonResource verb list for validation.
validNonResourceVerbs = []string{"*", "get", "post", "put", "delete", "patch", "head", "options"}
@ -57,12 +61,14 @@ var (
type CreateClusterRoleOptions struct {
*CreateRoleOptions
NonResourceURLs []string
AggregationRule map[string]string
}
// ClusterRole is a command to ease creating ClusterRoles.
func NewCmdCreateClusterRole(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
c := &CreateClusterRoleOptions{
CreateRoleOptions: NewCreateRoleOptions(ioStreams),
AggregationRule: map[string]string{},
}
cmd := &cobra.Command{
Use: "clusterrole NAME --verb=verb --resource=resource.group [--resource-name=resourcename] [--dry-run]",
@ -86,6 +92,7 @@ func NewCmdCreateClusterRole(f cmdutil.Factory, ioStreams genericclioptions.IOSt
cmd.Flags().StringSliceVar(&c.NonResourceURLs, "non-resource-url", c.NonResourceURLs, "A partial url that user should have access to.")
cmd.Flags().StringSlice("resource", []string{}, "Resource that the rule applies to")
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")
cmd.Flags().Var(utilflag.NewMapStringString(&c.AggregationRule), "aggregation-rule", "An aggregation label selector for combining ClusterRoles.")
return cmd
}
@ -108,6 +115,13 @@ func (c *CreateClusterRoleOptions) Validate() error {
return fmt.Errorf("name must be specified")
}
if len(c.AggregationRule) > 0 {
if len(c.NonResourceURLs) > 0 || len(c.Verbs) > 0 || len(c.Resources) > 0 || len(c.ResourceNames) > 0 {
return fmt.Errorf("aggregation rule must be specified without nonResourceURLs, verbs, resources or resourceNames")
}
return nil
}
// validate verbs.
if len(c.Verbs) == 0 {
return fmt.Errorf("at least one verb must be specified")
@ -162,11 +176,23 @@ func (c *CreateClusterRoleOptions) RunCreateRole() error {
TypeMeta: metav1.TypeMeta{APIVersion: rbacv1.SchemeGroupVersion.String(), Kind: "ClusterRole"},
}
clusterRole.Name = c.Name
rules, err := generateResourcePolicyRules(c.Mapper, c.Verbs, c.Resources, c.ResourceNames, c.NonResourceURLs)
if err != nil {
return err
var err error
if len(c.AggregationRule) == 0 {
rules, err := generateResourcePolicyRules(c.Mapper, c.Verbs, c.Resources, c.ResourceNames, c.NonResourceURLs)
if err != nil {
return err
}
clusterRole.Rules = rules
} else {
clusterRole.AggregationRule = &rbacv1.AggregationRule{
ClusterRoleSelectors: []metav1.LabelSelector{
{
MatchLabels: c.AggregationRule,
},
},
}
}
clusterRole.Rules = rules
// Create ClusterRole.
if !c.DryRun {

View File

@ -22,6 +22,7 @@ import (
rbac "k8s.io/api/rbac/v1"
"k8s.io/apimachinery/pkg/api/equality"
"k8s.io/apimachinery/pkg/apis/meta/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
restclient "k8s.io/client-go/rest"
@ -47,6 +48,7 @@ func TestCreateClusterRole(t *testing.T) {
resources string
nonResourceURL string
resourceNames string
aggregationRule string
expectedClusterRole *rbac.ClusterRole
}{
"test-duplicate-resources": {
@ -130,6 +132,25 @@ func TestCreateClusterRole(t *testing.T) {
},
},
},
"test-aggregation-rules": {
aggregationRule: "foo1=foo2,foo3=foo4",
expectedClusterRole: &rbac.ClusterRole{
TypeMeta: v1.TypeMeta{APIVersion: "rbac.authorization.k8s.io/v1", Kind: "ClusterRole"},
ObjectMeta: v1.ObjectMeta{
Name: clusterRoleName,
},
AggregationRule: &rbac.AggregationRule{
ClusterRoleSelectors: []metav1.LabelSelector{
{
MatchLabels: map[string]string{
"foo1": "foo2",
"foo3": "foo4",
},
},
},
},
},
},
}
for name, test := range tests {
@ -140,6 +161,7 @@ func TestCreateClusterRole(t *testing.T) {
cmd.Flags().Set("verb", test.verbs)
cmd.Flags().Set("resource", test.resources)
cmd.Flags().Set("non-resource-url", test.nonResourceURL)
cmd.Flags().Set("aggregation-rule", test.aggregationRule)
if test.resourceNames != "" {
cmd.Flags().Set("resource-name", test.resourceNames)
}
@ -433,6 +455,50 @@ func TestClusterRoleValidate(t *testing.T) {
},
expectErr: false,
},
"test-aggregation-rule-with-verb": {
clusterRoleOptions: &CreateClusterRoleOptions{
CreateRoleOptions: &CreateRoleOptions{
Name: "my-clusterrole",
Verbs: []string{"get"},
},
AggregationRule: map[string]string{"foo-key": "foo-vlue"},
},
expectErr: true,
},
"test-aggregation-rule-with-resource": {
clusterRoleOptions: &CreateClusterRoleOptions{
CreateRoleOptions: &CreateRoleOptions{
Name: "my-clusterrole",
Resources: []ResourceOptions{
{
Resource: "replicasets",
SubResource: "scale",
},
},
},
AggregationRule: map[string]string{"foo-key": "foo-vlue"},
},
expectErr: true,
},
"test-aggregation-rule-with-no-resource-url": {
clusterRoleOptions: &CreateClusterRoleOptions{
CreateRoleOptions: &CreateRoleOptions{
Name: "my-clusterrole",
},
NonResourceURLs: []string{"/logs/"},
AggregationRule: map[string]string{"foo-key": "foo-vlue"},
},
expectErr: true,
},
"test-aggregation-rule": {
clusterRoleOptions: &CreateClusterRoleOptions{
CreateRoleOptions: &CreateRoleOptions{
Name: "my-clusterrole",
},
AggregationRule: map[string]string{"foo-key": "foo-vlue"},
},
expectErr: false,
},
}
for name, test := range tests {