1
16
17 package config
18
19 import (
20 "bytes"
21 utiltesting "k8s.io/client-go/util/testing"
22 "os"
23 "reflect"
24 "testing"
25
26 "k8s.io/client-go/tools/clientcmd"
27 clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
28 cliflag "k8s.io/component-base/cli/flag"
29 )
30
31 func stringFlagFor(s string) cliflag.StringFlag {
32 var f cliflag.StringFlag
33 f.Set(s)
34 return f
35 }
36
37 func TestSetCredentialsOptions(t *testing.T) {
38 tests := []struct {
39 name string
40 flags []string
41 wantParseErr bool
42 wantCompleteErr bool
43 wantValidateErr bool
44
45 wantOptions *setCredentialsOptions
46 }{
47 {
48 name: "test1",
49 flags: []string{
50 "me",
51 },
52 wantOptions: &setCredentialsOptions{
53 name: "me",
54 },
55 },
56 {
57 name: "test2",
58 flags: []string{
59 "me",
60 "--token=foo",
61 },
62 wantOptions: &setCredentialsOptions{
63 name: "me",
64 token: stringFlagFor("foo"),
65 },
66 },
67 {
68 name: "test3",
69 flags: []string{
70 "me",
71 "--username=jane",
72 "--password=bar",
73 },
74 wantOptions: &setCredentialsOptions{
75 name: "me",
76 username: stringFlagFor("jane"),
77 password: stringFlagFor("bar"),
78 },
79 },
80 {
81 name: "test4",
82
83 flags: []string{
84 "me",
85 "--token=foo",
86 "--username=jane",
87 "--password=bar",
88 },
89 wantValidateErr: true,
90 },
91 {
92 name: "test5",
93 flags: []string{
94 "--auth-provider=oidc",
95 "--auth-provider-arg=client-id=foo",
96 "--auth-provider-arg=client-secret=bar",
97 "me",
98 },
99 wantOptions: &setCredentialsOptions{
100 name: "me",
101 authProvider: stringFlagFor("oidc"),
102 authProviderArgs: map[string]string{
103 "client-id": "foo",
104 "client-secret": "bar",
105 },
106 authProviderArgsToRemove: []string{},
107 },
108 },
109 {
110 name: "test6",
111 flags: []string{
112 "--auth-provider=oidc",
113 "--auth-provider-arg=client-id-",
114 "--auth-provider-arg=client-secret-",
115 "me",
116 },
117 wantOptions: &setCredentialsOptions{
118 name: "me",
119 authProvider: stringFlagFor("oidc"),
120 authProviderArgs: map[string]string{},
121 authProviderArgsToRemove: []string{
122 "client-id",
123 "client-secret",
124 },
125 },
126 },
127 {
128 name: "test7",
129 flags: []string{
130 "--auth-provider-arg=client-id-",
131 "--auth-provider-arg=client-secret-",
132 "me",
133 },
134 wantOptions: &setCredentialsOptions{
135 name: "me",
136 authProviderArgs: map[string]string{},
137 authProviderArgsToRemove: []string{
138 "client-id",
139 "client-secret",
140 },
141 },
142 },
143 {
144 name: "test8",
145 flags: []string{
146 "--auth-provider=oidc",
147 "--auth-provider-arg=client-id",
148 "me",
149 },
150 wantCompleteErr: true,
151 },
152 {
153 name: "test9",
154 flags: []string{
155
156 },
157 wantCompleteErr: true,
158 },
159 {
160 name: "test10",
161 flags: []string{
162 "--exec-command=example-client-go-exec-plugin",
163 "me",
164 },
165 wantOptions: &setCredentialsOptions{
166 name: "me",
167 execCommand: stringFlagFor("example-client-go-exec-plugin"),
168 },
169 },
170 {
171 name: "test11",
172 flags: []string{
173 "--exec-command=example-client-go-exec-plugin",
174 "--exec-arg=arg1",
175 "--exec-arg=arg2",
176 "me",
177 },
178 wantOptions: &setCredentialsOptions{
179 name: "me",
180 execCommand: stringFlagFor("example-client-go-exec-plugin"),
181 execArgs: []string{"arg1", "arg2"},
182 },
183 },
184 {
185 name: "test12",
186 flags: []string{
187 "--exec-command=example-client-go-exec-plugin",
188 "--exec-env=key1=val1",
189 "--exec-env=key2=val2",
190 "--exec-env=env-remove1-",
191 "--exec-env=env-remove2-",
192 "me",
193 },
194 wantOptions: &setCredentialsOptions{
195 name: "me",
196 execCommand: stringFlagFor("example-client-go-exec-plugin"),
197 execEnv: map[string]string{"key1": "val1", "key2": "val2"},
198 execEnvToRemove: []string{"env-remove1", "env-remove2"},
199 },
200 },
201 }
202
203 for _, tt := range tests {
204 t.Run(tt.name, func(t *testing.T) {
205 buff := new(bytes.Buffer)
206
207 opts := new(setCredentialsOptions)
208 cmd := newCmdConfigSetCredentials(buff, opts)
209 if err := cmd.ParseFlags(tt.flags); err != nil {
210 if !tt.wantParseErr {
211 t.Errorf("case %s: parsing error for flags %q: %v: %s", tt.name, tt.flags, err, buff)
212 }
213 return
214 }
215 if tt.wantParseErr {
216 t.Errorf("case %s: expected parsing error for flags %q: %s", tt.name, tt.flags, buff)
217 return
218 }
219
220 if err := opts.complete(cmd); err != nil {
221 if !tt.wantCompleteErr {
222 t.Errorf("case %s: complete() error for flags %q: %s", tt.name, tt.flags, buff)
223 }
224 return
225 }
226 if tt.wantCompleteErr {
227 t.Errorf("case %s: complete() expected errors for flags %q: %s", tt.name, tt.flags, buff)
228 return
229 }
230
231 if err := opts.validate(); err != nil {
232 if !tt.wantValidateErr {
233 t.Errorf("case %s: flags %q: validate failed: %v", tt.name, tt.flags, err)
234 }
235 return
236 }
237
238 if tt.wantValidateErr {
239 t.Errorf("case %s: flags %q: expected validate to fail", tt.name, tt.flags)
240 return
241 }
242
243 if !reflect.DeepEqual(opts, tt.wantOptions) {
244 t.Errorf("case %s: flags %q: mis-matched options,\nwanted=%#v\ngot= %#v", tt.name, tt.flags, tt.wantOptions, opts)
245 }
246 })
247 }
248 }
249
250 func TestModifyExistingAuthInfo(t *testing.T) {
251 tests := []struct {
252 name string
253 flags []string
254 wantParseErr bool
255 wantCompleteErr bool
256 wantValidateErr bool
257
258 existingAuthInfo clientcmdapi.AuthInfo
259 wantAuthInfo clientcmdapi.AuthInfo
260 }{
261 {
262 name: "1. create new exec config",
263 flags: []string{
264 "--exec-command=example-client-go-exec-plugin",
265 "--exec-api-version=client.authentication.k8s.io/v1",
266 "me",
267 },
268 existingAuthInfo: clientcmdapi.AuthInfo{},
269 wantAuthInfo: clientcmdapi.AuthInfo{
270 Exec: &clientcmdapi.ExecConfig{
271 Command: "example-client-go-exec-plugin",
272 APIVersion: "client.authentication.k8s.io/v1",
273 },
274 },
275 },
276 {
277 name: "2. redefine exec args",
278 flags: []string{
279 "--exec-arg=new-arg1",
280 "--exec-arg=new-arg2",
281 "me",
282 },
283 existingAuthInfo: clientcmdapi.AuthInfo{
284 Exec: &clientcmdapi.ExecConfig{
285 Command: "example-client-go-exec-plugin",
286 APIVersion: "client.authentication.k8s.io/v1beta1",
287 Args: []string{"existing-arg1", "existing-arg2"},
288 },
289 },
290 wantAuthInfo: clientcmdapi.AuthInfo{
291 Exec: &clientcmdapi.ExecConfig{
292 Command: "example-client-go-exec-plugin",
293 APIVersion: "client.authentication.k8s.io/v1beta1",
294 Args: []string{"new-arg1", "new-arg2"},
295 },
296 },
297 },
298 {
299 name: "3. reset exec args",
300 flags: []string{
301 "--exec-command=example-client-go-exec-plugin",
302 "me",
303 },
304 existingAuthInfo: clientcmdapi.AuthInfo{
305 Exec: &clientcmdapi.ExecConfig{
306 Command: "example-client-go-exec-plugin",
307 APIVersion: "client.authentication.k8s.io/v1beta1",
308 Args: []string{"existing-arg1", "existing-arg2"},
309 },
310 },
311 wantAuthInfo: clientcmdapi.AuthInfo{
312 Exec: &clientcmdapi.ExecConfig{
313 Command: "example-client-go-exec-plugin",
314 APIVersion: "client.authentication.k8s.io/v1beta1",
315 },
316 },
317 },
318 {
319 name: "4. modify exec env variables",
320 flags: []string{
321 "--exec-command=example-client-go-exec-plugin",
322 "--exec-env=name1=value1000",
323 "--exec-env=name3=value3",
324 "--exec-env=name2-",
325 "--exec-env=non-existing-",
326 "me",
327 },
328 existingAuthInfo: clientcmdapi.AuthInfo{
329 Exec: &clientcmdapi.ExecConfig{
330 Command: "existing-command",
331 APIVersion: "client.authentication.k8s.io/v1beta1",
332 Env: []clientcmdapi.ExecEnvVar{
333 {Name: "name1", Value: "value1"},
334 {Name: "name2", Value: "value2"},
335 },
336 },
337 },
338 wantAuthInfo: clientcmdapi.AuthInfo{
339 Exec: &clientcmdapi.ExecConfig{
340 Command: "example-client-go-exec-plugin",
341 APIVersion: "client.authentication.k8s.io/v1beta1",
342 Env: []clientcmdapi.ExecEnvVar{
343 {Name: "name1", Value: "value1000"},
344 {Name: "name3", Value: "value3"},
345 },
346 },
347 },
348 },
349 {
350 name: "5. modify auth provider arguments",
351 flags: []string{
352 "--auth-provider=new-auth-provider",
353 "--auth-provider-arg=key1=val1000",
354 "--auth-provider-arg=key3=val3",
355 "--auth-provider-arg=key2-",
356 "--auth-provider-arg=non-existing-",
357 "me",
358 },
359 existingAuthInfo: clientcmdapi.AuthInfo{
360 AuthProvider: &clientcmdapi.AuthProviderConfig{
361 Name: "auth-provider",
362 Config: map[string]string{
363 "key1": "val1",
364 "key2": "val2",
365 },
366 },
367 },
368 wantAuthInfo: clientcmdapi.AuthInfo{
369 AuthProvider: &clientcmdapi.AuthProviderConfig{
370 Name: "new-auth-provider",
371 Config: map[string]string{
372 "key1": "val1000",
373 "key3": "val3",
374 },
375 },
376 },
377 },
378 }
379
380 for _, tt := range tests {
381 t.Run(tt.name, func(t *testing.T) {
382 buff := new(bytes.Buffer)
383
384 opts := new(setCredentialsOptions)
385 cmd := newCmdConfigSetCredentials(buff, opts)
386 if err := cmd.ParseFlags(tt.flags); err != nil {
387 if !tt.wantParseErr {
388 t.Errorf("case %s: parsing error for flags %q: %v: %s", tt.name, tt.flags, err, buff)
389 }
390 return
391 }
392 if tt.wantParseErr {
393 t.Errorf("case %s: expected parsing error for flags %q: %s", tt.name, tt.flags, buff)
394 return
395 }
396
397 if err := opts.complete(cmd); err != nil {
398 if !tt.wantCompleteErr {
399 t.Errorf("case %s: complete() error for flags %q: %s", tt.name, tt.flags, buff)
400 }
401 return
402 }
403 if tt.wantCompleteErr {
404 t.Errorf("case %s: complete() expected errors for flags %q: %s", tt.name, tt.flags, buff)
405 return
406 }
407
408 if err := opts.validate(); err != nil {
409 if !tt.wantValidateErr {
410 t.Errorf("case %s: flags %q: validate failed: %v", tt.name, tt.flags, err)
411 }
412 return
413 }
414
415 if tt.wantValidateErr {
416 t.Errorf("case %s: flags %q: expected validate to fail", tt.name, tt.flags)
417 return
418 }
419
420 modifiedAuthInfo := opts.modifyAuthInfo(tt.existingAuthInfo)
421
422 if !reflect.DeepEqual(modifiedAuthInfo, tt.wantAuthInfo) {
423 t.Errorf("case %s: flags %q: mis-matched auth info,\nwanted=%#v\ngot= %#v", tt.name, tt.flags, tt.wantAuthInfo, modifiedAuthInfo)
424 }
425 })
426 }
427 }
428
429 type setCredentialsTest struct {
430 description string
431 config clientcmdapi.Config
432 args []string
433 flags []string
434 expected string
435 expectedConfig clientcmdapi.Config
436 }
437
438 func TestSetCredentials(t *testing.T) {
439 conf := clientcmdapi.Config{}
440 test := setCredentialsTest{
441 description: "Testing set credentials",
442 config: conf,
443 args: []string{"cluster-admin"},
444 flags: []string{
445 "--username=admin",
446 "--password=uXFGweU9l35qcif",
447 },
448 expected: `User "cluster-admin" set.` + "\n",
449 expectedConfig: clientcmdapi.Config{
450 AuthInfos: map[string]*clientcmdapi.AuthInfo{
451 "cluster-admin": {Username: "admin", Password: "uXFGweU9l35qcif"}},
452 },
453 }
454 test.run(t)
455 }
456 func (test setCredentialsTest) run(t *testing.T) {
457 fakeKubeFile, err := os.CreateTemp(os.TempDir(), "")
458 if err != nil {
459 t.Fatalf("unexpected error: %v", err)
460 }
461 defer utiltesting.CloseAndRemove(t, fakeKubeFile)
462 err = clientcmd.WriteToFile(test.config, fakeKubeFile.Name())
463 if err != nil {
464 t.Fatalf("unexpected error: %v", err)
465 }
466
467 pathOptions := clientcmd.NewDefaultPathOptions()
468 pathOptions.GlobalFile = fakeKubeFile.Name()
469 pathOptions.EnvVar = ""
470 buf := bytes.NewBuffer([]byte{})
471 cmd := NewCmdConfigSetCredentials(buf, pathOptions)
472 cmd.SetArgs(test.args)
473 cmd.Flags().Parse(test.flags)
474 if err := cmd.Execute(); err != nil {
475 t.Fatalf("unexpected error executing command: %v,kubectl config set-credentials args: %v,flags: %v", err, test.args, test.flags)
476 }
477 config, err := clientcmd.LoadFromFile(fakeKubeFile.Name())
478 if err != nil {
479 t.Fatalf("unexpected error loading kubeconfig file: %v", err)
480 }
481 if len(test.expected) != 0 {
482 if buf.String() != test.expected {
483 t.Errorf("Fail in %q:\n expected %v\n but got %v\n", test.description, test.expected, buf.String())
484 }
485 }
486 if test.expectedConfig.AuthInfos != nil {
487 expectAuthInfo := test.expectedConfig.AuthInfos[test.args[0]]
488 actualAuthInfo := config.AuthInfos[test.args[0]]
489 if expectAuthInfo.Username != actualAuthInfo.Username || expectAuthInfo.Password != actualAuthInfo.Password {
490 t.Errorf("Fail in %q:\n expected AuthInfo%v\n but found %v in kubeconfig\n", test.description, expectAuthInfo, actualAuthInfo)
491 }
492 }
493 }
494
View as plain text