1
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/apimachinery/pkg/runtime"
29 "k8s.io/cli-runtime/pkg/genericclioptions"
30 "k8s.io/cli-runtime/pkg/genericiooptions"
31 rbacclientv1 "k8s.io/client-go/kubernetes/typed/rbac/v1"
32 cmdutil "k8s.io/kubectl/pkg/cmd/util"
33 "k8s.io/kubectl/pkg/scheme"
34 "k8s.io/kubectl/pkg/util"
35 "k8s.io/kubectl/pkg/util/i18n"
36 "k8s.io/kubectl/pkg/util/templates"
37 )
38
39 var (
40 roleBindingLong = templates.LongDesc(i18n.T(`
41 Create a role binding for a particular role or cluster role.`))
42
43 roleBindingExample = templates.Examples(i18n.T(`
44 # Create a role binding for user1, user2, and group1 using the admin cluster role
45 kubectl create rolebinding admin --clusterrole=admin --user=user1 --user=user2 --group=group1
46
47 # Create a role binding for serviceaccount monitoring:sa-dev using the admin role
48 kubectl create rolebinding admin-binding --role=admin --serviceaccount=monitoring:sa-dev`))
49 )
50
51
52 type RoleBindingOptions struct {
53 PrintFlags *genericclioptions.PrintFlags
54 PrintObj func(obj runtime.Object) error
55
56 Name string
57 Namespace string
58 EnforceNamespace bool
59 ClusterRole string
60 Role string
61 Users []string
62 Groups []string
63 ServiceAccounts []string
64 FieldManager string
65 CreateAnnotation bool
66
67 Client rbacclientv1.RbacV1Interface
68 DryRunStrategy cmdutil.DryRunStrategy
69 ValidationDirective string
70
71 genericiooptions.IOStreams
72 }
73
74
75 func NewRoleBindingOptions(ioStreams genericiooptions.IOStreams) *RoleBindingOptions {
76 return &RoleBindingOptions{
77 Users: []string{},
78 Groups: []string{},
79 ServiceAccounts: []string{},
80 PrintFlags: genericclioptions.NewPrintFlags("created").WithTypeSetter(scheme.Scheme),
81 IOStreams: ioStreams,
82 }
83 }
84
85
86 func NewCmdCreateRoleBinding(f cmdutil.Factory, ioStreams genericiooptions.IOStreams) *cobra.Command {
87 o := NewRoleBindingOptions(ioStreams)
88
89 cmd := &cobra.Command{
90 Use: "rolebinding NAME --clusterrole=NAME|--role=NAME [--user=username] [--group=groupname] [--serviceaccount=namespace:serviceaccountname] [--dry-run=server|client|none]",
91 DisableFlagsInUseLine: true,
92 Short: i18n.T("Create a role binding for a particular role or cluster role"),
93 Long: roleBindingLong,
94 Example: roleBindingExample,
95 Run: func(cmd *cobra.Command, args []string) {
96 cmdutil.CheckErr(o.Complete(f, cmd, args))
97 cmdutil.CheckErr(o.Validate())
98 cmdutil.CheckErr(o.Run())
99 },
100 }
101
102 o.PrintFlags.AddFlags(cmd)
103
104 cmdutil.AddApplyAnnotationFlags(cmd)
105 cmdutil.AddValidateFlags(cmd)
106 cmdutil.AddDryRunFlag(cmd)
107 cmd.Flags().StringVar(&o.ClusterRole, "clusterrole", "", i18n.T("ClusterRole this RoleBinding should reference"))
108 cmd.Flags().StringVar(&o.Role, "role", "", i18n.T("Role this RoleBinding should reference"))
109 cmd.Flags().StringArrayVar(&o.Users, "user", o.Users, "Usernames to bind to the role. The flag can be repeated to add multiple users.")
110 cmd.Flags().StringArrayVar(&o.Groups, "group", o.Groups, "Groups to bind to the role. The flag can be repeated to add multiple groups.")
111 cmd.Flags().StringArrayVar(&o.ServiceAccounts, "serviceaccount", o.ServiceAccounts, "Service accounts to bind to the role, in the format <namespace>:<name>. The flag can be repeated to add multiple service accounts.")
112 cmdutil.AddFieldManagerFlagVar(cmd, &o.FieldManager, "kubectl-create")
113 return cmd
114 }
115
116
117 func (o *RoleBindingOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
118 var err error
119 o.Name, err = NameFromCommandArgs(cmd, args)
120 if err != nil {
121 return err
122 }
123 clientConfig, err := f.ToRESTConfig()
124 if err != nil {
125 return err
126 }
127 o.Client, err = rbacclientv1.NewForConfig(clientConfig)
128 if err != nil {
129 return err
130 }
131
132 o.Namespace, o.EnforceNamespace, err = f.ToRawKubeConfigLoader().Namespace()
133 if err != nil {
134 return err
135 }
136
137 o.CreateAnnotation = cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag)
138 o.DryRunStrategy, err = cmdutil.GetDryRunStrategy(cmd)
139 if err != nil {
140 return err
141 }
142 cmdutil.PrintFlagsWithDryRunStrategy(o.PrintFlags, o.DryRunStrategy)
143 printer, err := o.PrintFlags.ToPrinter()
144 if err != nil {
145 return err
146 }
147 o.PrintObj = func(obj runtime.Object) error {
148 return printer.PrintObj(obj, o.Out)
149 }
150
151 o.ValidationDirective, err = cmdutil.GetValidationDirective(cmd)
152 return err
153 }
154
155
156 func (o *RoleBindingOptions) Validate() error {
157 if len(o.Name) == 0 {
158 return fmt.Errorf("name must be specified")
159 }
160 if (len(o.ClusterRole) == 0) == (len(o.Role) == 0) {
161 return fmt.Errorf("exactly one of clusterrole or role must be specified")
162 }
163 return nil
164 }
165
166
167 func (o *RoleBindingOptions) Run() error {
168 roleBinding, err := o.createRoleBinding()
169 if err != nil {
170 return err
171 }
172 if err := util.CreateOrUpdateAnnotation(o.CreateAnnotation, roleBinding, scheme.DefaultJSONEncoder()); err != nil {
173 return err
174 }
175
176 if o.DryRunStrategy != cmdutil.DryRunClient {
177 createOptions := metav1.CreateOptions{}
178 if o.FieldManager != "" {
179 createOptions.FieldManager = o.FieldManager
180 }
181 createOptions.FieldValidation = o.ValidationDirective
182 if o.DryRunStrategy == cmdutil.DryRunServer {
183 createOptions.DryRun = []string{metav1.DryRunAll}
184 }
185 roleBinding, err = o.Client.RoleBindings(o.Namespace).Create(context.TODO(), roleBinding, createOptions)
186 if err != nil {
187 return fmt.Errorf("failed to create rolebinding: %v", err)
188 }
189 }
190 return o.PrintObj(roleBinding)
191 }
192
193 func (o *RoleBindingOptions) createRoleBinding() (*rbacv1.RoleBinding, error) {
194 namespace := ""
195 if o.EnforceNamespace {
196 namespace = o.Namespace
197 }
198
199 roleBinding := &rbacv1.RoleBinding{
200 TypeMeta: metav1.TypeMeta{APIVersion: rbacv1.SchemeGroupVersion.String(), Kind: "RoleBinding"},
201 ObjectMeta: metav1.ObjectMeta{
202 Name: o.Name,
203 Namespace: namespace,
204 },
205 }
206
207 switch {
208 case len(o.Role) > 0:
209 roleBinding.RoleRef = rbacv1.RoleRef{
210 APIGroup: rbacv1.GroupName,
211 Kind: "Role",
212 Name: o.Role,
213 }
214 case len(o.ClusterRole) > 0:
215 roleBinding.RoleRef = rbacv1.RoleRef{
216 APIGroup: rbacv1.GroupName,
217 Kind: "ClusterRole",
218 Name: o.ClusterRole,
219 }
220 }
221
222 for _, user := range o.Users {
223 roleBinding.Subjects = append(roleBinding.Subjects, rbacv1.Subject{
224 Kind: rbacv1.UserKind,
225 APIGroup: rbacv1.GroupName,
226 Name: user,
227 })
228 }
229
230 for _, group := range o.Groups {
231 roleBinding.Subjects = append(roleBinding.Subjects, rbacv1.Subject{
232 Kind: rbacv1.GroupKind,
233 APIGroup: rbacv1.GroupName,
234 Name: group,
235 })
236 }
237
238 for _, sa := range o.ServiceAccounts {
239 tokens := strings.Split(sa, ":")
240 if len(tokens) != 2 || tokens[0] == "" || tokens[1] == "" {
241 return nil, fmt.Errorf("serviceaccount must be <namespace>:<name>")
242 }
243 roleBinding.Subjects = append(roleBinding.Subjects, rbacv1.Subject{
244 Kind: rbacv1.ServiceAccountKind,
245 APIGroup: "",
246 Namespace: tokens[0],
247 Name: tokens[1],
248 })
249 }
250 return roleBinding, nil
251 }
252
View as plain text