1
16
17 package clientcmd
18
19 import (
20 "os"
21 "reflect"
22 "strings"
23 "testing"
24
25 utiltesting "k8s.io/client-go/util/testing"
26
27 "github.com/imdario/mergo"
28
29 "k8s.io/apimachinery/pkg/runtime"
30 restclient "k8s.io/client-go/rest"
31 clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
32 )
33
34 func TestMergoSemantics(t *testing.T) {
35 type U struct {
36 A string
37 B int64
38 }
39 type T struct {
40 S []string
41 X string
42 Y int64
43 U U
44 }
45 var testDataStruct = []struct {
46 dst T
47 src T
48 expected T
49 }{
50 {
51 dst: T{X: "one"},
52 src: T{X: "two"},
53 expected: T{X: "two"},
54 },
55 {
56 dst: T{X: "one", Y: 5, U: U{A: "four", B: 6}},
57 src: T{X: "two", U: U{A: "three", B: 4}},
58 expected: T{X: "two", Y: 5, U: U{A: "three", B: 4}},
59 },
60 {
61 dst: T{S: []string{"test3", "test4", "test5"}},
62 src: T{S: []string{"test1", "test2", "test3"}},
63 expected: T{S: []string{"test1", "test2", "test3"}},
64 },
65 }
66 for _, data := range testDataStruct {
67 err := mergo.Merge(&data.dst, &data.src, mergo.WithOverride)
68 if err != nil {
69 t.Errorf("error while merging: %s", err)
70 }
71 if !reflect.DeepEqual(data.dst, data.expected) {
72
73
74
75
76
77
78
79
80 t.Errorf("mergo.MergeWithOverwrite did not provide expected output: %+v doesn't match %+v", data.dst, data.expected)
81 }
82 }
83
84 var testDataMap = []struct {
85 dst map[string]int
86 src map[string]int
87 expected map[string]int
88 }{
89 {
90 dst: map[string]int{"rsc": 6543, "r": 2138, "gri": 1908, "adg": 912, "prt": 22},
91 src: map[string]int{"rsc": 3711, "r": 2138, "gri": 1908, "adg": 912},
92 expected: map[string]int{"rsc": 3711, "r": 2138, "gri": 1908, "adg": 912, "prt": 22},
93 },
94 }
95 for _, data := range testDataMap {
96 err := mergo.Merge(&data.dst, &data.src, mergo.WithOverride)
97 if err != nil {
98 t.Errorf("error while merging: %s", err)
99 }
100 if !reflect.DeepEqual(data.dst, data.expected) {
101
102
103
104
105
106
107
108
109 t.Errorf("mergo.MergeWithOverwrite did not provide expected output: %+v doesn't match %+v", data.dst, data.expected)
110 }
111 }
112 }
113
114 func createValidTestConfig() *clientcmdapi.Config {
115 const (
116 server = "https://anything.com:8080"
117 token = "the-token"
118 )
119
120 config := clientcmdapi.NewConfig()
121 config.Clusters["clean"] = &clientcmdapi.Cluster{
122 Server: server,
123 }
124 config.AuthInfos["clean"] = &clientcmdapi.AuthInfo{
125 Token: token,
126 }
127 config.Contexts["clean"] = &clientcmdapi.Context{
128 Cluster: "clean",
129 AuthInfo: "clean",
130 }
131 config.CurrentContext = "clean"
132
133 return config
134 }
135
136 func createCAValidTestConfig() *clientcmdapi.Config {
137
138 config := createValidTestConfig()
139 config.Clusters["clean"].CertificateAuthorityData = []byte{0, 0}
140 return config
141 }
142
143 func TestDisableCompression(t *testing.T) {
144 config := createValidTestConfig()
145 clientBuilder := NewNonInteractiveClientConfig(*config, "clean", &ConfigOverrides{
146 ClusterInfo: clientcmdapi.Cluster{
147 DisableCompression: true,
148 },
149 }, nil)
150
151 actualCfg, err := clientBuilder.ClientConfig()
152 if err != nil {
153 t.Fatalf("Unexpected error: %v", err)
154 }
155
156 matchBoolArg(true, actualCfg.DisableCompression, t)
157 }
158
159 func TestInsecureOverridesCA(t *testing.T) {
160 config := createCAValidTestConfig()
161 clientBuilder := NewNonInteractiveClientConfig(*config, "clean", &ConfigOverrides{
162 ClusterInfo: clientcmdapi.Cluster{
163 InsecureSkipTLSVerify: true,
164 },
165 }, nil)
166
167 actualCfg, err := clientBuilder.ClientConfig()
168 if err != nil {
169 t.Fatalf("Unexpected error: %v", err)
170 }
171
172 matchBoolArg(true, actualCfg.Insecure, t)
173 matchStringArg("", actualCfg.TLSClientConfig.CAFile, t)
174 matchByteArg(nil, actualCfg.TLSClientConfig.CAData, t)
175 }
176
177 func TestCAOverridesCAData(t *testing.T) {
178 file, err := os.CreateTemp("", "my.ca")
179 if err != nil {
180 t.Fatalf("could not create tempfile: %v", err)
181 }
182 defer utiltesting.CloseAndRemove(t, file)
183
184 config := createCAValidTestConfig()
185 clientBuilder := NewNonInteractiveClientConfig(*config, "clean", &ConfigOverrides{
186 ClusterInfo: clientcmdapi.Cluster{
187 CertificateAuthority: file.Name(),
188 },
189 }, nil)
190
191 actualCfg, err := clientBuilder.ClientConfig()
192 if err != nil {
193 t.Fatalf("Unexpected error: %v", err)
194 }
195
196 matchBoolArg(false, actualCfg.Insecure, t)
197 matchStringArg(file.Name(), actualCfg.TLSClientConfig.CAFile, t)
198 matchByteArg(nil, actualCfg.TLSClientConfig.CAData, t)
199 }
200
201 func TestTLSServerName(t *testing.T) {
202 config := createValidTestConfig()
203
204 clientBuilder := NewNonInteractiveClientConfig(*config, "clean", &ConfigOverrides{
205 ClusterInfo: clientcmdapi.Cluster{
206 TLSServerName: "overridden-server-name",
207 },
208 }, nil)
209
210 actualCfg, err := clientBuilder.ClientConfig()
211 if err != nil {
212 t.Errorf("Unexpected error: %v", err)
213 }
214
215 matchStringArg("overridden-server-name", actualCfg.ServerName, t)
216 matchStringArg("", actualCfg.TLSClientConfig.CAFile, t)
217 matchByteArg(nil, actualCfg.TLSClientConfig.CAData, t)
218 }
219
220 func TestTLSServerNameClearsWhenServerNameSet(t *testing.T) {
221 config := createValidTestConfig()
222
223 clientBuilder := NewNonInteractiveClientConfig(*config, "clean", &ConfigOverrides{
224 ClusterInfo: clientcmdapi.Cluster{
225 Server: "http://something",
226 },
227 }, nil)
228
229 actualCfg, err := clientBuilder.ClientConfig()
230 if err != nil {
231 t.Errorf("Unexpected error: %v", err)
232 }
233
234 matchStringArg("", actualCfg.ServerName, t)
235 }
236
237 func TestFullImpersonateConfig(t *testing.T) {
238 config := createValidTestConfig()
239 config.Clusters["clean"] = &clientcmdapi.Cluster{
240 Server: "https://localhost:8443",
241 }
242 config.AuthInfos["clean"] = &clientcmdapi.AuthInfo{
243 Impersonate: "alice",
244 ImpersonateUID: "abc123",
245 ImpersonateGroups: []string{"group-1"},
246 ImpersonateUserExtra: map[string][]string{"some-key": {"some-value"}},
247 }
248 config.Contexts["clean"] = &clientcmdapi.Context{
249 Cluster: "clean",
250 AuthInfo: "clean",
251 }
252 config.CurrentContext = "clean"
253
254 clientBuilder := NewNonInteractiveClientConfig(*config, "clean", &ConfigOverrides{
255 ClusterInfo: clientcmdapi.Cluster{
256 Server: "http://something",
257 },
258 }, nil)
259
260 actualCfg, err := clientBuilder.ClientConfig()
261 if err != nil {
262 t.Errorf("Unexpected error: %v", err)
263 }
264
265 matchStringArg("alice", actualCfg.Impersonate.UserName, t)
266 matchStringArg("abc123", actualCfg.Impersonate.UID, t)
267 matchIntArg(1, len(actualCfg.Impersonate.Groups), t)
268 matchStringArg("group-1", actualCfg.Impersonate.Groups[0], t)
269 matchIntArg(1, len(actualCfg.Impersonate.Extra), t)
270 matchIntArg(1, len(actualCfg.Impersonate.Extra["some-key"]), t)
271 matchStringArg("some-value", actualCfg.Impersonate.Extra["some-key"][0], t)
272 }
273
274 func TestMergeContext(t *testing.T) {
275 const namespace = "overridden-namespace"
276
277 config := createValidTestConfig()
278 clientBuilder := NewNonInteractiveClientConfig(*config, "clean", &ConfigOverrides{}, nil)
279
280 _, overridden, err := clientBuilder.Namespace()
281 if err != nil {
282 t.Errorf("Unexpected error: %v", err)
283 }
284
285 if overridden {
286 t.Error("Expected namespace to not be overridden")
287 }
288
289 clientBuilder = NewNonInteractiveClientConfig(*config, "clean", &ConfigOverrides{
290 Context: clientcmdapi.Context{
291 Namespace: namespace,
292 },
293 }, nil)
294
295 actual, overridden, err := clientBuilder.Namespace()
296 if err != nil {
297 t.Errorf("Unexpected error: %v", err)
298 }
299
300 if !overridden {
301 t.Error("Expected namespace to be overridden")
302 }
303
304 matchStringArg(namespace, actual, t)
305 }
306
307 func TestModifyContext(t *testing.T) {
308 expectedCtx := map[string]bool{
309 "updated": true,
310 "clean": true,
311 }
312
313 tempPath, err := os.CreateTemp("", "testclientcmd-")
314 if err != nil {
315 t.Fatalf("unexpected error: %v", err)
316 }
317 defer utiltesting.CloseAndRemove(t, tempPath)
318 pathOptions := NewDefaultPathOptions()
319 config := createValidTestConfig()
320
321 pathOptions.GlobalFile = tempPath.Name()
322
323
324 config.Contexts["updated"] = &clientcmdapi.Context{
325 Cluster: "updated",
326 AuthInfo: "updated",
327 }
328 config.CurrentContext = "updated"
329
330 if err := ModifyConfig(pathOptions, *config, true); err != nil {
331 t.Errorf("Unexpected error: %v", err)
332 }
333
334 startingConfig, err := pathOptions.GetStartingConfig()
335 if err != nil {
336 t.Fatalf("Unexpected error: %v", err)
337 }
338
339
340 matchStringArg("updated", startingConfig.CurrentContext, t)
341
342
343 if len(startingConfig.Contexts) != len(expectedCtx) {
344 t.Fatalf("unexpected number of contexts, expecting %v, but found %v", len(expectedCtx), len(startingConfig.Contexts))
345 }
346
347 for key := range startingConfig.Contexts {
348 if !expectedCtx[key] {
349 t.Fatalf("expected context %q to exist", key)
350 }
351 }
352 }
353
354 func TestCertificateData(t *testing.T) {
355 caData := []byte("ca-data")
356 certData := []byte("cert-data")
357 keyData := []byte("key-data")
358
359 config := clientcmdapi.NewConfig()
360 config.Clusters["clean"] = &clientcmdapi.Cluster{
361 Server: "https://localhost:8443",
362 CertificateAuthorityData: caData,
363 }
364 config.AuthInfos["clean"] = &clientcmdapi.AuthInfo{
365 ClientCertificateData: certData,
366 ClientKeyData: keyData,
367 }
368 config.Contexts["clean"] = &clientcmdapi.Context{
369 Cluster: "clean",
370 AuthInfo: "clean",
371 }
372 config.CurrentContext = "clean"
373
374 clientBuilder := NewNonInteractiveClientConfig(*config, "clean", &ConfigOverrides{}, nil)
375
376 clientConfig, err := clientBuilder.ClientConfig()
377 if err != nil {
378 t.Fatalf("Unexpected error: %v", err)
379 }
380
381
382 matchByteArg(caData, clientConfig.TLSClientConfig.CAData, t)
383 matchByteArg(certData, clientConfig.TLSClientConfig.CertData, t)
384 matchByteArg(keyData, clientConfig.TLSClientConfig.KeyData, t)
385 }
386
387 func TestProxyURL(t *testing.T) {
388 tests := []struct {
389 desc string
390 proxyURL string
391 expectErr bool
392 }{
393 {
394 desc: "no proxy-url",
395 },
396 {
397 desc: "socks5 proxy-url",
398 proxyURL: "socks5://example.com",
399 },
400 {
401 desc: "https proxy-url",
402 proxyURL: "https://example.com",
403 },
404 {
405 desc: "http proxy-url",
406 proxyURL: "http://example.com",
407 },
408 {
409 desc: "bad scheme proxy-url",
410 proxyURL: "socks6://example.com",
411 expectErr: true,
412 },
413 {
414 desc: "no scheme proxy-url",
415 proxyURL: "example.com",
416 expectErr: true,
417 },
418 {
419 desc: "not a url proxy-url",
420 proxyURL: "chewbacca@example.com",
421 expectErr: true,
422 },
423 }
424
425 for _, test := range tests {
426 t.Run(test.proxyURL, func(t *testing.T) {
427
428 config := clientcmdapi.NewConfig()
429 config.Clusters["clean"] = &clientcmdapi.Cluster{
430 Server: "https://localhost:8443",
431 ProxyURL: test.proxyURL,
432 }
433 config.AuthInfos["clean"] = &clientcmdapi.AuthInfo{}
434 config.Contexts["clean"] = &clientcmdapi.Context{
435 Cluster: "clean",
436 AuthInfo: "clean",
437 }
438 config.CurrentContext = "clean"
439
440 clientBuilder := NewNonInteractiveClientConfig(*config, "clean", &ConfigOverrides{}, nil)
441
442 clientConfig, err := clientBuilder.ClientConfig()
443 if test.expectErr {
444 if err == nil {
445 t.Fatal("Expected error constructing config")
446 }
447 return
448 }
449 if err != nil {
450 t.Fatalf("Unexpected error constructing config: %v", err)
451 }
452
453 if test.proxyURL == "" {
454 return
455 }
456 gotURL, err := clientConfig.Proxy(nil)
457 if err != nil {
458 t.Fatalf("Unexpected error from proxier: %v", err)
459 }
460 matchStringArg(test.proxyURL, gotURL.String(), t)
461 })
462 }
463 }
464
465 func TestBasicAuthData(t *testing.T) {
466 username := "myuser"
467 password := "mypass"
468
469 config := clientcmdapi.NewConfig()
470 config.Clusters["clean"] = &clientcmdapi.Cluster{
471 Server: "https://localhost:8443",
472 }
473 config.AuthInfos["clean"] = &clientcmdapi.AuthInfo{
474 Username: username,
475 Password: password,
476 }
477 config.Contexts["clean"] = &clientcmdapi.Context{
478 Cluster: "clean",
479 AuthInfo: "clean",
480 }
481 config.CurrentContext = "clean"
482
483 clientBuilder := NewNonInteractiveClientConfig(*config, "clean", &ConfigOverrides{}, nil)
484
485 clientConfig, err := clientBuilder.ClientConfig()
486 if err != nil {
487 t.Fatalf("Unexpected error: %v", err)
488 }
489
490
491 matchStringArg(username, clientConfig.Username, t)
492 matchStringArg(password, clientConfig.Password, t)
493 }
494
495 func TestBasicTokenFile(t *testing.T) {
496 token := "exampletoken"
497 f, err := os.CreateTemp("", "tokenfile")
498 if err != nil {
499 t.Errorf("Unexpected error: %v", err)
500 return
501 }
502 defer utiltesting.CloseAndRemove(t, f)
503 if err := os.WriteFile(f.Name(), []byte(token), 0644); err != nil {
504 t.Errorf("Unexpected error: %v", err)
505 return
506 }
507
508 config := clientcmdapi.NewConfig()
509 config.Clusters["clean"] = &clientcmdapi.Cluster{
510 Server: "https://localhost:8443",
511 }
512 config.AuthInfos["clean"] = &clientcmdapi.AuthInfo{
513 TokenFile: f.Name(),
514 }
515 config.Contexts["clean"] = &clientcmdapi.Context{
516 Cluster: "clean",
517 AuthInfo: "clean",
518 }
519 config.CurrentContext = "clean"
520
521 clientBuilder := NewNonInteractiveClientConfig(*config, "clean", &ConfigOverrides{}, nil)
522
523 clientConfig, err := clientBuilder.ClientConfig()
524 if err != nil {
525 t.Fatalf("Unexpected error: %v", err)
526 }
527
528 matchStringArg(token, clientConfig.BearerToken, t)
529 }
530
531 func TestPrecedenceTokenFile(t *testing.T) {
532 token := "exampletoken"
533 f, err := os.CreateTemp("", "tokenfile")
534 if err != nil {
535 t.Errorf("Unexpected error: %v", err)
536 return
537 }
538 defer utiltesting.CloseAndRemove(t, f)
539 if err := os.WriteFile(f.Name(), []byte(token), 0644); err != nil {
540 t.Errorf("Unexpected error: %v", err)
541 return
542 }
543
544 config := clientcmdapi.NewConfig()
545 config.Clusters["clean"] = &clientcmdapi.Cluster{
546 Server: "https://localhost:8443",
547 }
548 expectedToken := "expected"
549 config.AuthInfos["clean"] = &clientcmdapi.AuthInfo{
550 Token: expectedToken,
551 TokenFile: f.Name(),
552 }
553 config.Contexts["clean"] = &clientcmdapi.Context{
554 Cluster: "clean",
555 AuthInfo: "clean",
556 }
557 config.CurrentContext = "clean"
558
559 clientBuilder := NewNonInteractiveClientConfig(*config, "clean", &ConfigOverrides{}, nil)
560
561 clientConfig, err := clientBuilder.ClientConfig()
562 if err != nil {
563 t.Fatalf("Unexpected error: %v", err)
564 }
565
566 matchStringArg(expectedToken, clientConfig.BearerToken, t)
567 }
568
569 func TestCreateClean(t *testing.T) {
570 config := createValidTestConfig()
571 clientBuilder := NewNonInteractiveClientConfig(*config, "clean", &ConfigOverrides{}, nil)
572
573 clientConfig, err := clientBuilder.ClientConfig()
574 if err != nil {
575 t.Errorf("Unexpected error: %v", err)
576 }
577
578 matchStringArg(config.Clusters["clean"].Server, clientConfig.Host, t)
579 matchStringArg("", clientConfig.APIPath, t)
580 matchBoolArg(config.Clusters["clean"].InsecureSkipTLSVerify, clientConfig.Insecure, t)
581 matchStringArg(config.AuthInfos["clean"].Token, clientConfig.BearerToken, t)
582 matchStringArg(config.Clusters["clean"].TLSServerName, clientConfig.ServerName, t)
583 }
584
585 func TestCreateCleanWithPrefix(t *testing.T) {
586 tt := []struct {
587 server string
588 host string
589 }{
590 {"https://anything.com:8080/foo/bar", "https://anything.com:8080/foo/bar"},
591 {"http://anything.com:8080/foo/bar", "http://anything.com:8080/foo/bar"},
592 {"http://anything.com:8080/foo/bar/", "http://anything.com:8080/foo/bar/"},
593 {"http://anything.com:8080/", "http://anything.com:8080/"},
594 {"http://anything.com:8080//", "http://anything.com:8080//"},
595 {"anything.com:8080/foo/bar", "anything.com:8080/foo/bar"},
596 {"anything.com:8080", "anything.com:8080"},
597 {"anything.com", "anything.com"},
598 {"anything", "anything"},
599 }
600
601 tt = append(tt, struct{ server, host string }{"", "http://localhost:8080"})
602
603 for _, tc := range tt {
604 config := createValidTestConfig()
605
606 cleanConfig := config.Clusters["clean"]
607 cleanConfig.Server = tc.server
608 config.Clusters["clean"] = cleanConfig
609
610 clientBuilder := NewNonInteractiveClientConfig(*config, "clean", &ConfigOverrides{
611 ClusterDefaults: clientcmdapi.Cluster{Server: "http://localhost:8080"},
612 }, nil)
613
614 clientConfig, err := clientBuilder.ClientConfig()
615 if err != nil {
616 t.Fatalf("Unexpected error: %v", err)
617 }
618
619 matchStringArg(tc.host, clientConfig.Host, t)
620 }
621 }
622
623 func TestCreateCleanDefault(t *testing.T) {
624 config := createValidTestConfig()
625 clientBuilder := NewDefaultClientConfig(*config, &ConfigOverrides{})
626
627 clientConfig, err := clientBuilder.ClientConfig()
628 if err != nil {
629 t.Fatalf("Unexpected error: %v", err)
630 }
631
632 matchStringArg(config.Clusters["clean"].Server, clientConfig.Host, t)
633 matchStringArg(config.Clusters["clean"].TLSServerName, clientConfig.ServerName, t)
634 matchBoolArg(config.Clusters["clean"].InsecureSkipTLSVerify, clientConfig.Insecure, t)
635 matchStringArg(config.AuthInfos["clean"].Token, clientConfig.BearerToken, t)
636 }
637
638 func TestCreateCleanDefaultCluster(t *testing.T) {
639 config := createValidTestConfig()
640 clientBuilder := NewDefaultClientConfig(*config, &ConfigOverrides{
641 ClusterDefaults: clientcmdapi.Cluster{Server: "http://localhost:8080"},
642 })
643
644 clientConfig, err := clientBuilder.ClientConfig()
645 if err != nil {
646 t.Fatalf("Unexpected error: %v", err)
647 }
648
649 matchStringArg(config.Clusters["clean"].Server, clientConfig.Host, t)
650 matchStringArg(config.Clusters["clean"].TLSServerName, clientConfig.ServerName, t)
651 matchBoolArg(config.Clusters["clean"].InsecureSkipTLSVerify, clientConfig.Insecure, t)
652 matchStringArg(config.AuthInfos["clean"].Token, clientConfig.BearerToken, t)
653 }
654
655 func TestCreateMissingContextNoDefault(t *testing.T) {
656 config := createValidTestConfig()
657 clientBuilder := NewNonInteractiveClientConfig(*config, "not-present", &ConfigOverrides{}, nil)
658
659 _, err := clientBuilder.ClientConfig()
660 if err == nil {
661 t.Fatalf("Unexpected error: %v", err)
662 }
663 }
664
665 func TestCreateMissingContext(t *testing.T) {
666 const expectedErrorContains = "context was not found for specified context: not-present"
667 config := createValidTestConfig()
668 clientBuilder := NewNonInteractiveClientConfig(*config, "not-present", &ConfigOverrides{
669 ClusterDefaults: clientcmdapi.Cluster{Server: "http://localhost:8080"},
670 }, nil)
671
672 _, err := clientBuilder.ClientConfig()
673 if err == nil {
674 t.Fatalf("Expected error: %v", expectedErrorContains)
675 }
676 if !strings.Contains(err.Error(), expectedErrorContains) {
677 t.Fatalf("Expected error: %v, but got %v", expectedErrorContains, err)
678 }
679 }
680
681 func TestCreateAuthConfigExecInstallHintCleanup(t *testing.T) {
682 config := createValidTestConfig()
683 clientBuilder := NewNonInteractiveClientConfig(*config, "clean", &ConfigOverrides{
684 AuthInfo: clientcmdapi.AuthInfo{
685 Exec: &clientcmdapi.ExecConfig{
686 APIVersion: "client.authentication.k8s.io/v1alpha1",
687 Command: "some-command",
688 InstallHint: "some install hint with \x1b[1mcontrol chars\x1b[0m\nand a newline",
689 InteractiveMode: clientcmdapi.IfAvailableExecInteractiveMode,
690 },
691 },
692 }, nil)
693 cleanedInstallHint := "some install hint with U+001B[1mcontrol charsU+001B[0m\nand a newline"
694
695 clientConfig, err := clientBuilder.ClientConfig()
696 if err != nil {
697 t.Fatalf("Unexpected error: %v", err)
698 }
699 matchStringArg(cleanedInstallHint, clientConfig.ExecProvider.InstallHint, t)
700 }
701
702 func TestInClusterClientConfigPrecedence(t *testing.T) {
703 tt := []struct {
704 overrides *ConfigOverrides
705 }{
706 {
707 overrides: &ConfigOverrides{
708 ClusterInfo: clientcmdapi.Cluster{
709 Server: "https://host-from-overrides.com",
710 },
711 },
712 },
713 {
714 overrides: &ConfigOverrides{
715 AuthInfo: clientcmdapi.AuthInfo{
716 Token: "https://host-from-overrides.com",
717 },
718 },
719 },
720 {
721 overrides: &ConfigOverrides{
722 ClusterInfo: clientcmdapi.Cluster{
723 CertificateAuthority: "/path/to/ca-from-overrides.crt",
724 },
725 },
726 },
727 {
728 overrides: &ConfigOverrides{
729 ClusterInfo: clientcmdapi.Cluster{
730 Server: "https://host-from-overrides.com",
731 },
732 AuthInfo: clientcmdapi.AuthInfo{
733 Token: "https://host-from-overrides.com",
734 },
735 },
736 },
737 {
738 overrides: &ConfigOverrides{
739 ClusterInfo: clientcmdapi.Cluster{
740 Server: "https://host-from-overrides.com",
741 CertificateAuthority: "/path/to/ca-from-overrides.crt",
742 },
743 },
744 },
745 {
746 overrides: &ConfigOverrides{
747 ClusterInfo: clientcmdapi.Cluster{
748 CertificateAuthority: "/path/to/ca-from-overrides.crt",
749 },
750 AuthInfo: clientcmdapi.AuthInfo{
751 Token: "https://host-from-overrides.com",
752 },
753 },
754 },
755 {
756 overrides: &ConfigOverrides{
757 ClusterInfo: clientcmdapi.Cluster{
758 Server: "https://host-from-overrides.com",
759 CertificateAuthority: "/path/to/ca-from-overrides.crt",
760 },
761 AuthInfo: clientcmdapi.AuthInfo{
762 Token: "https://host-from-overrides.com",
763 },
764 },
765 },
766 {
767 overrides: &ConfigOverrides{
768 ClusterInfo: clientcmdapi.Cluster{
769 Server: "https://host-from-overrides.com",
770 CertificateAuthority: "/path/to/ca-from-overrides.crt",
771 },
772 AuthInfo: clientcmdapi.AuthInfo{
773 Token: "token-from-override",
774 TokenFile: "tokenfile-from-override",
775 },
776 },
777 },
778 {
779 overrides: &ConfigOverrides{
780 ClusterInfo: clientcmdapi.Cluster{
781 Server: "https://host-from-overrides.com",
782 CertificateAuthority: "/path/to/ca-from-overrides.crt",
783 },
784 AuthInfo: clientcmdapi.AuthInfo{
785 Token: "",
786 TokenFile: "tokenfile-from-override",
787 },
788 },
789 },
790 {
791 overrides: &ConfigOverrides{},
792 },
793 }
794
795 for _, tc := range tt {
796 expectedServer := "https://host-from-cluster.com"
797 expectedToken := "token-from-cluster"
798 expectedTokenFile := "tokenfile-from-cluster"
799 expectedCAFile := "/path/to/ca-from-cluster.crt"
800
801 icc := &inClusterClientConfig{
802 inClusterConfigProvider: func() (*restclient.Config, error) {
803 return &restclient.Config{
804 Host: expectedServer,
805 BearerToken: expectedToken,
806 BearerTokenFile: expectedTokenFile,
807 TLSClientConfig: restclient.TLSClientConfig{
808 CAFile: expectedCAFile,
809 },
810 }, nil
811 },
812 overrides: tc.overrides,
813 }
814
815 clientConfig, err := icc.ClientConfig()
816 if err != nil {
817 t.Fatalf("Unxpected error: %v", err)
818 }
819
820 if overridenServer := tc.overrides.ClusterInfo.Server; len(overridenServer) > 0 {
821 expectedServer = overridenServer
822 }
823 if len(tc.overrides.AuthInfo.Token) > 0 || len(tc.overrides.AuthInfo.TokenFile) > 0 {
824 expectedToken = tc.overrides.AuthInfo.Token
825 expectedTokenFile = tc.overrides.AuthInfo.TokenFile
826 }
827 if overridenCAFile := tc.overrides.ClusterInfo.CertificateAuthority; len(overridenCAFile) > 0 {
828 expectedCAFile = overridenCAFile
829 }
830
831 if clientConfig.Host != expectedServer {
832 t.Errorf("Expected server %v, got %v", expectedServer, clientConfig.Host)
833 }
834 if clientConfig.BearerToken != expectedToken {
835 t.Errorf("Expected token %v, got %v", expectedToken, clientConfig.BearerToken)
836 }
837 if clientConfig.BearerTokenFile != expectedTokenFile {
838 t.Errorf("Expected tokenfile %v, got %v", expectedTokenFile, clientConfig.BearerTokenFile)
839 }
840 if clientConfig.TLSClientConfig.CAFile != expectedCAFile {
841 t.Errorf("Expected Certificate Authority %v, got %v", expectedCAFile, clientConfig.TLSClientConfig.CAFile)
842 }
843 }
844 }
845
846 func matchBoolArg(expected, got bool, t *testing.T) {
847 if expected != got {
848 t.Errorf("Expected %v, got %v", expected, got)
849 }
850 }
851
852 func matchStringArg(expected, got string, t *testing.T) {
853 if expected != got {
854 t.Errorf("Expected %q, got %q", expected, got)
855 }
856 }
857
858 func matchByteArg(expected, got []byte, t *testing.T) {
859 if !reflect.DeepEqual(expected, got) {
860 t.Errorf("Expected %v, got %v", expected, got)
861 }
862 }
863
864 func matchIntArg(expected, got int, t *testing.T) {
865 if expected != got {
866 t.Errorf("Expected %d, got %d", expected, got)
867 }
868 }
869
870 func TestNamespaceOverride(t *testing.T) {
871 config := &DirectClientConfig{
872 overrides: &ConfigOverrides{
873 Context: clientcmdapi.Context{
874 Namespace: "foo",
875 },
876 },
877 }
878
879 ns, overridden, err := config.Namespace()
880
881 if err != nil {
882 t.Errorf("Unexpected error: %v", err)
883 }
884
885 if !overridden {
886 t.Errorf("Expected overridden = true")
887 }
888
889 matchStringArg("foo", ns, t)
890 }
891
892 func TestAuthConfigMerge(t *testing.T) {
893 content := `
894 apiVersion: v1
895 clusters:
896 - cluster:
897 server: https://localhost:8080
898 extensions:
899 - name: client.authentication.k8s.io/exec
900 extension:
901 audience: foo
902 other: bar
903 name: foo-cluster
904 contexts:
905 - context:
906 cluster: foo-cluster
907 user: foo-user
908 namespace: bar
909 name: foo-context
910 current-context: foo-context
911 kind: Config
912 users:
913 - name: foo-user
914 user:
915 exec:
916 apiVersion: client.authentication.k8s.io/v1alpha1
917 args:
918 - arg-1
919 - arg-2
920 command: foo-command
921 provideClusterInfo: true
922 `
923 tmpfile, err := os.CreateTemp("", "kubeconfig")
924 if err != nil {
925 t.Error(err)
926 }
927 defer utiltesting.CloseAndRemove(t, tmpfile)
928 if err := os.WriteFile(tmpfile.Name(), []byte(content), 0666); err != nil {
929 t.Error(err)
930 }
931 config, err := BuildConfigFromFlags("", tmpfile.Name())
932 if err != nil {
933 t.Error(err)
934 }
935 if !reflect.DeepEqual(config.ExecProvider.Args, []string{"arg-1", "arg-2"}) {
936 t.Errorf("Got args %v when they should be %v\n", config.ExecProvider.Args, []string{"arg-1", "arg-2"})
937 }
938 if !config.ExecProvider.ProvideClusterInfo {
939 t.Error("Wanted provider cluster info to be true")
940 }
941 want := &runtime.Unknown{
942 Raw: []byte(`{"audience":"foo","other":"bar"}`),
943 ContentType: "application/json",
944 }
945 if !reflect.DeepEqual(config.ExecProvider.Config, want) {
946 t.Errorf("Got config %v when it should be %v\n", config.ExecProvider.Config, want)
947 }
948 }
949
950 func TestCleanANSIEscapeCodes(t *testing.T) {
951 tests := []struct {
952 name string
953 in, out string
954 }{
955 {
956 name: "DenyBoldCharacters",
957 in: "\x1b[1mbold tuna\x1b[0m, fish, \x1b[1mbold marlin\x1b[0m",
958 out: "U+001B[1mbold tunaU+001B[0m, fish, U+001B[1mbold marlinU+001B[0m",
959 },
960 {
961 name: "DenyCursorNavigation",
962 in: "\x1b[2Aup up, \x1b[2Cright right",
963 out: "U+001B[2Aup up, U+001B[2Cright right",
964 },
965 {
966 name: "DenyClearScreen",
967 in: "clear: \x1b[2J",
968 out: "clear: U+001B[2J",
969 },
970 {
971 name: "AllowSpaceCharactersUnchanged",
972 in: "tuna\nfish\r\nmarlin\t\r\ntuna\vfish\fmarlin",
973 },
974 {
975 name: "AllowLetters",
976 in: "alpha: \u03b1, beta: \u03b2, gamma: \u03b3",
977 },
978 {
979 name: "AllowMarks",
980 in: "tu\u0301na with a mark over the u, fi\u0302sh with a mark over the i," +
981 " ma\u030Arlin with a mark over the a",
982 },
983 {
984 name: "AllowNumbers",
985 in: "t1na, f2sh, m3rlin, t12a, f34h, m56lin, t123, f456, m567n",
986 },
987 {
988 name: "AllowPunctuation",
989 in: "\"here's a sentence; with! some...punctuation ;)\"",
990 },
991 {
992 name: "AllowSymbols",
993 in: "the integral of f(x) from 0 to n approximately equals the sum of f(x)" +
994 " from a = 0 to n, where a and n are natural numbers:" +
995 "\u222b\u2081\u207F f(x) dx \u2248 \u2211\u2090\u208C\u2081\u207F f(x)," +
996 " a \u2208 \u2115, n \u2208 \u2115",
997 },
998 {
999 name: "AllowSepatators",
1000 in: "here is a paragraph separator\u2029and here\u2003are\u2003some" +
1001 "\u2003em\u2003spaces",
1002 },
1003 }
1004 for _, test := range tests {
1005 t.Run(test.name, func(t *testing.T) {
1006 if len(test.out) == 0 {
1007 test.out = test.in
1008 }
1009
1010 if actualOut := cleanANSIEscapeCodes(test.in); test.out != actualOut {
1011 t.Errorf("expected %q, actual %q", test.out, actualOut)
1012 }
1013 })
1014 }
1015 }
1016
1017 func TestMergeRawConfigDoOverride(t *testing.T) {
1018 const (
1019 server = "https://anything.com:8080"
1020 token = "the-token"
1021 modifiedServer = "http://localhost:8081"
1022 modifiedToken = "modified-token"
1023 )
1024 config := createValidTestConfig()
1025
1026
1027 config.Clusters["modify"] = &clientcmdapi.Cluster{
1028 Server: server,
1029 }
1030 config.AuthInfos["modify"] = &clientcmdapi.AuthInfo{
1031 Token: token,
1032 }
1033 config.Contexts["modify"] = &clientcmdapi.Context{
1034 Cluster: "modify",
1035 AuthInfo: "modify",
1036 Namespace: "modify",
1037 }
1038
1039
1040 overrides := &ConfigOverrides{
1041 ClusterInfo: clientcmdapi.Cluster{
1042 Server: modifiedServer,
1043 },
1044 Context: clientcmdapi.Context{
1045 Namespace: "foobar",
1046 Cluster: "modify",
1047 AuthInfo: "modify",
1048 },
1049 AuthInfo: clientcmdapi.AuthInfo{
1050 Token: modifiedToken,
1051 },
1052 CurrentContext: "modify",
1053 }
1054
1055 cut := NewDefaultClientConfig(*config, overrides)
1056 act, err := cut.MergedRawConfig()
1057 if err != nil {
1058 t.Fatalf("Unexpected error: %v", err)
1059 }
1060
1061
1062 actContext := act.CurrentContext
1063 if actContext != "modify" {
1064 t.Errorf("Expected context %v, got %v", "modify", actContext)
1065 }
1066 if act.Clusters[actContext].Server != "http://localhost:8081" {
1067 t.Errorf("Expected server %v, got %v", "http://localhost:8081", act.Clusters[actContext].Server)
1068 }
1069 if act.Contexts[actContext].Namespace != "foobar" {
1070 t.Errorf("Expected namespace %v, got %v", "foobar", act.Contexts[actContext].Namespace)
1071 }
1072
1073
1074 if act.Clusters["clean"].Server != config.Clusters["clean"].Server {
1075 t.Errorf("Expected server %v, got %v", config.Clusters["clean"].Server, act.Clusters["clean"].Server)
1076 }
1077 if act.Contexts["clean"].Namespace != config.Contexts["clean"].Namespace {
1078 t.Errorf("Expected namespace %v, got %v", config.Contexts["clean"].Namespace, act.Contexts["clean"].Namespace)
1079 }
1080 }
1081
View as plain text