diff --git a/cmd/kubeadm/test/cmd/completion_test.go b/cmd/kubeadm/test/cmd/completion_test.go index 95fe79d8924..d5dc7bd5179 100644 --- a/cmd/kubeadm/test/cmd/completion_test.go +++ b/cmd/kubeadm/test/cmd/completion_test.go @@ -37,7 +37,7 @@ func TestCmdCompletion(t *testing.T) { for _, rt := range tests { t.Run(rt.name, func(t *testing.T) { - _, _, actual := RunCmd(kubeadmPath, "completion", rt.args) + _, _, _, actual := RunCmd(kubeadmPath, "completion", rt.args) if (actual == nil) != rt.expected { t.Errorf( "failed CmdCompletion running 'kubeadm completion %s' with an error: %v\n\texpected: %t\n\t actual: %t", diff --git a/cmd/kubeadm/test/cmd/init_test.go b/cmd/kubeadm/test/cmd/init_test.go index 1cae76ac246..4aab86c195d 100644 --- a/cmd/kubeadm/test/cmd/init_test.go +++ b/cmd/kubeadm/test/cmd/init_test.go @@ -29,7 +29,7 @@ import ( testutil "k8s.io/kubernetes/cmd/kubeadm/test" ) -func runKubeadmInit(args ...string) (string, string, error) { +func runKubeadmInit(args ...string) (string, string, int, error) { kubeadmPath := getKubeadmPath() kubeadmArgs := []string{"init", "--dry-run", "--ignore-preflight-errors=all"} kubeadmArgs = append(kubeadmArgs, args...) @@ -66,7 +66,7 @@ func TestCmdInitToken(t *testing.T) { for _, rt := range initTest { t.Run(rt.name, func(t *testing.T) { - _, _, err := runKubeadmInit(rt.args) + _, _, _, err := runKubeadmInit(rt.args) if (err == nil) != rt.expected { t.Fatalf(dedent.Dedent(` CmdInitToken test case %q failed with an error: %v @@ -110,7 +110,7 @@ func TestCmdInitKubernetesVersion(t *testing.T) { for _, rt := range initTest { t.Run(rt.name, func(t *testing.T) { - _, _, err := runKubeadmInit(rt.args) + _, _, _, err := runKubeadmInit(rt.args) if (err == nil) != rt.expected { t.Fatalf(dedent.Dedent(` CmdInitKubernetesVersion test case %q failed with an error: %v @@ -184,7 +184,7 @@ func TestCmdInitConfig(t *testing.T) { for _, rt := range initTest { t.Run(rt.name, func(t *testing.T) { - _, _, err := runKubeadmInit(rt.args) + _, _, _, err := runKubeadmInit(rt.args) if (err == nil) != rt.expected { t.Fatalf(dedent.Dedent(` CmdInitConfig test case %q failed with an error: %v @@ -235,7 +235,7 @@ func TestCmdInitCertPhaseCSR(t *testing.T) { csrDir := testutil.SetupTempDir(t) cert := &certs.KubeadmCertKubeletClient kubeadmPath := getKubeadmPath() - _, stderr, err := RunCmd(kubeadmPath, + _, stderr, _, err := RunCmd(kubeadmPath, "init", "phase", "certs", @@ -303,7 +303,7 @@ func TestCmdInitAPIPort(t *testing.T) { for _, rt := range initTest { t.Run(rt.name, func(t *testing.T) { - _, _, err := runKubeadmInit(rt.args) + _, _, _, err := runKubeadmInit(rt.args) if (err == nil) != rt.expected { t.Fatalf(dedent.Dedent(` CmdInitAPIPort test case %q failed with an error: %v @@ -321,3 +321,52 @@ func TestCmdInitAPIPort(t *testing.T) { }) } } + +// TestCmdInitFeatureGates test that feature gates won't make kubeadm panic. +// When go panics it will exit with a 2 code. While we don't expect the init +// calls to succeed in these tests, we ensure that the exit code of calling +// kubeadm with different feature gates is not 2. +func TestCmdInitFeatureGates(t *testing.T) { + const PanicExitcode = 2 + + if *kubeadmCmdSkip { + t.Log("kubeadm cmd tests being skipped") + t.Skip() + } + + initTest := []struct { + name string + args string + }{ + { + name: "no feature gates passed", + args: "", + }, + { + name: "feature gate CoreDNS=true", + args: "--feature-gates=CoreDNS=true", + }, + { + name: "feature gate IPv6DualStack=true", + args: "--feature-gates=IPv6DualStack=true", + }, + } + + for _, rt := range initTest { + t.Run(rt.name, func(t *testing.T) { + _, _, exitcode, err := runKubeadmInit(rt.args) + if exitcode == PanicExitcode { + t.Fatalf(dedent.Dedent(` + CmdInitFeatureGates test case %q failed with an error: %v + command 'kubeadm init %s' + got exit code: %t (panic); unexpected + `), + rt.name, + err, + rt.args, + PanicExitcode, + ) + } + }) + } +} diff --git a/cmd/kubeadm/test/cmd/join_test.go b/cmd/kubeadm/test/cmd/join_test.go index 54b59901f3c..e4c8efdbce2 100644 --- a/cmd/kubeadm/test/cmd/join_test.go +++ b/cmd/kubeadm/test/cmd/join_test.go @@ -21,7 +21,7 @@ import "testing" // kubeadmReset executes "kubeadm reset" and restarts kubelet. func kubeadmReset() error { kubeadmPath := getKubeadmPath() - _, _, err := RunCmd(kubeadmPath, "reset") + _, _, _, err := RunCmd(kubeadmPath, "reset") return err } @@ -43,7 +43,7 @@ func TestCmdJoinConfig(t *testing.T) { kubeadmPath := getKubeadmPath() for _, rt := range initTest { t.Run(rt.name, func(t *testing.T) { - _, _, actual := RunCmd(kubeadmPath, "join", rt.args, "--ignore-preflight-errors=all") + _, _, _, actual := RunCmd(kubeadmPath, "join", rt.args, "--ignore-preflight-errors=all") if (actual == nil) != rt.expected { t.Errorf( "failed CmdJoinConfig running 'kubeadm join %s' with an error: %v\n\texpected: %t\n\t actual: %t", @@ -76,7 +76,7 @@ func TestCmdJoinDiscoveryFile(t *testing.T) { kubeadmPath := getKubeadmPath() for _, rt := range initTest { t.Run(rt.name, func(t *testing.T) { - _, _, actual := RunCmd(kubeadmPath, "join", rt.args, "--ignore-preflight-errors=all") + _, _, _, actual := RunCmd(kubeadmPath, "join", rt.args, "--ignore-preflight-errors=all") if (actual == nil) != rt.expected { t.Errorf( "failed CmdJoinDiscoveryFile running 'kubeadm join %s' with an error: %v\n\texpected: %t\n\t actual: %t", @@ -109,7 +109,7 @@ func TestCmdJoinDiscoveryToken(t *testing.T) { kubeadmPath := getKubeadmPath() for _, rt := range initTest { t.Run(rt.name, func(t *testing.T) { - _, _, actual := RunCmd(kubeadmPath, "join", rt.args, "--ignore-preflight-errors=all") + _, _, _, actual := RunCmd(kubeadmPath, "join", rt.args, "--ignore-preflight-errors=all") if (actual == nil) != rt.expected { t.Errorf( "failed CmdJoinDiscoveryToken running 'kubeadm join %s' with an error: %v\n\texpected: %t\n\t actual: %t", @@ -141,7 +141,7 @@ func TestCmdJoinNodeName(t *testing.T) { kubeadmPath := getKubeadmPath() for _, rt := range initTest { t.Run(rt.name, func(t *testing.T) { - _, _, actual := RunCmd(kubeadmPath, "join", rt.args, "--ignore-preflight-errors=all") + _, _, _, actual := RunCmd(kubeadmPath, "join", rt.args, "--ignore-preflight-errors=all") if (actual == nil) != rt.expected { t.Errorf( "failed CmdJoinNodeName running 'kubeadm join %s' with an error: %v\n\texpected: %t\n\t actual: %t", @@ -174,7 +174,7 @@ func TestCmdJoinTLSBootstrapToken(t *testing.T) { kubeadmPath := getKubeadmPath() for _, rt := range initTest { t.Run(rt.name, func(t *testing.T) { - _, _, actual := RunCmd(kubeadmPath, "join", rt.args, "--ignore-preflight-errors=all") + _, _, _, actual := RunCmd(kubeadmPath, "join", rt.args, "--ignore-preflight-errors=all") if (actual == nil) != rt.expected { t.Errorf( "failed CmdJoinTLSBootstrapToken running 'kubeadm join %s' with an error: %v\n\texpected: %t\n\t actual: %t", @@ -207,7 +207,7 @@ func TestCmdJoinToken(t *testing.T) { kubeadmPath := getKubeadmPath() for _, rt := range initTest { t.Run(rt.name, func(t *testing.T) { - _, _, actual := RunCmd(kubeadmPath, "join", rt.args, "--ignore-preflight-errors=all") + _, _, _, actual := RunCmd(kubeadmPath, "join", rt.args, "--ignore-preflight-errors=all") if (actual == nil) != rt.expected { t.Errorf( "failed CmdJoinToken running 'kubeadm join %s' with an error: %v\n\texpected: %t\n\t actual: %t", @@ -240,7 +240,7 @@ func TestCmdJoinBadArgs(t *testing.T) { for _, rt := range initTest { t.Run(rt.name, func(t *testing.T) { - _, _, actual := RunCmd(kubeadmPath, "join", rt.args, "--ignore-preflight-errors=all") + _, _, _, actual := RunCmd(kubeadmPath, "join", rt.args, "--ignore-preflight-errors=all") if (actual == nil) != rt.expected { t.Errorf( "failed CmdJoinBadArgs 'kubeadm join %s' with an error: %v\n\texpected: %t\n\t actual: %t", @@ -272,7 +272,7 @@ func TestCmdJoinArgsMixed(t *testing.T) { kubeadmPath := getKubeadmPath() for _, rt := range initTest { t.Run(rt.name, func(t *testing.T) { - _, _, actual := RunCmd(kubeadmPath, "join", rt.args, "--ignore-preflight-errors=all") + _, _, _, actual := RunCmd(kubeadmPath, "join", rt.args, "--ignore-preflight-errors=all") if (actual == nil) != rt.expected { t.Errorf( "failed CmdJoinArgsMixed running 'kubeadm join %s' with an error: %v\n\texpected: %t\n\t actual: %t", diff --git a/cmd/kubeadm/test/cmd/token_test.go b/cmd/kubeadm/test/cmd/token_test.go index f24b936c6c1..7d61a519db1 100644 --- a/cmd/kubeadm/test/cmd/token_test.go +++ b/cmd/kubeadm/test/cmd/token_test.go @@ -48,7 +48,7 @@ func TestCmdTokenGenerate(t *testing.T) { t.Log("kubeadm cmd tests being skipped") t.Skip() } - stdout, _, err := RunCmd(kubeadmPath, "token", "generate") + stdout, _, _, err := RunCmd(kubeadmPath, "token", "generate") if err != nil { t.Fatalf("'kubeadm token generate' exited uncleanly: %v", err) } @@ -78,7 +78,7 @@ func TestCmdTokenGenerateTypoError(t *testing.T) { } kubeadmPath := getKubeadmPath() - _, _, err := RunCmd(kubeadmPath, "token", "genorate") // subtle typo + _, _, _, err := RunCmd(kubeadmPath, "token", "genorate") // subtle typo if err == nil { t.Error("'kubeadm token genorate' (a deliberate typo) exited without an error when we expected non-zero exit status") } @@ -101,7 +101,7 @@ func TestCmdTokenDelete(t *testing.T) { kubeadmPath := getKubeadmPath() for _, rt := range tests { t.Run(rt.name, func(t *testing.T) { - _, _, actual := RunCmd(kubeadmPath, "token", "delete", rt.args) + _, _, _, actual := RunCmd(kubeadmPath, "token", "delete", rt.args) if (actual == nil) != rt.expected { t.Errorf( "failed CmdTokenDelete running 'kubeadm token %s' with an error: %v\n\texpected: %t\n\t actual: %t", diff --git a/cmd/kubeadm/test/cmd/util.go b/cmd/kubeadm/test/cmd/util.go index ecc1951b5dd..babf6684a90 100644 --- a/cmd/kubeadm/test/cmd/util.go +++ b/cmd/kubeadm/test/cmd/util.go @@ -29,24 +29,24 @@ import ( // Forked from test/e2e/framework because the e2e framework is quite bloated // for our purposes here, and modified to remove undesired logging. -func runCmdNoWrap(command string, args ...string) (string, string, error) { +func runCmdNoWrap(command string, args ...string) (string, string, int, error) { var bout, berr bytes.Buffer cmd := exec.Command(command, args...) cmd.Stdout = &bout cmd.Stderr = &berr err := cmd.Run() stdout, stderr := bout.String(), berr.String() - return stdout, stderr, err + return stdout, stderr, cmd.ProcessState.ExitCode(), err } // RunCmd is a utility function for kubeadm testing that executes a specified command -func RunCmd(command string, args ...string) (string, string, error) { - stdout, stderr, err := runCmdNoWrap(command, args...) +func RunCmd(command string, args ...string) (string, string, int, error) { + stdout, stderr, retcode, err := runCmdNoWrap(command, args...) if err != nil { - return stdout, stderr, errors.Wrapf(err, "error running %s %v; \nstdout %q, \nstderr %q, \ngot error", - command, args, stdout, stderr) + return stdout, stderr, retcode, errors.Wrapf(err, "error running %s %v; \nretcode %d, \nstdout %q, \nstderr %q, \ngot error", + command, args, retcode, stdout, stderr) } - return stdout, stderr, nil + return stdout, stderr, retcode, nil } // RunSubCommand is a utility function for kubeadm testing that executes a Cobra sub command diff --git a/cmd/kubeadm/test/cmd/version_test.go b/cmd/kubeadm/test/cmd/version_test.go index 320bb48249c..98511b6aa6a 100644 --- a/cmd/kubeadm/test/cmd/version_test.go +++ b/cmd/kubeadm/test/cmd/version_test.go @@ -53,7 +53,7 @@ func TestCmdVersion(t *testing.T) { kubeadmPath := getKubeadmPath() for _, rt := range versionTest { t.Run(rt.name, func(t *testing.T) { - stdout, _, actual := RunCmd(kubeadmPath, "version", rt.args) + stdout, _, _, actual := RunCmd(kubeadmPath, "version", rt.args) if (actual == nil) != rt.expected { t.Errorf( "failed CmdVersion running 'kubeadm version %s' with an error: %v\n\texpected: %t\n\t actual: %t", @@ -96,7 +96,7 @@ func TestCmdVersionOutputJsonOrYaml(t *testing.T) { kubeadmPath := getKubeadmPath() for _, rt := range versionTest { t.Run(rt.name, func(t *testing.T) { - stdout, _, actual := RunCmd(kubeadmPath, "version", rt.args) + stdout, _, _, actual := RunCmd(kubeadmPath, "version", rt.args) if (actual == nil) != rt.expected { t.Errorf( "failed CmdVersion running 'kubeadm version %s' with an error: %v\n\texpected: %t\n\t actual: %t",