mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-21 10:51:29 +00:00
kubeadm: add test to detect panics when given certain feature gates
This integration test allows us to detect if a given feature gate will panic kubeadm. This builds on the assumption that a golang panic makes the process exit with the code 2. These tests are not trying to check if the init process succeeds or not, their only purpose is to ensure that the exit code of the `kubeadm init` invocation is not 2, thus, reflecting a golang panic. Some refactors had to be made to the test code, so we return the exit code along with stdout and stderr.
This commit is contained in:
parent
af67b2cbce
commit
cd1ad5646e
@ -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",
|
||||
|
@ -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,
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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
|
||||
|
@ -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",
|
||||
|
Loading…
Reference in New Issue
Block a user