k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/cmd/kubeadm/app/preflight/checks_test.go (about) 1 /* 2 Copyright 2016 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package preflight 18 19 import ( 20 "bytes" 21 "fmt" 22 "net" 23 "net/http" 24 "os" 25 "runtime" 26 "strings" 27 "testing" 28 29 utiltesting "k8s.io/client-go/util/testing" 30 31 "github.com/google/go-cmp/cmp" 32 "github.com/lithammer/dedent" 33 "github.com/pkg/errors" 34 35 corev1 "k8s.io/api/core/v1" 36 "k8s.io/apimachinery/pkg/util/sets" 37 "k8s.io/apimachinery/pkg/util/version" 38 "k8s.io/utils/exec" 39 fakeexec "k8s.io/utils/exec/testing" 40 41 kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" 42 kubeadmapiv1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta3" 43 "k8s.io/kubernetes/cmd/kubeadm/app/constants" 44 configutil "k8s.io/kubernetes/cmd/kubeadm/app/util/config" 45 utilruntime "k8s.io/kubernetes/cmd/kubeadm/app/util/runtime" 46 ) 47 48 var ( 49 externalEtcdRootCAFileContent = dedent.Dedent(` 50 -----BEGIN CERTIFICATE----- 51 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 81 Z9JkQayesM6D7uwbQJXd5rgy 82 -----END CERTIFICATE----- 83 `) 84 85 externalEtcdCertFileContent = dedent.Dedent(` 86 -----BEGIN CERTIFICATE----- 87 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 119 XRMVQjawUTknbAgHuE7/rEX8c27WUA== 120 -----END CERTIFICATE----- 121 `) 122 externalEtcdKeyFileContent = dedent.Dedent(` 123 -----BEGIN RSA PRIVATE KEY----- 124 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 173 -----END RSA PRIVATE KEY----- 174 `) 175 ) 176 177 type preflightCheckTest struct { 178 msg string 179 } 180 181 func (pfct preflightCheckTest) Name() string { 182 return "preflightCheckTest" 183 } 184 185 func (pfct preflightCheckTest) Check() (warning, errorList []error) { 186 if pfct.msg == "warning" { 187 return []error{errors.New("warning")}, nil 188 } 189 if pfct.msg != "" { 190 return nil, []error{errors.New("fake error")} 191 } 192 return 193 } 194 195 func TestFileExistingCheck(t *testing.T) { 196 f, err := os.CreateTemp("", "file-exist-check") 197 if err != nil { 198 t.Fatalf("Failed to create file: %v", err) 199 } 200 defer utiltesting.CloseAndRemove(t, f) 201 var tests = []struct { 202 name string 203 check FileExistingCheck 204 expectedError bool 205 }{ 206 { 207 name: "File does not exist, so it's not available", 208 check: FileExistingCheck{ 209 Path: "/does/not/exist", 210 }, 211 expectedError: true, 212 }, 213 { 214 name: "File exists and is available", 215 check: FileExistingCheck{ 216 Path: f.Name(), 217 }, 218 expectedError: false, 219 }, 220 } 221 for _, rt := range tests { 222 _, output := rt.check.Check() 223 if (output != nil) != rt.expectedError { 224 t.Errorf( 225 "Failed FileExistingCheck:%v\n\texpectedError: %t\n\t actual: %t", 226 rt.name, 227 rt.expectedError, 228 (output != nil), 229 ) 230 } 231 } 232 } 233 234 func TestFileAvailableCheck(t *testing.T) { 235 f, err := os.CreateTemp("", "file-avail-check") 236 if err != nil { 237 t.Fatalf("Failed to create file: %v", err) 238 } 239 defer utiltesting.CloseAndRemove(t, f) 240 var tests = []struct { 241 name string 242 check FileAvailableCheck 243 expectedError bool 244 }{ 245 { 246 name: "The file does not exist", 247 check: FileAvailableCheck{ 248 Path: "/does/not/exist", 249 }, 250 expectedError: false, 251 }, 252 { 253 name: "The file exists", 254 check: FileAvailableCheck{ 255 Path: f.Name(), 256 }, 257 expectedError: true, 258 }, 259 } 260 for _, rt := range tests { 261 _, output := rt.check.Check() 262 if (output != nil) != rt.expectedError { 263 t.Errorf( 264 "Failed FileAvailableCheck:%v\n\texpectedError: %t\n\t actual: %t", 265 rt.name, 266 rt.expectedError, 267 (output != nil), 268 ) 269 } 270 } 271 } 272 273 func TestFileContentCheck(t *testing.T) { 274 f, err := os.CreateTemp("", "file-content-check") 275 if err != nil { 276 t.Fatalf("Failed to create file: %v", err) 277 } 278 defer os.Remove(f.Name()) 279 var tests = []struct { 280 name string 281 check FileContentCheck 282 expectedError bool 283 }{ 284 { 285 name: "File exists and has matching content", 286 check: FileContentCheck{ 287 Path: f.Name(), 288 Content: []byte("Test FileContentCheck"), 289 }, 290 expectedError: false, 291 }, 292 { 293 name: "File exists, content is nil", 294 check: FileContentCheck{ 295 Path: f.Name(), 296 Content: nil, 297 }, 298 expectedError: false, 299 }, 300 { 301 name: "File exists but has unexpected content", 302 check: FileContentCheck{ 303 Path: f.Name(), 304 Content: []byte("foo"), 305 }, 306 expectedError: true, 307 }, 308 { 309 name: "File does not exist, content is not nil", 310 check: FileContentCheck{ 311 Path: "/does/not/exist", 312 Content: []byte("foo"), 313 }, 314 expectedError: true, 315 }, 316 { 317 name: "File dose not exist, content is nil", 318 check: FileContentCheck{ 319 Path: "/does/not/exist", 320 Content: nil, 321 }, 322 expectedError: true, 323 }, 324 } 325 if _, err = f.WriteString("Test FileContentCheck"); err != nil { 326 t.Fatalf("Failed to write to file: %v", err) 327 } 328 for _, rt := range tests { 329 _, output := rt.check.Check() 330 if (len(output) > 0) != rt.expectedError { 331 t.Errorf( 332 "Failed FileContentCheck:%v\n\texpectedError: %t\n\t actual: %t", 333 rt.name, 334 rt.expectedError, 335 (len(output) > 0), 336 ) 337 } 338 } 339 } 340 341 func TestDirAvailableCheck(t *testing.T) { 342 fileDir, err := os.MkdirTemp("", "dir-avail-check") 343 if err != nil { 344 t.Fatalf("failed creating directory: %v", err) 345 } 346 defer os.RemoveAll(fileDir) 347 var tests = []struct { 348 name string 349 check DirAvailableCheck 350 expectedError bool 351 }{ 352 { 353 name: "Directory exists and is empty", 354 check: DirAvailableCheck{ 355 Path: fileDir, 356 }, 357 expectedError: false, 358 }, 359 { 360 name: "Directory exists and has something", 361 check: DirAvailableCheck{ 362 Path: os.TempDir(), // a directory was created previously in this test 363 }, 364 expectedError: true, 365 }, 366 { 367 name: "Directory does not exist", 368 check: DirAvailableCheck{ 369 Path: "/does/not/exist", 370 }, 371 expectedError: false, 372 }, 373 } 374 for _, rt := range tests { 375 _, output := rt.check.Check() 376 if (output != nil) != rt.expectedError { 377 t.Errorf( 378 "Failed DirAvailableCheck:%v\n\texpectedError: %t\n\t actual: %t", 379 rt.name, 380 rt.expectedError, 381 (output != nil), 382 ) 383 } 384 } 385 } 386 387 func TestPortOpenCheck(t *testing.T) { 388 ln, err := net.Listen("tcp", ":0") 389 if err != nil { 390 t.Fatalf("could not listen on local network: %v", err) 391 } 392 defer ln.Close() 393 var tests = []struct { 394 name string 395 check PortOpenCheck 396 expectedError bool 397 }{ 398 { 399 name: "Port is available", 400 check: PortOpenCheck{port: 0}, 401 expectedError: false, 402 }, 403 { 404 name: "Port is not available", 405 check: PortOpenCheck{port: ln.Addr().(*net.TCPAddr).Port}, 406 expectedError: true, 407 }, 408 } 409 for _, rt := range tests { 410 _, output := rt.check.Check() 411 if (output != nil) != rt.expectedError { 412 t.Errorf( 413 "Failed PortOpenCheck:%v\n\texpectedError: %t\n\t actual: %t", 414 rt.name, 415 rt.expectedError, 416 (output != nil), 417 ) 418 } 419 } 420 } 421 422 func TestRunChecks(t *testing.T) { 423 var tokenTest = []struct { 424 p []Checker 425 expected bool 426 output string 427 }{ 428 {[]Checker{}, true, ""}, 429 {[]Checker{preflightCheckTest{"warning"}}, true, "\t[WARNING preflightCheckTest]: warning\n"}, // should just print warning 430 {[]Checker{preflightCheckTest{"error"}}, false, ""}, 431 {[]Checker{preflightCheckTest{"test"}}, false, ""}, 432 {[]Checker{DirAvailableCheck{Path: "/does/not/exist"}}, true, ""}, 433 {[]Checker{DirAvailableCheck{Path: "/"}}, false, ""}, 434 {[]Checker{FileAvailableCheck{Path: "/does/not/exist"}}, true, ""}, 435 {[]Checker{FileContentCheck{Path: "/does/not/exist"}}, false, ""}, 436 {[]Checker{FileContentCheck{Path: "/"}}, true, ""}, 437 {[]Checker{FileContentCheck{Path: "/", Content: []byte("does not exist")}}, false, ""}, 438 {[]Checker{InPathCheck{executable: "foobarbaz", exec: exec.New()}}, true, "\t[WARNING FileExisting-foobarbaz]: foobarbaz not found in system path\n"}, 439 {[]Checker{InPathCheck{executable: "foobarbaz", mandatory: true, exec: exec.New()}}, false, ""}, 440 {[]Checker{InPathCheck{executable: "foobar", mandatory: false, exec: exec.New(), suggestion: "install foobar"}}, true, "\t[WARNING FileExisting-foobar]: foobar not found in system path\nSuggestion: install foobar\n"}, 441 } 442 for _, rt := range tokenTest { 443 buf := new(bytes.Buffer) 444 actual := RunChecks(rt.p, buf, sets.New[string]()) 445 if (actual == nil) != rt.expected { 446 t.Errorf( 447 "failed RunChecks:\n\texpected: %t\n\t actual: %t", 448 rt.expected, 449 (actual == nil), 450 ) 451 } 452 if buf.String() != rt.output { 453 t.Errorf( 454 "failed RunChecks:\n\texpected: %s\n\t actual: %s", 455 rt.output, 456 buf.String(), 457 ) 458 } 459 } 460 } 461 func TestConfigRootCAs(t *testing.T) { 462 f, err := os.CreateTemp(os.TempDir(), "kubeadm-external-etcd-test-cafile") 463 if err != nil { 464 t.Errorf("failed configRootCAs:\n\texpected: succeed creating temp CA file\n\tactual:%v", err) 465 } 466 defer utiltesting.CloseAndRemove(t, f) 467 if _, err := f.Write([]byte(externalEtcdRootCAFileContent)); err != nil { 468 t.Errorf("failed configRootCAs:\n\texpected: succeed writing contents to temp CA file %s\n\tactual:%v", f.Name(), err) 469 } 470 471 c := ExternalEtcdVersionCheck{Etcd: kubeadmapi.Etcd{External: &kubeadmapi.ExternalEtcd{CAFile: f.Name()}}} 472 473 config, err := c.configRootCAs(nil) 474 if err != nil { 475 t.Errorf( 476 "failed configRootCAs:\n\texpected: has no error\n\tactual:%v", 477 err, 478 ) 479 } 480 if config.RootCAs == nil { 481 t.Errorf( 482 "failed configRootCAs:\n\texpected: RootCAs not equal to nil\n\tactual:%v", 483 config.RootCAs, 484 ) 485 } 486 } 487 func TestConfigCertAndKey(t *testing.T) { 488 certFile, err := os.CreateTemp(os.TempDir(), "kubeadm-external-etcd-test-certfile") 489 if err != nil { 490 t.Errorf( 491 "failed configCertAndKey:\n\texpected: succeed creating temp CertFile file\n\tactual:%v", 492 err, 493 ) 494 } 495 defer utiltesting.CloseAndRemove(t, certFile) 496 if _, err := certFile.Write([]byte(externalEtcdCertFileContent)); err != nil { 497 t.Errorf( 498 "failed configCertAndKey:\n\texpected: succeed writing contents to temp CertFile file %s\n\tactual:%v", 499 certFile.Name(), 500 err, 501 ) 502 } 503 504 keyFile, err := os.CreateTemp(os.TempDir(), "kubeadm-external-etcd-test-keyfile") 505 if err != nil { 506 t.Errorf( 507 "failed configCertAndKey:\n\texpected: succeed creating temp KeyFile file\n\tactual:%v", 508 err, 509 ) 510 } 511 defer utiltesting.CloseAndRemove(t, keyFile) 512 if _, err := keyFile.Write([]byte(externalEtcdKeyFileContent)); err != nil { 513 t.Errorf( 514 "failed configCertAndKey:\n\texpected: succeed writing contents to temp KeyFile file %s\n\tactual:%v", 515 keyFile.Name(), 516 err, 517 ) 518 } 519 c := ExternalEtcdVersionCheck{ 520 Etcd: kubeadmapi.Etcd{ 521 External: &kubeadmapi.ExternalEtcd{ 522 CertFile: certFile.Name(), 523 KeyFile: keyFile.Name(), 524 }, 525 }, 526 } 527 528 config, err := c.configCertAndKey(nil) 529 if err != nil { 530 t.Errorf( 531 "failed configCertAndKey:\n\texpected: has no error\n\tactual:%v", 532 err, 533 ) 534 } 535 if config.Certificates == nil { 536 t.Errorf( 537 "failed configCertAndKey:\n\texpected: Certificates not equal to nil\n\tactual:%v", 538 config.Certificates, 539 ) 540 } 541 } 542 543 func TestKubernetesVersionCheck(t *testing.T) { 544 var tests = []struct { 545 check KubernetesVersionCheck 546 expectWarnings bool 547 }{ 548 { 549 check: KubernetesVersionCheck{ 550 KubeadmVersion: "v1.6.6", //Same version 551 KubernetesVersion: "v1.6.6", 552 }, 553 expectWarnings: false, 554 }, 555 { 556 check: KubernetesVersionCheck{ 557 KubeadmVersion: "v1.6.6", //KubernetesVersion version older than KubeadmVersion 558 KubernetesVersion: "v1.5.5", 559 }, 560 expectWarnings: false, 561 }, 562 { 563 check: KubernetesVersionCheck{ 564 KubeadmVersion: "v1.6.6", //KubernetesVersion newer than KubeadmVersion, within the same minor release (new patch) 565 KubernetesVersion: "v1.6.7", 566 }, 567 expectWarnings: false, 568 }, 569 { 570 check: KubernetesVersionCheck{ 571 KubeadmVersion: "v1.6.6", //KubernetesVersion newer than KubeadmVersion, in a different minor/in pre-release 572 KubernetesVersion: "v1.7.0-alpha.0", 573 }, 574 expectWarnings: true, 575 }, 576 { 577 check: KubernetesVersionCheck{ 578 KubeadmVersion: "v1.6.6", //KubernetesVersion newer than KubeadmVersion, in a different minor/stable 579 KubernetesVersion: "v1.7.0", 580 }, 581 expectWarnings: true, 582 }, 583 { 584 check: KubernetesVersionCheck{ 585 KubeadmVersion: "v0.0.0", //"super-custom" builds - Skip this check 586 KubernetesVersion: "v1.7.0", 587 }, 588 expectWarnings: false, 589 }, 590 } 591 592 for _, rt := range tests { 593 warning, _ := rt.check.Check() 594 if (warning != nil) != rt.expectWarnings { 595 t.Errorf( 596 "failed KubernetesVersionCheck:\n\texpected: %t\n\t actual: %t (KubeadmVersion:%s, KubernetesVersion: %s)", 597 rt.expectWarnings, 598 (warning != nil), 599 rt.check.KubeadmVersion, 600 rt.check.KubernetesVersion, 601 ) 602 } 603 } 604 } 605 606 func TestHTTPProxyCIDRCheck(t *testing.T) { 607 var tests = []struct { 608 check HTTPProxyCIDRCheck 609 expectWarnings bool 610 }{ 611 { 612 check: HTTPProxyCIDRCheck{ 613 Proto: "https", 614 CIDR: "127.0.0.0/8", 615 }, // Loopback addresses never should produce proxy warnings 616 expectWarnings: false, 617 }, 618 { 619 check: HTTPProxyCIDRCheck{ 620 Proto: "https", 621 CIDR: "10.96.0.0/12", 622 }, // Expected to be accessed directly, we set NO_PROXY to 10.0.0.0/8 623 expectWarnings: false, 624 }, 625 { 626 check: HTTPProxyCIDRCheck{ 627 Proto: "https", 628 CIDR: "192.168.0.0/16", 629 }, // Expected to go via proxy as this range is not listed in NO_PROXY 630 expectWarnings: true, 631 }, 632 { 633 check: HTTPProxyCIDRCheck{ 634 Proto: "https", 635 CIDR: "2001:db8::/56", 636 }, // Expected to be accessed directly, part of 2001:db8::/48 in NO_PROXY 637 expectWarnings: false, 638 }, 639 { 640 check: HTTPProxyCIDRCheck{ 641 Proto: "https", 642 CIDR: "2001:db8:1::/56", 643 }, // Expected to go via proxy, range is not in 2001:db8::/48 644 expectWarnings: true, 645 }, 646 } 647 648 resetProxyEnv(t) 649 650 for _, rt := range tests { 651 warning, _ := rt.check.Check() 652 if (warning != nil) != rt.expectWarnings { 653 t.Errorf( 654 "failed HTTPProxyCIDRCheck:\n\texpected: %t\n\t actual: %t (CIDR:%s). Warnings: %v", 655 rt.expectWarnings, 656 (warning != nil), 657 rt.check.CIDR, 658 warning, 659 ) 660 } 661 } 662 } 663 664 func TestHTTPProxyCheck(t *testing.T) { 665 var tests = []struct { 666 name string 667 check HTTPProxyCheck 668 expectWarnings bool 669 }{ 670 { 671 name: "Loopback address", 672 check: HTTPProxyCheck{ 673 Proto: "https", 674 Host: "127.0.0.1", 675 }, // Loopback addresses never should produce proxy warnings 676 expectWarnings: false, 677 }, 678 { 679 name: "IPv4 direct access", 680 check: HTTPProxyCheck{ 681 Proto: "https", 682 Host: "10.96.0.1", 683 }, // Expected to be accessed directly, we set NO_PROXY to 10.0.0.0/8 684 expectWarnings: false, 685 }, 686 { 687 name: "IPv4 via proxy", 688 check: HTTPProxyCheck{ 689 Proto: "https", 690 Host: "192.168.0.1", 691 }, // Expected to go via proxy as this range is not listed in NO_PROXY 692 expectWarnings: true, 693 }, 694 { 695 name: "IPv6 direct access", 696 check: HTTPProxyCheck{ 697 Proto: "https", 698 Host: "[2001:db8::1:15]", 699 }, // Expected to be accessed directly, part of 2001:db8::/48 in NO_PROXY 700 expectWarnings: false, 701 }, 702 { 703 name: "IPv6 via proxy", 704 check: HTTPProxyCheck{ 705 Proto: "https", 706 Host: "[2001:db8:1::1:15]", 707 }, // Expected to go via proxy, range is not in 2001:db8::/48 708 expectWarnings: true, 709 }, 710 { 711 name: "IPv6 direct access, no brackets", 712 check: HTTPProxyCheck{ 713 Proto: "https", 714 Host: "2001:db8::1:15", 715 }, // Expected to be accessed directly, part of 2001:db8::/48 in NO_PROXY 716 expectWarnings: false, 717 }, 718 { 719 name: "IPv6 via proxy, no brackets", 720 check: HTTPProxyCheck{ 721 Proto: "https", 722 Host: "2001:db8:1::1:15", 723 }, // Expected to go via proxy, range is not in 2001:db8::/48 724 expectWarnings: true, 725 }, 726 } 727 728 resetProxyEnv(t) 729 730 for _, rt := range tests { 731 warning, _ := rt.check.Check() 732 if (warning != nil) != rt.expectWarnings { 733 t.Errorf( 734 "%s failed HTTPProxyCheck:\n\texpected: %t\n\t actual: %t (Host:%s). Warnings: %v", 735 rt.name, 736 rt.expectWarnings, 737 (warning != nil), 738 rt.check.Host, 739 warning, 740 ) 741 } 742 } 743 } 744 745 // resetProxyEnv is helper function that unsets all *_proxy variables. 746 func resetProxyEnv(t *testing.T) { 747 for _, e := range os.Environ() { 748 key, value, _ := strings.Cut(e, "=") 749 if strings.HasSuffix(strings.ToLower(key), "_proxy") { 750 t.Cleanup(func() { os.Setenv(key, value) }) 751 os.Unsetenv(key) 752 } 753 } 754 755 t.Setenv("HTTP_PROXY", "http://proxy.example.com:3128") 756 t.Setenv("HTTPS_PROXY", "https://proxy.example.com:3128") 757 t.Setenv("NO_PROXY", "example.com,10.0.0.0/8,2001:db8::/48") 758 // Check if we can reliably execute tests: 759 // ProxyFromEnvironment caches the *_proxy environment variables and 760 // if ProxyFromEnvironment already executed before our test with empty 761 // HTTP_PROXY it will make these tests return false positive failures 762 req, err := http.NewRequest("GET", "http://host.fake.tld/", nil) 763 if err != nil { 764 t.Fatalf("unexpected err: %v", err) 765 } 766 proxy, err := http.ProxyFromEnvironment(req) 767 if err != nil { 768 t.Fatalf("unexpected err: %v", err) 769 } 770 if proxy == nil { 771 t.Skip("test skipped as ProxyFromEnvironment already initialized in environment without defined HTTP proxy") 772 } 773 t.Log("http.ProxyFromEnvironment is usable, continue executing test") 774 } 775 776 func TestKubeletVersionCheck(t *testing.T) { 777 minimumKubeletVersion := version.MustParseSemantic("v1.3.0") 778 minimumControlPlaneVersion := version.MustParseSemantic("v1.3.0") 779 currentKubernetesVersion := version.MustParseSemantic("v1.4.0") 780 cases := []struct { 781 kubeletVersion string 782 k8sVersion string 783 expectErrors bool 784 expectWarnings bool 785 }{ 786 {"v" + currentKubernetesVersion.WithPatch(2).String(), "", false, false}, // check minimally supported version when there is no information about control plane 787 {"v1.1.0", "v1.11.8", true, false}, // too old kubelet, should fail. 788 {"v" + minimumKubeletVersion.String(), minimumControlPlaneVersion.WithPatch(5).String(), false, false}, // kubelet within same major.minor as control plane 789 {"v" + minimumKubeletVersion.WithPatch(5).String(), minimumControlPlaneVersion.WithPatch(1).String(), false, false}, // kubelet is newer, but still within same major.minor as control plane 790 {"v" + minimumKubeletVersion.String(), currentKubernetesVersion.WithPatch(1).String(), false, false}, // kubelet is lower than control plane, but newer than minimally supported 791 {"v" + currentKubernetesVersion.WithPreRelease("alpha.1").String(), minimumControlPlaneVersion.WithPatch(1).String(), true, false}, // kubelet is newer (development build) than control plane, should fail. 792 {"v" + currentKubernetesVersion.String(), minimumControlPlaneVersion.WithPatch(5).String(), true, false}, // kubelet is newer (release) than control plane, should fail. 793 } 794 795 for _, tc := range cases { 796 t.Run(tc.kubeletVersion, func(t *testing.T) { 797 fcmd := fakeexec.FakeCmd{ 798 OutputScript: []fakeexec.FakeAction{ 799 func() ([]byte, []byte, error) { return []byte("Kubernetes " + tc.kubeletVersion), nil, nil }, 800 }, 801 } 802 fexec := &fakeexec.FakeExec{ 803 CommandScript: []fakeexec.FakeCommandAction{ 804 func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) }, 805 }, 806 } 807 808 check := KubeletVersionCheck{KubernetesVersion: tc.k8sVersion, exec: fexec, minKubeletVersion: minimumKubeletVersion} 809 warnings, errors := check.Check() 810 811 switch { 812 case warnings != nil && !tc.expectWarnings: 813 t.Errorf("KubeletVersionCheck: unexpected warnings for kubelet version %q and Kubernetes version %q. Warnings: %v", tc.kubeletVersion, tc.k8sVersion, warnings) 814 case warnings == nil && tc.expectWarnings: 815 t.Errorf("KubeletVersionCheck: expected warnings for kubelet version %q and Kubernetes version %q but got nothing", tc.kubeletVersion, tc.k8sVersion) 816 case errors != nil && !tc.expectErrors: 817 t.Errorf("KubeletVersionCheck: unexpected errors for kubelet version %q and Kubernetes version %q. errors: %v", tc.kubeletVersion, tc.k8sVersion, errors) 818 case errors == nil && tc.expectErrors: 819 t.Errorf("KubeletVersionCheck: expected errors for kubelet version %q and Kubernetes version %q but got nothing", tc.kubeletVersion, tc.k8sVersion) 820 } 821 }) 822 } 823 } 824 825 func TestSetHasItemOrAll(t *testing.T) { 826 var tests = []struct { 827 ignoreSet sets.Set[string] 828 testString string 829 expectedResult bool 830 }{ 831 {sets.New[string](), "foo", false}, 832 {sets.New("all"), "foo", true}, 833 {sets.New("all", "bar"), "foo", true}, 834 {sets.New("bar"), "foo", false}, 835 {sets.New("baz", "foo", "bar"), "foo", true}, 836 {sets.New("baz", "bar", "foo"), "Foo", true}, 837 } 838 839 for _, rt := range tests { 840 t.Run(rt.testString, func(t *testing.T) { 841 result := setHasItemOrAll(rt.ignoreSet, rt.testString) 842 if result != rt.expectedResult { 843 t.Errorf( 844 "setHasItemOrAll: expected: %v actual: %v (arguments: %q, %q)", 845 rt.expectedResult, result, 846 rt.ignoreSet, 847 rt.testString, 848 ) 849 } 850 }) 851 } 852 } 853 854 func TestImagePullCheck(t *testing.T) { 855 fcmd := fakeexec.FakeCmd{ 856 RunScript: []fakeexec.FakeAction{ 857 // Test case 1: img1 and img2 exist, img3 doesn't exist 858 func() ([]byte, []byte, error) { return nil, nil, nil }, 859 func() ([]byte, []byte, error) { return nil, nil, nil }, 860 func() ([]byte, []byte, error) { return nil, nil, &fakeexec.FakeExitError{Status: 1} }, 861 862 // Test case 2: images don't exist 863 func() ([]byte, []byte, error) { return nil, nil, &fakeexec.FakeExitError{Status: 1} }, 864 func() ([]byte, []byte, error) { return nil, nil, &fakeexec.FakeExitError{Status: 1} }, 865 func() ([]byte, []byte, error) { return nil, nil, &fakeexec.FakeExitError{Status: 1} }, 866 }, 867 CombinedOutputScript: []fakeexec.FakeAction{ 868 // Test case1: pull only img3 869 func() ([]byte, []byte, error) { return []byte("pause"), nil, nil }, 870 func() ([]byte, []byte, error) { return nil, nil, nil }, 871 // Test case 2: fail to pull image2 and image3 872 // If the pull fails, it will be retried 5 times (see PullImageRetry in constants/constants.go) 873 func() ([]byte, []byte, error) { return []byte("pause"), nil, nil }, 874 func() ([]byte, []byte, error) { return nil, nil, nil }, 875 func() ([]byte, []byte, error) { return []byte("error"), nil, &fakeexec.FakeExitError{Status: 1} }, 876 func() ([]byte, []byte, error) { return []byte("error"), nil, &fakeexec.FakeExitError{Status: 1} }, 877 func() ([]byte, []byte, error) { return []byte("error"), nil, &fakeexec.FakeExitError{Status: 1} }, 878 func() ([]byte, []byte, error) { return []byte("error"), nil, &fakeexec.FakeExitError{Status: 1} }, 879 func() ([]byte, []byte, error) { return []byte("error"), nil, &fakeexec.FakeExitError{Status: 1} }, 880 func() ([]byte, []byte, error) { return []byte("error"), nil, &fakeexec.FakeExitError{Status: 1} }, 881 func() ([]byte, []byte, error) { return []byte("error"), nil, &fakeexec.FakeExitError{Status: 1} }, 882 func() ([]byte, []byte, error) { return []byte("error"), nil, &fakeexec.FakeExitError{Status: 1} }, 883 func() ([]byte, []byte, error) { return []byte("error"), nil, &fakeexec.FakeExitError{Status: 1} }, 884 func() ([]byte, []byte, error) { return []byte("error"), nil, &fakeexec.FakeExitError{Status: 1} }, 885 }, 886 } 887 888 fexec := &fakeexec.FakeExec{ 889 CommandScript: []fakeexec.FakeCommandAction{ 890 func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) }, 891 func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) }, 892 func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) }, 893 func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) }, 894 func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) }, 895 func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) }, 896 func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) }, 897 func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) }, 898 func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) }, 899 func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) }, 900 func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) }, 901 func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) }, 902 func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) }, 903 func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) }, 904 func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) }, 905 func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) }, 906 func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) }, 907 func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) }, 908 func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) }, 909 func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) }, 910 }, 911 LookPathFunc: func(cmd string) (string, error) { return "/usr/bin/crictl", nil }, 912 } 913 914 containerRuntime, err := utilruntime.NewContainerRuntime(fexec, constants.DefaultCRISocket) 915 if err != nil { 916 t.Errorf("unexpected NewContainerRuntime error: %v", err) 917 } 918 919 check := ImagePullCheck{ 920 runtime: containerRuntime, 921 sandboxImage: "pause", 922 imageList: []string{"img1", "img2", "img3"}, 923 imagePullPolicy: corev1.PullIfNotPresent, 924 imagePullSerial: true, 925 } 926 warnings, errors := check.Check() 927 if len(warnings) != 0 { 928 t.Fatalf("did not expect any warnings but got %q", warnings) 929 } 930 if len(errors) != 0 { 931 t.Fatalf("expected 1 errors but got %d: %q", len(errors), errors) 932 } 933 934 warnings, errors = check.Check() 935 if len(warnings) != 0 { 936 t.Fatalf("did not expect any warnings but got %q", warnings) 937 } 938 if len(errors) != 2 { 939 t.Fatalf("expected 2 errors but got %d: %q", len(errors), errors) 940 } 941 942 // Test with unknown policy 943 check = ImagePullCheck{ 944 runtime: containerRuntime, 945 sandboxImage: "pause", 946 imageList: []string{"img1", "img2", "img3"}, 947 imagePullPolicy: "", 948 imagePullSerial: true, 949 } 950 _, errors = check.Check() 951 if len(errors) != 1 { 952 t.Fatalf("expected 1 error but got %d: %q", len(errors), errors) 953 } 954 } 955 956 func TestNumCPUCheck(t *testing.T) { 957 var tests = []struct { 958 numCPU int 959 numErrors int 960 numWarnings int 961 }{ 962 {0, 0, 0}, 963 {999999999, 1, 0}, 964 } 965 966 for _, rt := range tests { 967 t.Run(fmt.Sprintf("number of CPUs: %d", rt.numCPU), func(t *testing.T) { 968 warnings, errors := NumCPUCheck{NumCPU: rt.numCPU}.Check() 969 if len(warnings) != rt.numWarnings { 970 t.Errorf("expected %d warning(s) but got %d: %q", rt.numWarnings, len(warnings), warnings) 971 } 972 if len(errors) != rt.numErrors { 973 t.Errorf("expected %d warning(s) but got %d: %q", rt.numErrors, len(errors), errors) 974 } 975 }) 976 } 977 } 978 979 func TestMemCheck(t *testing.T) { 980 // skip this test, if OS in not Linux, since it will ONLY pass on Linux. 981 if runtime.GOOS != "linux" { 982 t.Skip("unsupported OS for memory check test ") 983 } 984 985 var tests = []struct { 986 minimum uint64 987 expectedErrors int 988 }{ 989 {0, 0}, 990 {9999999999999999, 1}, 991 } 992 993 for _, rt := range tests { 994 t.Run(fmt.Sprintf("MemoryCheck{%d}", rt.minimum), func(t *testing.T) { 995 warnings, errors := MemCheck{Mem: rt.minimum}.Check() 996 if len(warnings) > 0 { 997 t.Errorf("expected 0 warnings but got %d: %q", len(warnings), warnings) 998 } else if len(errors) != rt.expectedErrors { 999 t.Errorf("expected %d error(s) but got %d: %q", rt.expectedErrors, len(errors), errors) 1000 } 1001 }) 1002 } 1003 } 1004 1005 func TestInitIPCheck(t *testing.T) { 1006 // skip this test, if OS in not Linux, since it will ONLY pass on Linux. 1007 if runtime.GOOS != "linux" { 1008 t.Skip("unsupported OS") 1009 } 1010 // should be a privileged user for the `init` command, otherwise just skip it. 1011 isPrivileged := IsPrivilegedUserCheck{} 1012 if _, err := isPrivileged.Check(); err != nil { 1013 t.Skip("not a privileged user") 1014 } 1015 internalcfg, err := configutil.DefaultedStaticInitConfiguration() 1016 if err != nil { 1017 t.Fatalf("unexpected failure when defaulting InitConfiguration: %v", err) 1018 } 1019 internalcfg.LocalAPIEndpoint.AdvertiseAddress = "" // AdvertiseAddress is optional, it could be auto-detected. 1020 ipv4File := "FileContent--proc-sys-net-ipv4-ip_forward" 1021 ipv6File := "FileContent--proc-sys-net-ipv6-conf-default-forwarding" 1022 var tests = []struct { 1023 testName string 1024 PodSubnet string 1025 serviceCidr string 1026 expStr []string 1027 }{ 1028 { 1029 testName: "dual stack", 1030 PodSubnet: "fda9:d324:354d:0::/56", 1031 serviceCidr: "10.96.0.0/16,fda9:d324:354d:1::/112", 1032 expStr: []string{"FileContent--proc-sys-net-ipv4-ip_forward", "FileContent--proc-sys-net-ipv6-conf-default-forwarding"}, 1033 }, 1034 { 1035 testName: "single stack ipv4", 1036 PodSubnet: "10.244.0.0/16", 1037 serviceCidr: "10.96.0.0/16", 1038 expStr: []string{"FileContent--proc-sys-net-ipv4-ip_forward"}, 1039 }, 1040 { 1041 testName: "single stack ipv6", 1042 PodSubnet: "fda9:d324:354d:0::/56", 1043 serviceCidr: "fda9:d324:354d:1::/112", 1044 expStr: []string{"FileContent--proc-sys-net-ipv6-conf-default-forwarding"}, 1045 }, 1046 } 1047 1048 for _, rt := range tests { 1049 t.Run(rt.testName, func(t *testing.T) { 1050 checkList := []string{} 1051 internalcfg.Networking.ServiceSubnet = rt.serviceCidr 1052 internalcfg.Networking.PodSubnet = rt.PodSubnet 1053 checks, err := InitNodeChecks(exec.New(), internalcfg, nil, false, false) 1054 if err != nil { 1055 t.Fatalf("unexpected error: %v", err) 1056 } 1057 for _, check := range checks { 1058 if check.Name() == ipv4File { 1059 checkList = append(checkList, ipv4File) 1060 } 1061 if check.Name() == ipv6File { 1062 checkList = append(checkList, ipv6File) 1063 } 1064 } 1065 if diff := cmp.Diff(checkList, rt.expStr); diff != "" { 1066 t.Fatalf("unexpected file content check (-want,+got):\n%s", diff) 1067 } 1068 }) 1069 } 1070 } 1071 1072 func TestJoinIPCheck(t *testing.T) { 1073 // skip this test, if OS in not Linux, since it will ONLY pass on Linux. 1074 if runtime.GOOS != "linux" { 1075 t.Skip("unsupported OS") 1076 } 1077 // should be a privileged user for the `join` command, otherwise just skip it. 1078 isPrivileged := IsPrivilegedUserCheck{} 1079 if _, err := isPrivileged.Check(); err != nil { 1080 t.Skip("not a privileged user") 1081 } 1082 1083 opts := configutil.LoadOrDefaultConfigurationOptions{ 1084 SkipCRIDetect: true, 1085 } 1086 1087 internalcfg, err := configutil.DefaultedJoinConfiguration(&kubeadmapiv1.JoinConfiguration{ 1088 Discovery: kubeadmapiv1.Discovery{ 1089 BootstrapToken: &kubeadmapiv1.BootstrapTokenDiscovery{ 1090 Token: configutil.PlaceholderToken.Token.String(), 1091 APIServerEndpoint: "kube-apiserver:6443", 1092 UnsafeSkipCAVerification: true, 1093 }, 1094 }, 1095 }, opts) 1096 if err != nil { 1097 t.Fatalf("unexpected failure when defaulting JoinConfiguration: %v", err) 1098 } 1099 ipv4File := "FileContent--proc-sys-net-ipv4-ip_forward" 1100 ipv6File := "FileContent--proc-sys-net-ipv6-conf-default-forwarding" 1101 var tests = []struct { 1102 testName string 1103 endpoint string 1104 expStr []string 1105 }{ 1106 { 1107 testName: "single stack ipv4", 1108 endpoint: "10.244.0.0:1234", 1109 expStr: []string{"FileContent--proc-sys-net-ipv4-ip_forward"}, 1110 }, 1111 { 1112 testName: "single stack ipv6", 1113 endpoint: "[fda9:d324:354d:0::]:1234", 1114 expStr: []string{"FileContent--proc-sys-net-ipv6-conf-default-forwarding"}, 1115 }, 1116 } 1117 1118 for _, rt := range tests { 1119 t.Run(rt.testName, func(t *testing.T) { 1120 checkList := []string{} 1121 internalcfg.Discovery.BootstrapToken.APIServerEndpoint = rt.endpoint 1122 checks, err := JoinNodeChecks(exec.New(), internalcfg, nil) 1123 if err != nil { 1124 t.Fatalf("unexpected error: %v", err) 1125 } 1126 for _, check := range checks { 1127 if check.Name() == ipv4File { 1128 checkList = append(checkList, ipv4File) 1129 } 1130 if check.Name() == ipv6File { 1131 checkList = append(checkList, ipv6File) 1132 } 1133 } 1134 if diff := cmp.Diff(checkList, rt.expStr); diff != "" { 1135 t.Fatalf("unexpected file content check (-want,+got):\n%s", diff) 1136 } 1137 }) 1138 } 1139 }