Skip to content

Commit 8ab7f6f

Browse files
committed
Remove dependency between create rolebinding
1 parent 1dff024 commit 8ab7f6f

File tree

2 files changed

+240
-129
lines changed

2 files changed

+240
-129
lines changed

staging/src/k8s.io/kubectl/pkg/cmd/create/create_rolebinding.go

Lines changed: 187 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,21 @@ limitations under the License.
1717
package create
1818

1919
import (
20+
"context"
21+
"fmt"
22+
"strings"
23+
2024
"github.com/spf13/cobra"
2125

26+
rbacv1 "k8s.io/api/rbac/v1"
27+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
28+
"k8s.io/apimachinery/pkg/runtime"
2229
"k8s.io/cli-runtime/pkg/genericclioptions"
30+
"k8s.io/cli-runtime/pkg/resource"
31+
rbacclientv1 "k8s.io/client-go/kubernetes/typed/rbac/v1"
2332
cmdutil "k8s.io/kubectl/pkg/cmd/util"
24-
"k8s.io/kubectl/pkg/generate"
25-
generateversioned "k8s.io/kubectl/pkg/generate/versioned"
33+
"k8s.io/kubectl/pkg/scheme"
34+
"k8s.io/kubectl/pkg/util"
2635
"k8s.io/kubectl/pkg/util/i18n"
2736
"k8s.io/kubectl/pkg/util/templates"
2837
)
@@ -37,15 +46,42 @@ var (
3746
)
3847

