1
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(),
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"},
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",
551 KubernetesVersion: "v1.6.6",
552 },
553 expectWarnings: false,
554 },
555 {
556 check: KubernetesVersionCheck{
557 KubeadmVersion: "v1.6.6",
558 KubernetesVersion: "v1.5.5",
559 },
560 expectWarnings: false,
561 },
562 {
563 check: KubernetesVersionCheck{
564 KubeadmVersion: "v1.6.6",
565 KubernetesVersion: "v1.6.7",
566 },
567 expectWarnings: false,
568 },
569 {
570 check: KubernetesVersionCheck{
571 KubeadmVersion: "v1.6.6",
572 KubernetesVersion: "v1.7.0-alpha.0",
573 },
574 expectWarnings: true,
575 },
576 {
577 check: KubernetesVersionCheck{
578 KubeadmVersion: "v1.6.6",
579 KubernetesVersion: "v1.7.0",
580 },
581 expectWarnings: true,
582 },
583 {
584 check: KubernetesVersionCheck{
585 KubeadmVersion: "v0.0.0",
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 },
616 expectWarnings: false,
617 },
618 {
619 check: HTTPProxyCIDRCheck{
620 Proto: "https",
621 CIDR: "10.96.0.0/12",
622 },
623 expectWarnings: false,
624 },
625 {
626 check: HTTPProxyCIDRCheck{
627 Proto: "https",
628 CIDR: "192.168.0.0/16",
629 },
630 expectWarnings: true,
631 },
632 {
633 check: HTTPProxyCIDRCheck{
634 Proto: "https",
635 CIDR: "2001:db8::/56",
636 },
637 expectWarnings: false,
638 },
639 {
640 check: HTTPProxyCIDRCheck{
641 Proto: "https",
642 CIDR: "2001:db8:1::/56",
643 },
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 },
676 expectWarnings: false,
677 },
678 {
679 name: "IPv4 direct access",
680 check: HTTPProxyCheck{
681 Proto: "https",
682 Host: "10.96.0.1",
683 },
684 expectWarnings: false,
685 },
686 {
687 name: "IPv4 via proxy",
688 check: HTTPProxyCheck{
689 Proto: "https",
690 Host: "192.168.0.1",
691 },
692 expectWarnings: true,
693 },
694 {
695 name: "IPv6 direct access",
696 check: HTTPProxyCheck{
697 Proto: "https",
698 Host: "[2001:db8::1:15]",
699 },
700 expectWarnings: false,
701 },
702 {
703 name: "IPv6 via proxy",
704 check: HTTPProxyCheck{
705 Proto: "https",
706 Host: "[2001:db8:1::1:15]",
707 },
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 },
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 },
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
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
759
760
761
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},
787 {"v1.1.0", "v1.11.8", true, false},
788 {"v" + minimumKubeletVersion.String(), minimumControlPlaneVersion.WithPatch(5).String(), false, false},
789 {"v" + minimumKubeletVersion.WithPatch(5).String(), minimumControlPlaneVersion.WithPatch(1).String(), false, false},
790 {"v" + minimumKubeletVersion.String(), currentKubernetesVersion.WithPatch(1).String(), false, false},
791 {"v" + currentKubernetesVersion.WithPreRelease("alpha.1").String(), minimumControlPlaneVersion.WithPatch(1).String(), true, false},
792 {"v" + currentKubernetesVersion.String(), minimumControlPlaneVersion.WithPatch(5).String(), true, false},
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
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
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
869 func() ([]byte, []byte, error) { return []byte("pause"), nil, nil },
870 func() ([]byte, []byte, error) { return nil, nil, nil },
871
872
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
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
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
1007 if runtime.GOOS != "linux" {
1008 t.Skip("unsupported OS")
1009 }
1010
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 = ""
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
1074 if runtime.GOOS != "linux" {
1075 t.Skip("unsupported OS")
1076 }
1077
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 }
1140
View as plain text