1
16
17 package clientcmd
18
19 import (
20 "bytes"
21 "flag"
22 "fmt"
23 "os"
24 "path"
25 "path/filepath"
26 "reflect"
27 "strings"
28 "testing"
29
30 utiltesting "k8s.io/client-go/util/testing"
31
32 "github.com/google/go-cmp/cmp"
33 "sigs.k8s.io/yaml"
34
35 "k8s.io/apimachinery/pkg/runtime"
36 clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
37 clientcmdlatest "k8s.io/client-go/tools/clientcmd/api/latest"
38 "k8s.io/klog/v2"
39 )
40
41 var (
42 testConfigAlfa = clientcmdapi.Config{
43 AuthInfos: map[string]*clientcmdapi.AuthInfo{
44 "red-user": {Token: "red-token"}},
45 Clusters: map[string]*clientcmdapi.Cluster{
46 "cow-cluster": {Server: "http://cow.org:8080"}},
47 Contexts: map[string]*clientcmdapi.Context{
48 "federal-context": {AuthInfo: "red-user", Cluster: "cow-cluster", Namespace: "hammer-ns"}},
49 }
50 testConfigBravo = clientcmdapi.Config{
51 AuthInfos: map[string]*clientcmdapi.AuthInfo{
52 "black-user": {Token: "black-token"}},
53 Clusters: map[string]*clientcmdapi.Cluster{
54 "pig-cluster": {Server: "http://pig.org:8080"}},
55 Contexts: map[string]*clientcmdapi.Context{
56 "queen-anne-context": {AuthInfo: "black-user", Cluster: "pig-cluster", Namespace: "saw-ns"}},
57 }
58 testConfigCharlie = clientcmdapi.Config{
59 AuthInfos: map[string]*clientcmdapi.AuthInfo{
60 "green-user": {Token: "green-token"}},
61 Clusters: map[string]*clientcmdapi.Cluster{
62 "horse-cluster": {Server: "http://horse.org:8080"}},
63 Contexts: map[string]*clientcmdapi.Context{
64 "shaker-context": {AuthInfo: "green-user", Cluster: "horse-cluster", Namespace: "chisel-ns"}},
65 }
66 testConfigDelta = clientcmdapi.Config{
67 AuthInfos: map[string]*clientcmdapi.AuthInfo{
68 "blue-user": {Token: "blue-token"}},
69 Clusters: map[string]*clientcmdapi.Cluster{
70 "chicken-cluster": {Server: "http://chicken.org:8080"}},
71 Contexts: map[string]*clientcmdapi.Context{
72 "gothic-context": {AuthInfo: "blue-user", Cluster: "chicken-cluster", Namespace: "plane-ns"}},
73 }
74
75 testConfigConflictAlfa = clientcmdapi.Config{
76 AuthInfos: map[string]*clientcmdapi.AuthInfo{
77 "red-user": {Token: "a-different-red-token"},
78 "yellow-user": {Token: "yellow-token"}},
79 Clusters: map[string]*clientcmdapi.Cluster{
80 "cow-cluster": {Server: "http://a-different-cow.org:8080", InsecureSkipTLSVerify: true, DisableCompression: true},
81 "donkey-cluster": {Server: "http://donkey.org:8080", InsecureSkipTLSVerify: true, DisableCompression: true}},
82 CurrentContext: "federal-context",
83 }
84 )
85
86 func TestNilOutMap(t *testing.T) {
87 var fakeKubeconfigData = `apiVersion: v1
88 kind: Config
89 clusters:
90 - cluster:
91 certificate-authority-data: UEhPTlkK
92 server: https://1.1.1.1
93 name: production
94 contexts:
95 - context:
96 cluster: production
97 user: production
98 name: production
99 current-context: production
100 users:
101 - name: production
102 user:
103 auth-provider:
104 name: gcp`
105
106 _, _, err := clientcmdlatest.Codec.Decode([]byte(fakeKubeconfigData), nil, nil)
107 if err != nil {
108 t.Fatalf("unexpected error: %v", err)
109 }
110 }
111
112 func TestNonExistentCommandLineFile(t *testing.T) {
113 loadingRules := ClientConfigLoadingRules{
114 ExplicitPath: "bogus_file",
115 }
116
117 _, err := loadingRules.Load()
118 if err == nil {
119 t.Fatalf("Expected error for missing command-line file, got none")
120 }
121 if !strings.Contains(err.Error(), "bogus_file") {
122 t.Fatalf("Expected error about 'bogus_file', got %s", err.Error())
123 }
124 }
125
126 func TestToleratingMissingFiles(t *testing.T) {
127 envVarValue := "bogus"
128 loadingRules := ClientConfigLoadingRules{
129 Precedence: []string{"bogus1", "bogus2", "bogus3"},
130 WarnIfAllMissing: true,
131 Warner: func(err error) { klog.Warning(err) },
132 }
133
134 buffer := &bytes.Buffer{}
135
136 klog.LogToStderr(false)
137 klog.SetOutput(buffer)
138
139 _, err := loadingRules.Load()
140 if err != nil {
141 t.Fatalf("Unexpected error: %v", err)
142 }
143 klog.Flush()
144 expectedLog := fmt.Sprintf("Config not found: %s", envVarValue)
145 if !strings.Contains(buffer.String(), expectedLog) {
146 t.Fatalf("expected log: \"%s\"", expectedLog)
147 }
148 }
149
150 func TestWarningMissingFiles(t *testing.T) {
151 envVarValue := "bogus"
152 t.Setenv(RecommendedConfigPathEnvVar, envVarValue)
153 loadingRules := NewDefaultClientConfigLoadingRules()
154
155 buffer := &bytes.Buffer{}
156
157 flags := &flag.FlagSet{}
158 klog.InitFlags(flags)
159 flags.Set("v", "1")
160 klog.LogToStderr(false)
161 klog.SetOutput(buffer)
162
163 _, err := loadingRules.Load()
164 if err != nil {
165 t.Fatalf("Unexpected error: %v", err)
166 }
167 klog.Flush()
168
169 expectedLog := fmt.Sprintf("Config not found: %s", envVarValue)
170 if !strings.Contains(buffer.String(), expectedLog) {
171 t.Fatalf("expected log: \"%s\"", expectedLog)
172 }
173 }
174
175 func TestNoWarningMissingFiles(t *testing.T) {
176 envVarValue := "bogus"
177 t.Setenv(RecommendedConfigPathEnvVar, envVarValue)
178 loadingRules := NewDefaultClientConfigLoadingRules()
179
180 buffer := &bytes.Buffer{}
181
182 flags := &flag.FlagSet{}
183 klog.InitFlags(flags)
184 flags.Set("v", "0")
185 klog.LogToStderr(false)
186 klog.SetOutput(buffer)
187
188 _, err := loadingRules.Load()
189 if err != nil {
190 t.Fatalf("Unexpected error: %v", err)
191 }
192 klog.Flush()
193
194 logNotExpected := fmt.Sprintf("Config not found: %s", envVarValue)
195 if strings.Contains(buffer.String(), logNotExpected) {
196 t.Fatalf("log not expected: \"%s\"", logNotExpected)
197 }
198 }
199
200 func TestErrorReadingFile(t *testing.T) {
201 commandLineFile, _ := os.CreateTemp("", "")
202 defer utiltesting.CloseAndRemove(t, commandLineFile)
203
204 if err := os.WriteFile(commandLineFile.Name(), []byte("bogus value"), 0644); err != nil {
205 t.Fatalf("Error creating tempfile: %v", err)
206 }
207
208 loadingRules := ClientConfigLoadingRules{
209 ExplicitPath: commandLineFile.Name(),
210 }
211
212 _, err := loadingRules.Load()
213 if err == nil {
214 t.Fatalf("Expected error for unloadable file, got none")
215 }
216 if !strings.Contains(err.Error(), commandLineFile.Name()) {
217 t.Fatalf("Expected error about '%s', got %s", commandLineFile.Name(), err.Error())
218 }
219 }
220
221 func TestErrorReadingNonFile(t *testing.T) {
222 tmpdir, err := os.MkdirTemp("", "")
223 if err != nil {
224 t.Fatalf("Couldn't create tmpdir")
225 }
226 defer os.RemoveAll(tmpdir)
227
228 loadingRules := ClientConfigLoadingRules{
229 ExplicitPath: tmpdir,
230 }
231
232 _, err = loadingRules.Load()
233 if err == nil {
234 t.Fatalf("Expected error for non-file, got none")
235 }
236 if !strings.Contains(err.Error(), tmpdir) {
237 t.Fatalf("Expected error about '%s', got %s", tmpdir, err.Error())
238 }
239 }
240
241 func TestConflictingCurrentContext(t *testing.T) {
242 commandLineFile, _ := os.CreateTemp("", "")
243 envVarFile, _ := os.CreateTemp("", "")
244 defer utiltesting.CloseAndRemove(t, commandLineFile, envVarFile)
245
246 mockCommandLineConfig := clientcmdapi.Config{
247 CurrentContext: "any-context-value",
248 }
249 mockEnvVarConfig := clientcmdapi.Config{
250 CurrentContext: "a-different-context",
251 }
252
253 WriteToFile(mockCommandLineConfig, commandLineFile.Name())
254 WriteToFile(mockEnvVarConfig, envVarFile.Name())
255
256 loadingRules := ClientConfigLoadingRules{
257 ExplicitPath: commandLineFile.Name(),
258 Precedence: []string{envVarFile.Name()},
259 }
260
261 mergedConfig, err := loadingRules.Load()
262 if err != nil {
263 t.Errorf("Unexpected error: %v", err)
264 }
265
266 if mergedConfig.CurrentContext != mockCommandLineConfig.CurrentContext {
267 t.Errorf("expected %v, got %v", mockCommandLineConfig.CurrentContext, mergedConfig.CurrentContext)
268 }
269 }
270
271 func TestEncodeYAML(t *testing.T) {
272 config := clientcmdapi.Config{
273 CurrentContext: "any-context-value",
274 Contexts: map[string]*clientcmdapi.Context{
275 "433e40": {
276 Cluster: "433e40",
277 },
278 },
279 Clusters: map[string]*clientcmdapi.Cluster{
280 "0": {
281 Server: "https://localhost:1234",
282 },
283 "1": {
284 Server: "https://localhost:1234",
285 },
286 "433e40": {
287 Server: "https://localhost:1234",
288 },
289 },
290 }
291 data, err := Write(config)
292 if err != nil {
293 t.Fatal(err)
294 }
295 expected := []byte(`apiVersion: v1
296 clusters:
297 - cluster:
298 server: https://localhost:1234
299 name: "0"
300 - cluster:
301 server: https://localhost:1234
302 name: "1"
303 - cluster:
304 server: https://localhost:1234
305 name: "433e40"
306 contexts:
307 - context:
308 cluster: "433e40"
309 user: ""
310 name: "433e40"
311 current-context: any-context-value
312 kind: Config
313 preferences: {}
314 users: null
315 `)
316 if !bytes.Equal(expected, data) {
317 t.Error(cmp.Diff(string(expected), string(data)))
318 }
319 }
320
321 func TestLoadingEmptyMaps(t *testing.T) {
322 configFile, _ := os.CreateTemp("", "")
323 defer utiltesting.CloseAndRemove(t, configFile)
324
325 mockConfig := clientcmdapi.Config{
326 CurrentContext: "any-context-value",
327 }
328
329 WriteToFile(mockConfig, configFile.Name())
330
331 config, err := LoadFromFile(configFile.Name())
332 if err != nil {
333 t.Errorf("Unexpected error: %v", err)
334 }
335
336 if config.Clusters == nil {
337 t.Error("expected config.Clusters to be non-nil")
338 }
339 if config.AuthInfos == nil {
340 t.Error("expected config.AuthInfos to be non-nil")
341 }
342 if config.Contexts == nil {
343 t.Error("expected config.Contexts to be non-nil")
344 }
345 }
346
347 func TestDuplicateClusterName(t *testing.T) {
348 configFile, _ := os.CreateTemp("", "")
349 defer utiltesting.CloseAndRemove(t, configFile)
350
351 err := os.WriteFile(configFile.Name(), []byte(`
352 kind: Config
353 apiVersion: v1
354 clusters:
355 - cluster:
356 api-version: v1
357 server: https://kubernetes.default.svc:443
358 certificate-authority: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
359 name: kubeconfig-cluster
360 - cluster:
361 api-version: v2
362 server: https://test.example.server:443
363 certificate-authority: /var/run/secrets/test.example.io/serviceaccount/ca.crt
364 name: kubeconfig-cluster
365 contexts:
366 - context:
367 cluster: kubeconfig-cluster
368 namespace: default
369 user: kubeconfig-user
370 name: kubeconfig-context
371 current-context: kubeconfig-context
372 users:
373 - name: kubeconfig-user
374 user:
375 tokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token
376 `), os.FileMode(0755))
377
378 if err != nil {
379 t.Errorf("Unexpected error: %v", err)
380 }
381
382 _, err = LoadFromFile(configFile.Name())
383 if err == nil || !strings.Contains(err.Error(),
384 "error converting *[]NamedCluster into *map[string]*api.Cluster: duplicate name \"kubeconfig-cluster\" in list") {
385 t.Error("Expected error in loading duplicate cluster name, got none")
386 }
387 }
388
389 func TestDuplicateContextName(t *testing.T) {
390 configFile, _ := os.CreateTemp("", "")
391 defer utiltesting.CloseAndRemove(t, configFile)
392
393 err := os.WriteFile(configFile.Name(), []byte(`
394 kind: Config
395 apiVersion: v1
396 clusters:
397 - cluster:
398 api-version: v1
399 server: https://kubernetes.default.svc:443
400 certificate-authority: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
401 name: kubeconfig-cluster
402 contexts:
403 - context:
404 cluster: kubeconfig-cluster
405 namespace: default
406 user: kubeconfig-user
407 name: kubeconfig-context
408 - context:
409 cluster: test-example-cluster
410 namespace: test-example
411 user: test-example-user
412 name: kubeconfig-context
413 current-context: kubeconfig-context
414 users:
415 - name: kubeconfig-user
416 user:
417 tokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token
418 `), os.FileMode(0755))
419
420 if err != nil {
421 t.Errorf("Unexpected error: %v", err)
422 }
423
424 _, err = LoadFromFile(configFile.Name())
425 if err == nil || !strings.Contains(err.Error(),
426 "error converting *[]NamedContext into *map[string]*api.Context: duplicate name \"kubeconfig-context\" in list") {
427 t.Error("Expected error in loading duplicate context name, got none")
428 }
429 }
430
431 func TestDuplicateUserName(t *testing.T) {
432 configFile, _ := os.CreateTemp("", "")
433 defer utiltesting.CloseAndRemove(t, configFile)
434
435 err := os.WriteFile(configFile.Name(), []byte(`
436 kind: Config
437 apiVersion: v1
438 clusters:
439 - cluster:
440 api-version: v1
441 server: https://kubernetes.default.svc:443
442 certificate-authority: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
443 name: kubeconfig-cluster
444 contexts:
445 - context:
446 cluster: kubeconfig-cluster
447 namespace: default
448 user: kubeconfig-user
449 name: kubeconfig-context
450 current-context: kubeconfig-context
451 users:
452 - name: kubeconfig-user
453 user:
454 tokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token
455 - name: kubeconfig-user
456 user:
457 tokenFile: /var/run/secrets/test.example.com/serviceaccount/token
458 `), os.FileMode(0755))
459
460 if err != nil {
461 t.Errorf("Unexpected error: %v", err)
462 }
463
464 _, err = LoadFromFile(configFile.Name())
465 if err == nil || !strings.Contains(err.Error(),
466 "error converting *[]NamedAuthInfo into *map[string]*api.AuthInfo: duplicate name \"kubeconfig-user\" in list") {
467 t.Error("Expected error in loading duplicate user name, got none")
468 }
469 }
470
471 func TestDuplicateExtensionName(t *testing.T) {
472 configFile, _ := os.CreateTemp("", "")
473 defer utiltesting.CloseAndRemove(t, configFile)
474
475 err := os.WriteFile(configFile.Name(), []byte(`
476 kind: Config
477 apiVersion: v1
478 clusters:
479 - cluster:
480 api-version: v1
481 server: https://kubernetes.default.svc:443
482 certificate-authority: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
483 name: kubeconfig-cluster
484 contexts:
485 - context:
486 cluster: kubeconfig-cluster
487 namespace: default
488 user: kubeconfig-user
489 name: kubeconfig-context
490 current-context: kubeconfig-context
491 users:
492 - name: kubeconfig-user
493 user:
494 tokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token
495 extensions:
496 - extension:
497 bytes: test
498 name: test-extension
499 - extension:
500 bytes: some-example
501 name: test-extension
502 `), os.FileMode(0755))
503
504 if err != nil {
505 t.Errorf("Unexpected error: %v", err)
506 }
507
508 _, err = LoadFromFile(configFile.Name())
509 if err == nil || !strings.Contains(err.Error(),
510 "error converting *[]NamedExtension into *map[string]runtime.Object: duplicate name \"test-extension\" in list") {
511 t.Error("Expected error in loading duplicate extension name, got none")
512 }
513 }
514
515 func TestResolveRelativePaths(t *testing.T) {
516 pathResolutionConfig1 := clientcmdapi.Config{
517 AuthInfos: map[string]*clientcmdapi.AuthInfo{
518 "relative-user-1": {ClientCertificate: "relative/client/cert", ClientKey: "../relative/client/key"},
519 "absolute-user-1": {ClientCertificate: "/absolute/client/cert", ClientKey: "/absolute/client/key"},
520 "relative-cmd-1": {Exec: &clientcmdapi.ExecConfig{Command: "../relative/client/cmd"}},
521 "absolute-cmd-1": {Exec: &clientcmdapi.ExecConfig{Command: "/absolute/client/cmd"}},
522 "PATH-cmd-1": {Exec: &clientcmdapi.ExecConfig{Command: "cmd"}},
523 },
524 Clusters: map[string]*clientcmdapi.Cluster{
525 "relative-server-1": {CertificateAuthority: "../relative/ca"},
526 "absolute-server-1": {CertificateAuthority: "/absolute/ca"},
527 },
528 }
529 pathResolutionConfig2 := clientcmdapi.Config{
530 AuthInfos: map[string]*clientcmdapi.AuthInfo{
531 "relative-user-2": {ClientCertificate: "relative/client/cert2", ClientKey: "../relative/client/key2"},
532 "absolute-user-2": {ClientCertificate: "/absolute/client/cert2", ClientKey: "/absolute/client/key2"},
533 },
534 Clusters: map[string]*clientcmdapi.Cluster{
535 "relative-server-2": {CertificateAuthority: "../relative/ca2"},
536 "absolute-server-2": {CertificateAuthority: "/absolute/ca2"},
537 },
538 }
539
540 configDir1, _ := os.MkdirTemp("", "")
541 defer os.RemoveAll(configDir1)
542 configFile1 := path.Join(configDir1, ".kubeconfig")
543 configDir1, _ = filepath.Abs(configDir1)
544
545 configDir2, _ := os.MkdirTemp("", "")
546 defer os.RemoveAll(configDir2)
547 configDir2, _ = os.MkdirTemp(configDir2, "")
548 configFile2 := path.Join(configDir2, ".kubeconfig")
549 configDir2, _ = filepath.Abs(configDir2)
550
551 WriteToFile(pathResolutionConfig1, configFile1)
552 WriteToFile(pathResolutionConfig2, configFile2)
553
554 loadingRules := ClientConfigLoadingRules{
555 Precedence: []string{configFile1, configFile2},
556 }
557
558 mergedConfig, err := loadingRules.Load()
559 if err != nil {
560 t.Errorf("Unexpected error: %v", err)
561 }
562
563 foundClusterCount := 0
564 for key, cluster := range mergedConfig.Clusters {
565 if key == "relative-server-1" {
566 foundClusterCount++
567 matchStringArg(path.Join(configDir1, pathResolutionConfig1.Clusters["relative-server-1"].CertificateAuthority), cluster.CertificateAuthority, t)
568 }
569 if key == "relative-server-2" {
570 foundClusterCount++
571 matchStringArg(path.Join(configDir2, pathResolutionConfig2.Clusters["relative-server-2"].CertificateAuthority), cluster.CertificateAuthority, t)
572 }
573 if key == "absolute-server-1" {
574 foundClusterCount++
575 matchStringArg(pathResolutionConfig1.Clusters["absolute-server-1"].CertificateAuthority, cluster.CertificateAuthority, t)
576 }
577 if key == "absolute-server-2" {
578 foundClusterCount++
579 matchStringArg(pathResolutionConfig2.Clusters["absolute-server-2"].CertificateAuthority, cluster.CertificateAuthority, t)
580 }
581 }
582 if foundClusterCount != 4 {
583 t.Errorf("Expected 4 clusters, found %v: %v", foundClusterCount, mergedConfig.Clusters)
584 }
585
586 foundAuthInfoCount := 0
587 for key, authInfo := range mergedConfig.AuthInfos {
588 if key == "relative-user-1" {
589 foundAuthInfoCount++
590 matchStringArg(path.Join(configDir1, pathResolutionConfig1.AuthInfos["relative-user-1"].ClientCertificate), authInfo.ClientCertificate, t)
591 matchStringArg(path.Join(configDir1, pathResolutionConfig1.AuthInfos["relative-user-1"].ClientKey), authInfo.ClientKey, t)
592 }
593 if key == "relative-user-2" {
594 foundAuthInfoCount++
595 matchStringArg(path.Join(configDir2, pathResolutionConfig2.AuthInfos["relative-user-2"].ClientCertificate), authInfo.ClientCertificate, t)
596 matchStringArg(path.Join(configDir2, pathResolutionConfig2.AuthInfos["relative-user-2"].ClientKey), authInfo.ClientKey, t)
597 }
598 if key == "absolute-user-1" {
599 foundAuthInfoCount++
600 matchStringArg(pathResolutionConfig1.AuthInfos["absolute-user-1"].ClientCertificate, authInfo.ClientCertificate, t)
601 matchStringArg(pathResolutionConfig1.AuthInfos["absolute-user-1"].ClientKey, authInfo.ClientKey, t)
602 }
603 if key == "absolute-user-2" {
604 foundAuthInfoCount++
605 matchStringArg(pathResolutionConfig2.AuthInfos["absolute-user-2"].ClientCertificate, authInfo.ClientCertificate, t)
606 matchStringArg(pathResolutionConfig2.AuthInfos["absolute-user-2"].ClientKey, authInfo.ClientKey, t)
607 }
608 if key == "relative-cmd-1" {
609 foundAuthInfoCount++
610 matchStringArg(path.Join(configDir1, pathResolutionConfig1.AuthInfos[key].Exec.Command), authInfo.Exec.Command, t)
611 }
612 if key == "absolute-cmd-1" {
613 foundAuthInfoCount++
614 matchStringArg(pathResolutionConfig1.AuthInfos[key].Exec.Command, authInfo.Exec.Command, t)
615 }
616 if key == "PATH-cmd-1" {
617 foundAuthInfoCount++
618 matchStringArg(pathResolutionConfig1.AuthInfos[key].Exec.Command, authInfo.Exec.Command, t)
619 }
620 }
621 if foundAuthInfoCount != 7 {
622 t.Errorf("Expected 7 users, found %v: %v", foundAuthInfoCount, mergedConfig.AuthInfos)
623 }
624
625 }
626
627 func TestMigratingFile(t *testing.T) {
628 sourceFile, _ := os.CreateTemp("", "")
629 defer utiltesting.CloseAndRemove(t, sourceFile)
630 destinationFile, _ := os.CreateTemp("", "")
631
632 os.Remove(destinationFile.Name())
633
634 WriteToFile(testConfigAlfa, sourceFile.Name())
635
636 loadingRules := ClientConfigLoadingRules{
637 MigrationRules: map[string]string{destinationFile.Name(): sourceFile.Name()},
638 }
639
640 if _, err := loadingRules.Load(); err != nil {
641 t.Errorf("unexpected error %v", err)
642 }
643
644 defer utiltesting.CloseAndRemove(t, destinationFile)
645
646 sourceContent, err := os.ReadFile(sourceFile.Name())
647 if err != nil {
648 t.Errorf("unexpected error %v", err)
649 }
650 destinationContent, err := os.ReadFile(destinationFile.Name())
651 if err != nil {
652 t.Errorf("unexpected error %v", err)
653 }
654
655 if !reflect.DeepEqual(sourceContent, destinationContent) {
656 t.Errorf("source and destination do not match")
657 }
658 }
659
660 func TestMigratingFileLeaveExistingFileAlone(t *testing.T) {
661 sourceFile, _ := os.CreateTemp("", "")
662 destinationFile, _ := os.CreateTemp("", "")
663 defer utiltesting.CloseAndRemove(t, sourceFile, destinationFile)
664
665 WriteToFile(testConfigAlfa, sourceFile.Name())
666
667 loadingRules := ClientConfigLoadingRules{
668 MigrationRules: map[string]string{destinationFile.Name(): sourceFile.Name()},
669 }
670
671 if _, err := loadingRules.Load(); err != nil {
672 t.Errorf("unexpected error %v", err)
673 }
674
675 destinationContent, err := os.ReadFile(destinationFile.Name())
676 if err != nil {
677 t.Errorf("unexpected error %v", err)
678 }
679
680 if len(destinationContent) > 0 {
681 t.Errorf("destination should not have been touched")
682 }
683 }
684
685 func TestMigratingFileSourceMissingSkip(t *testing.T) {
686 sourceFilename := "some-missing-file"
687 destinationFile, _ := os.CreateTemp("", "")
688
689 utiltesting.CloseAndRemove(t, destinationFile)
690
691 loadingRules := ClientConfigLoadingRules{
692 MigrationRules: map[string]string{destinationFile.Name(): sourceFilename},
693 }
694
695 if _, err := loadingRules.Load(); err != nil {
696 t.Errorf("unexpected error %v", err)
697 }
698
699 if _, err := os.Stat(destinationFile.Name()); !os.IsNotExist(err) {
700 t.Errorf("destination should not exist")
701 }
702 }
703
704 func TestFileLocking(t *testing.T) {
705 f, _ := os.CreateTemp("", "")
706 defer utiltesting.CloseAndRemove(t, f)
707
708 err := lockFile(f.Name())
709 if err != nil {
710 t.Errorf("unexpected error while locking file: %v", err)
711 }
712 defer unlockFile(f.Name())
713
714 err = lockFile(f.Name())
715 if err == nil {
716 t.Error("expected error while locking file.")
717 }
718 }
719
720 func Example_noMergingOnExplicitPaths() {
721 commandLineFile, _ := os.CreateTemp("", "")
722 envVarFile, _ := os.CreateTemp("", "")
723 defer utiltesting.CloseAndRemove(&testing.T{}, commandLineFile, envVarFile)
724
725 WriteToFile(testConfigAlfa, commandLineFile.Name())
726 WriteToFile(testConfigConflictAlfa, envVarFile.Name())
727
728 loadingRules := ClientConfigLoadingRules{
729 ExplicitPath: commandLineFile.Name(),
730 Precedence: []string{envVarFile.Name()},
731 }
732
733 mergedConfig, err := loadingRules.Load()
734 if err != nil {
735 fmt.Printf("Unexpected error: %v", err)
736 }
737 json, err := runtime.Encode(clientcmdlatest.Codec, mergedConfig)
738 if err != nil {
739 fmt.Printf("Unexpected error: %v", err)
740 }
741 output, err := yaml.JSONToYAML(json)
742 if err != nil {
743 fmt.Printf("Unexpected error: %v", err)
744 }
745
746 fmt.Printf("%v", string(output))
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766 }
767
768 func Example_mergingSomeWithConflict() {
769 commandLineFile, _ := os.CreateTemp("", "")
770 envVarFile, _ := os.CreateTemp("", "")
771 defer utiltesting.CloseAndRemove(&testing.T{}, commandLineFile, envVarFile)
772
773 WriteToFile(testConfigAlfa, commandLineFile.Name())
774 WriteToFile(testConfigConflictAlfa, envVarFile.Name())
775
776 loadingRules := ClientConfigLoadingRules{
777 Precedence: []string{commandLineFile.Name(), envVarFile.Name()},
778 }
779
780 mergedConfig, err := loadingRules.Load()
781 if err != nil {
782 fmt.Printf("Unexpected error: %v", err)
783 }
784 json, err := runtime.Encode(clientcmdlatest.Codec, mergedConfig)
785 if err != nil {
786 fmt.Printf("Unexpected error: %v", err)
787 }
788 output, err := yaml.JSONToYAML(json)
789 if err != nil {
790 fmt.Printf("Unexpected error: %v", err)
791 }
792
793 fmt.Printf("%v", string(output))
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821 }
822
823 func Example_mergingEverythingNoConflicts() {
824 commandLineFile, _ := os.CreateTemp("", "")
825 envVarFile, _ := os.CreateTemp("", "")
826 currentDirFile, _ := os.CreateTemp("", "")
827 homeDirFile, _ := os.CreateTemp("", "")
828 defer utiltesting.CloseAndRemove(&testing.T{}, commandLineFile, envVarFile, currentDirFile, homeDirFile)
829
830 WriteToFile(testConfigAlfa, commandLineFile.Name())
831 WriteToFile(testConfigBravo, envVarFile.Name())
832 WriteToFile(testConfigCharlie, currentDirFile.Name())
833 WriteToFile(testConfigDelta, homeDirFile.Name())
834
835 loadingRules := ClientConfigLoadingRules{
836 Precedence: []string{commandLineFile.Name(), envVarFile.Name(), currentDirFile.Name(), homeDirFile.Name()},
837 }
838
839 mergedConfig, err := loadingRules.Load()
840 if err != nil {
841 fmt.Printf("Unexpected error: %v", err)
842 }
843 json, err := runtime.Encode(clientcmdlatest.Codec, mergedConfig)
844 if err != nil {
845 fmt.Printf("Unexpected error: %v", err)
846 }
847 output, err := yaml.JSONToYAML(json)
848 if err != nil {
849 fmt.Printf("Unexpected error: %v", err)
850 }
851
852 fmt.Printf("%v", string(output))
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905 }
906
907 func TestDeduplicate(t *testing.T) {
908 testCases := []struct {
909 src []string
910 expect []string
911 }{
912 {
913 src: []string{"a", "b", "c", "d", "e", "f"},
914 expect: []string{"a", "b", "c", "d", "e", "f"},
915 },
916 {
917 src: []string{"a", "b", "c", "b", "e", "f"},
918 expect: []string{"a", "b", "c", "e", "f"},
919 },
920 {
921 src: []string{"a", "a", "b", "b", "c", "b"},
922 expect: []string{"a", "b", "c"},
923 },
924 }
925
926 for _, testCase := range testCases {
927 get := deduplicate(testCase.src)
928 if !reflect.DeepEqual(get, testCase.expect) {
929 t.Errorf("expect: %v, get: %v", testCase.expect, get)
930 }
931 }
932 }
933
934 func TestLoadingGetLoadingPrecedence(t *testing.T) {
935 testCases := map[string]struct {
936 rules *ClientConfigLoadingRules
937 env string
938 precedence []string
939 }{
940 "default": {
941 precedence: []string{filepath.Join(os.Getenv("HOME"), ".kube/config")},
942 },
943 "explicit": {
944 rules: &ClientConfigLoadingRules{
945 ExplicitPath: "/explicit/kubeconfig",
946 },
947 precedence: []string{"/explicit/kubeconfig"},
948 },
949 "envvar-single": {
950 env: "/env/kubeconfig",
951 precedence: []string{"/env/kubeconfig"},
952 },
953 "envvar-multiple": {
954 env: "/env/kubeconfig:/other/kubeconfig",
955 precedence: []string{"/env/kubeconfig", "/other/kubeconfig"},
956 },
957 }
958
959 for name, test := range testCases {
960 t.Run(name, func(t *testing.T) {
961 t.Setenv("KUBECONFIG", test.env)
962 rules := test.rules
963 if rules == nil {
964 rules = NewDefaultClientConfigLoadingRules()
965 }
966 actual := rules.GetLoadingPrecedence()
967 if !reflect.DeepEqual(actual, test.precedence) {
968 t.Errorf("expect %v, got %v", test.precedence, actual)
969 }
970 })
971 }
972 }
973
View as plain text