3948
// RoleBindingOpts holds the options for 'create rolebinding' sub command
40-
type RoleBindingOpts struct {
41-
CreateSubcommandOptions *CreateSubcommandOptions
49+
type RoleBindingOptions struct {
50+
PrintFlags *genericclioptions.PrintFlags
51+
PrintObj func(obj runtime.Object) error
52+
53+
Name string
54+
Namespace string
55+
EnforceNamespace bool
56+
ClusterRole string
57+
Role string
58+
Users []string
59+
Groups []string
60+
ServiceAccounts []string
61+
FieldManager string
62+
CreateAnnotation bool
63+
64+
Client rbacclientv1.RbacV1Interface
65+
DryRunStrategy cmdutil.DryRunStrategy
66+
DryRunVerifier *resource.DryRunVerifier
67+
68+
genericclioptions.IOStreams
69+
}
70+
71+
// NewRoleBindingOptions creates a new *RoleBindingOptions with sane defaults
72+
func NewRoleBindingOptions(ioStreams genericclioptions.IOStreams) *RoleBindingOptions {
73+
return &RoleBindingOptions{
74+
Users: []string{},
75+
Groups: []string{},
76+
ServiceAccounts: []string{},
77+
PrintFlags: genericclioptions.NewPrintFlags("created").WithTypeSetter(scheme.Scheme),
78+
IOStreams: ioStreams,
79+
}
4280
}
4381

4482
// NewCmdCreateRoleBinding returns an initialized Command instance for 'create rolebinding' sub command
4583
func NewCmdCreateRoleBinding(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
46-
o := &RoleBindingOpts{
47-
CreateSubcommandOptions: NewCreateSubcommandOptions(ioStreams),
48-
}
84+
o := NewRoleBindingOptions(ioStreams)
4985

5086
cmd := &cobra.Command{
5187
Use: "rolebinding NAME --clusterrole=NAME|--role=NAME [--user=username] [--group=groupname] [--serviceaccount=namespace:serviceaccountname] [--dry-run=server|client|none]",
@@ -55,50 +91,167 @@ func NewCmdCreateRoleBinding(f cmdutil.Factory, ioStreams genericclioptions.IOSt
5591
Example: roleBindingExample,
5692
Run: func(cmd *cobra.Command, args []string) {
5793
cmdutil.CheckErr(o.Complete(f, cmd, args))
94+
cmdutil.CheckErr(o.Validate())
5895
cmdutil.CheckErr(o.Run())
5996
},
6097
}
6198

62-
o.CreateSubcommandOptions.PrintFlags.AddFlags(cmd)
99+
o.PrintFlags.AddFlags(cmd)
63100

64101
cmdutil.AddApplyAnnotationFlags(cmd)
65102
cmdutil.AddValidateFlags(cmd)
66-
cmdutil.AddGeneratorFlags(cmd, generateversioned.RoleBindingV1GeneratorName)
67-
cmd.Flags().String("clusterrole", "", i18n.T("ClusterRole this RoleBinding should reference"))
68-
cmd.Flags().String("role", "", i18n.T("Role this RoleBinding should reference"))
69-
cmd.Flags().StringArray("user", []string{}, "Usernames to bind to the role")
70-
cmd.Flags().StringArray("group", []string{}, "Groups to bind to the role")
71-
cmd.Flags().StringArray("serviceaccount", []string{}, "Service accounts to bind to the role, in the format <namespace>:<name>")
72-
cmdutil.AddFieldManagerFlagVar(cmd, &o.CreateSubcommandOptions.FieldManager, "kubectl-create")
103+
cmdutil.AddDryRunFlag(cmd)
104+
cmd.Flags().StringVar(&o.ClusterRole, "clusterrole", "", i18n.T("ClusterRole this RoleBinding should reference"))
105+
cmd.Flags().StringVar(&o.Role, "role", "", i18n.T("Role this RoleBinding should reference"))
106+
cmd.Flags().StringArrayVar(&o.Users, "user", o.Users, "Usernames to bind to the role")
107+
cmd.Flags().StringArrayVar(&o.Groups, "group", o.Groups, "Groups to bind to the role")
108+
cmd.Flags().StringArrayVar(&o.ServiceAccounts, "serviceaccount", o.ServiceAccounts, "Service accounts to bind to the role, in the format <namespace>:<name>")
109+
cmdutil.AddFieldManagerFlagVar(cmd, &o.FieldManager, "kubectl-create")
73110
return cmd
74111
}
75112

76113
// Complete completes all the required options
77-
func (o *RoleBindingOpts) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
78-
name, err := NameFromCommandArgs(cmd, args)
114+
func (o *RoleBindingOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
115+
var err error
116+
o.Name, err = NameFromCommandArgs(cmd, args)
117+
if err != nil {
118+
return err
119+
}
120+
clientConfig, err := f.ToRESTConfig()
121+
if err != nil {
122+
return err
123+
}
124+
o.Client, err = rbacclientv1.NewForConfig(clientConfig)
79125
if err != nil {
80126
return err
81127
}
82128

83-
var generator generate.StructuredGenerator
84-
switch generatorName := cmdutil.GetFlagString(cmd, "generator"); generatorName {
85-
case generateversioned.RoleBindingV1GeneratorName:
86-
generator = &generateversioned.RoleBindingGeneratorV1{
87-
Name: name,
88-
ClusterRole: cmdutil.GetFlagString(cmd, "clusterrole"),
89-
Role: cmdutil.GetFlagString(cmd, "role"),
90-
Users: cmdutil.GetFlagStringArray(cmd, "user"),
91-
Groups: cmdutil.GetFlagStringArray(cmd, "group"),
92-
ServiceAccounts: cmdutil.GetFlagStringArray(cmd, "serviceaccount"),
93-
}
94-
default:
95-
return errUnsupportedGenerator(cmd, generatorName)
129+
o.Namespace, o.EnforceNamespace, err = f.ToRawKubeConfigLoader().Namespace()
130+
if err != nil {
131+
return err
96132
}
97133

98-
return o.CreateSubcommandOptions.Complete(f, cmd, args, generator)
134+
o.CreateAnnotation = cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag)
135+
o.DryRunStrategy, err = cmdutil.GetDryRunStrategy(cmd)
136+
if err != nil {
137+
return err
138+
}
139+
dynamicCient, err := f.DynamicClient()
140+
if err != nil {
141+
return err
142+
}
143+
discoveryClient, err := f.ToDiscoveryClient()
144+
if err != nil {
145+
return err
146+
}
147+
o.DryRunVerifier = resource.NewDryRunVerifier(dynamicCient, discoveryClient)
148+
cmdutil.PrintFlagsWithDryRunStrategy(o.PrintFlags, o.DryRunStrategy)
149+
printer, err := o.PrintFlags.ToPrinter()
150+
if err != nil {
151+
return err
152+
}
153+
o.PrintObj = func(obj runtime.Object) error {
154+
return printer.PrintObj(obj, o.Out)
155+
}
156+
return nil
99157
}
100158

101-
// Run calls the CreateSubcommandOptions.Run in RoleBindingOpts instance
102-
func (o *RoleBindingOpts) Run() error {
103-
return o.CreateSubcommandOptions.Run()
159+
// Validate validates required fields are set
160+
func (o *RoleBindingOptions) Validate() error {
161+
if len(o.Name) == 0 {
162+
return fmt.Errorf("name must be specified")
163+
}
164+
if (len(o.ClusterRole) == 0) == (len(o.Role) == 0) {
165+
return fmt.Errorf("exactly one of clusterrole or role must be specified")
166+
}
167+
return nil
168+
}
169+
170+
// Run performs the execution of 'create rolebinding' sub command
171+
func (o *RoleBindingOptions) Run() error {
172+
roleBinding, err := o.createRoleBinding()
173+
if err != nil {
174+
return err
175+
}
176+
if err := util.CreateOrUpdateAnnotation(o.CreateAnnotation, roleBinding, scheme.DefaultJSONEncoder()); err != nil {
177+
return err
178+
}
179+
180+
if o.DryRunStrategy != cmdutil.DryRunClient {
181+
createOptions := metav1.CreateOptions{}
182+
if o.FieldManager != "" {
183+
createOptions.FieldManager = o.FieldManager
184+
}
185+
if o.DryRunStrategy == cmdutil.DryRunServer {
186+
if err := o.DryRunVerifier.HasSupport(roleBinding.GroupVersionKind()); err != nil {
187+
return err
188+
}
189+
createOptions.DryRun = []string{metav1.DryRunAll}
190+
}
191+
roleBinding, err = o.Client.RoleBindings(o.Namespace).Create(context.TODO(), roleBinding, createOptions)
192+
if err != nil {
193+
return fmt.Errorf("failed to create rolebinding: %v", err)
194+
}
195+
}
196+
return o.PrintObj(roleBinding)
197+
}
198+
199+
func (o *RoleBindingOptions) createRoleBinding() (*rbacv1.RoleBinding, error) {
200+
namespace := ""
201+
if o.EnforceNamespace {
202+
namespace = o.Namespace
203+
}
204+
205+
roleBinding := &rbacv1.RoleBinding{
206+
TypeMeta: metav1.TypeMeta{APIVersion: rbacv1.SchemeGroupVersion.String(), Kind: "RoleBinding"},
207+
ObjectMeta: metav1.ObjectMeta{
208+
Name: o.Name,
209+
Namespace: namespace,
210+
},
211+
}
212+
213+
switch {
214+
case len(o.Role) > 0:
215+
roleBinding.RoleRef = rbacv1.RoleRef{
216+
APIGroup: rbacv1.GroupName,
217+
Kind: "Role",
218+
Name: o.Role,
219+
}
220+
case len(o.ClusterRole) > 0:
221+
roleBinding.RoleRef = rbacv1.RoleRef{
222+
APIGroup: rbacv1.GroupName,
223+
Kind: "ClusterRole",
224+
Name: o.ClusterRole,
225+
}
226+
}
227+
228+
for _, user := range o.Users {
229+
roleBinding.Subjects = append(roleBinding.Subjects, rbacv1.Subject{
230+
Kind: rbacv1.UserKind,
231+
APIGroup: rbacv1.GroupName,
232+
Name: user,
233+
})
234+
}
235+
236+
for _, group := range o.Groups {
237+
roleBinding.Subjects = append(roleBinding.Subjects, rbacv1.Subject{
238+
Kind: rbacv1.GroupKind,
239+
APIGroup: rbacv1.GroupName,
240+
Name: group,
241+
})
242+
}
243+
244+
for _, sa := range o.ServiceAccounts {
245+
tokens := strings.Split(sa, ":")
246+
if len(tokens) != 2 || tokens[0] == "" || tokens[1] == "" {
247+
return nil, fmt.Errorf("serviceaccount must be <namespace>:<name>")
248+
}
249+
roleBinding.Subjects = append(roleBinding.Subjects, rbacv1.Subject{
250+
Kind: rbacv1.ServiceAccountKind,
251+
APIGroup: "",
252+
Namespace: tokens[0],
253+
Name: tokens[1],
254+
})
255+
}
256+
return roleBinding, nil
104257
}

0 commit comments

Comments
 (0)