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