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.Merge(&data.dst, &data.src, mergo.WithOverride) 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.Merge(&data.dst, &data.src, mergo.WithOverride) 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 InteractiveMode: clientcmdapi.IfAvailableExecInteractiveMode, 637 }, 638 }, 639 }, nil) 640 cleanedInstallHint := "some install hint with U+001B[1mcontrol charsU+001B[0m\nand a newline" 641 642 clientConfig, err := clientBuilder.ClientConfig() 643 if err != nil { 644 t.Fatalf("Unexpected error: %v", err) 645 } 646 matchStringArg(cleanedInstallHint, clientConfig.ExecProvider.InstallHint, t) 647} 648 649func TestInClusterClientConfigPrecedence(t *testing.T) { 650 tt := []struct { 651 overrides *ConfigOverrides 652 }{ 653 { 654 overrides: &ConfigOverrides{ 655 ClusterInfo: clientcmdapi.Cluster{ 656 Server: "https://host-from-overrides.com", 657 }, 658 }, 659 }, 660 { 661 overrides: &ConfigOverrides{ 662 AuthInfo: clientcmdapi.AuthInfo{ 663 Token: "https://host-from-overrides.com", 664 }, 665 }, 666 }, 667 { 668 overrides: &ConfigOverrides{ 669 ClusterInfo: clientcmdapi.Cluster{ 670 CertificateAuthority: "/path/to/ca-from-overrides.crt", 671 }, 672 }, 673 }, 674 { 675 overrides: &ConfigOverrides{ 676 ClusterInfo: clientcmdapi.Cluster{ 677 Server: "https://host-from-overrides.com", 678 }, 679 AuthInfo: clientcmdapi.AuthInfo{ 680 Token: "https://host-from-overrides.com", 681 }, 682 }, 683 }, 684 { 685 overrides: &ConfigOverrides{ 686 ClusterInfo: clientcmdapi.Cluster{ 687 Server: "https://host-from-overrides.com", 688 CertificateAuthority: "/path/to/ca-from-overrides.crt", 689 }, 690 }, 691 }, 692 { 693 overrides: &ConfigOverrides{ 694 ClusterInfo: clientcmdapi.Cluster{ 695 CertificateAuthority: "/path/to/ca-from-overrides.crt", 696 }, 697 AuthInfo: clientcmdapi.AuthInfo{ 698 Token: "https://host-from-overrides.com", 699 }, 700 }, 701 }, 702 { 703 overrides: &ConfigOverrides{ 704 ClusterInfo: clientcmdapi.Cluster{ 705 Server: "https://host-from-overrides.com", 706 CertificateAuthority: "/path/to/ca-from-overrides.crt", 707 }, 708 AuthInfo: clientcmdapi.AuthInfo{ 709 Token: "https://host-from-overrides.com", 710 }, 711 }, 712 }, 713 { 714 overrides: &ConfigOverrides{ 715 ClusterInfo: clientcmdapi.Cluster{ 716 Server: "https://host-from-overrides.com", 717 CertificateAuthority: "/path/to/ca-from-overrides.crt", 718 }, 719 AuthInfo: clientcmdapi.AuthInfo{ 720 Token: "token-from-override", 721 TokenFile: "tokenfile-from-override", 722 }, 723 }, 724 }, 725 { 726 overrides: &ConfigOverrides{ 727 ClusterInfo: clientcmdapi.Cluster{ 728 Server: "https://host-from-overrides.com", 729 CertificateAuthority: "/path/to/ca-from-overrides.crt", 730 }, 731 AuthInfo: clientcmdapi.AuthInfo{ 732 Token: "", 733 TokenFile: "tokenfile-from-override", 734 }, 735 }, 736 }, 737 { 738 overrides: &ConfigOverrides{}, 739 }, 740 } 741 742 for _, tc := range tt { 743 expectedServer := "https://host-from-cluster.com" 744 expectedToken := "token-from-cluster" 745 expectedTokenFile := "tokenfile-from-cluster" 746 expectedCAFile := "/path/to/ca-from-cluster.crt" 747 748 icc := &inClusterClientConfig{ 749 inClusterConfigProvider: func() (*restclient.Config, error) { 750 return &restclient.Config{ 751 Host: expectedServer, 752 BearerToken: expectedToken, 753 BearerTokenFile: expectedTokenFile, 754 TLSClientConfig: restclient.TLSClientConfig{ 755 CAFile: expectedCAFile, 756 }, 757 }, nil 758 }, 759 overrides: tc.overrides, 760 } 761 762 clientConfig, err := icc.ClientConfig() 763 if err != nil { 764 t.Fatalf("Unxpected error: %v", err) 765 } 766 767 if overridenServer := tc.overrides.ClusterInfo.Server; len(overridenServer) > 0 { 768 expectedServer = overridenServer 769 } 770 if len(tc.overrides.AuthInfo.Token) > 0 || len(tc.overrides.AuthInfo.TokenFile) > 0 { 771 expectedToken = tc.overrides.AuthInfo.Token 772 expectedTokenFile = tc.overrides.AuthInfo.TokenFile 773 } 774 if overridenCAFile := tc.overrides.ClusterInfo.CertificateAuthority; len(overridenCAFile) > 0 { 775 expectedCAFile = overridenCAFile 776 } 777 778 if clientConfig.Host != expectedServer { 779 t.Errorf("Expected server %v, got %v", expectedServer, clientConfig.Host) 780 } 781 if clientConfig.BearerToken != expectedToken { 782 t.Errorf("Expected token %v, got %v", expectedToken, clientConfig.BearerToken) 783 } 784 if clientConfig.BearerTokenFile != expectedTokenFile { 785 t.Errorf("Expected tokenfile %v, got %v", expectedTokenFile, clientConfig.BearerTokenFile) 786 } 787 if clientConfig.TLSClientConfig.CAFile != expectedCAFile { 788 t.Errorf("Expected Certificate Authority %v, got %v", expectedCAFile, clientConfig.TLSClientConfig.CAFile) 789 } 790 } 791} 792 793func matchBoolArg(expected, got bool, t *testing.T) { 794 if expected != got { 795 t.Errorf("Expected %v, got %v", expected, got) 796 } 797} 798 799func matchStringArg(expected, got string, t *testing.T) { 800 if expected != got { 801 t.Errorf("Expected %q, got %q", expected, got) 802 } 803} 804 805func matchByteArg(expected, got []byte, t *testing.T) { 806 if !reflect.DeepEqual(expected, got) { 807 t.Errorf("Expected %v, got %v", expected, got) 808 } 809} 810 811func TestNamespaceOverride(t *testing.T) { 812 config := &DirectClientConfig{ 813 overrides: &ConfigOverrides{ 814 Context: clientcmdapi.Context{ 815 Namespace: "foo", 816 }, 817 }, 818 } 819 820 ns, overridden, err := config.Namespace() 821 822 if err != nil { 823 t.Errorf("Unexpected error: %v", err) 824 } 825 826 if !overridden { 827 t.Errorf("Expected overridden = true") 828 } 829 830 matchStringArg("foo", ns, t) 831} 832 833func TestAuthConfigMerge(t *testing.T) { 834 content := ` 835apiVersion: v1 836clusters: 837- cluster: 838 server: https://localhost:8080 839 extensions: 840 - name: client.authentication.k8s.io/exec 841 extension: 842 audience: foo 843 other: bar 844 name: foo-cluster 845contexts: 846- context: 847 cluster: foo-cluster 848 user: foo-user 849 namespace: bar 850 name: foo-context 851current-context: foo-context 852kind: Config 853users: 854- name: foo-user 855 user: 856 exec: 857 apiVersion: client.authentication.k8s.io/v1alpha1 858 args: 859 - arg-1 860 - arg-2 861 command: foo-command 862 provideClusterInfo: true 863` 864 tmpfile, err := ioutil.TempFile("", "kubeconfig") 865 if err != nil { 866 t.Error(err) 867 } 868 defer os.Remove(tmpfile.Name()) 869 if err := ioutil.WriteFile(tmpfile.Name(), []byte(content), 0666); err != nil { 870 t.Error(err) 871 } 872 config, err := BuildConfigFromFlags("", tmpfile.Name()) 873 if err != nil { 874 t.Error(err) 875 } 876 if !reflect.DeepEqual(config.ExecProvider.Args, []string{"arg-1", "arg-2"}) { 877 t.Errorf("Got args %v when they should be %v\n", config.ExecProvider.Args, []string{"arg-1", "arg-2"}) 878 } 879 if !config.ExecProvider.ProvideClusterInfo { 880 t.Error("Wanted provider cluster info to be true") 881 } 882 want := &runtime.Unknown{ 883 Raw: []byte(`{"audience":"foo","other":"bar"}`), 884 ContentType: "application/json", 885 } 886 if !reflect.DeepEqual(config.ExecProvider.Config, want) { 887 t.Errorf("Got config %v when it should be %v\n", config.ExecProvider.Config, want) 888 } 889} 890 891func TestCleanANSIEscapeCodes(t *testing.T) { 892 tests := []struct { 893 name string 894 in, out string 895 }{ 896 { 897 name: "DenyBoldCharacters", 898 in: "\x1b[1mbold tuna\x1b[0m, fish, \x1b[1mbold marlin\x1b[0m", 899 out: "U+001B[1mbold tunaU+001B[0m, fish, U+001B[1mbold marlinU+001B[0m", 900 }, 901 { 902 name: "DenyCursorNavigation", 903 in: "\x1b[2Aup up, \x1b[2Cright right", 904 out: "U+001B[2Aup up, U+001B[2Cright right", 905 }, 906 { 907 name: "DenyClearScreen", 908 in: "clear: \x1b[2J", 909 out: "clear: U+001B[2J", 910 }, 911 { 912 name: "AllowSpaceCharactersUnchanged", 913 in: "tuna\nfish\r\nmarlin\t\r\ntuna\vfish\fmarlin", 914 }, 915 { 916 name: "AllowLetters", 917 in: "alpha: \u03b1, beta: \u03b2, gamma: \u03b3", 918 }, 919 { 920 name: "AllowMarks", 921 in: "tu\u0301na with a mark over the u, fi\u0302sh with a mark over the i," + 922 " ma\u030Arlin with a mark over the a", 923 }, 924 { 925 name: "AllowNumbers", 926 in: "t1na, f2sh, m3rlin, t12a, f34h, m56lin, t123, f456, m567n", 927 }, 928 { 929 name: "AllowPunctuation", 930 in: "\"here's a sentence; with! some...punctuation ;)\"", 931 }, 932 { 933 name: "AllowSymbols", 934 in: "the integral of f(x) from 0 to n approximately equals the sum of f(x)" + 935 " from a = 0 to n, where a and n are natural numbers:" + 936 "\u222b\u2081\u207F f(x) dx \u2248 \u2211\u2090\u208C\u2081\u207F f(x)," + 937 " a \u2208 \u2115, n \u2208 \u2115", 938 }, 939 { 940 name: "AllowSepatators", 941 in: "here is a paragraph separator\u2029and here\u2003are\u2003some" + 942 "\u2003em\u2003spaces", 943 }, 944 } 945 for _, test := range tests { 946 t.Run(test.name, func(t *testing.T) { 947 if len(test.out) == 0 { 948 test.out = test.in 949 } 950 951 if actualOut := cleanANSIEscapeCodes(test.in); test.out != actualOut { 952 t.Errorf("expected %q, actual %q", test.out, actualOut) 953 } 954 }) 955 } 956} 957