mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-10-31 18:28:13 +00:00 
			
		
		
		
	e2e node: support running the test binary under a debugger
Single-stepping interactively through a test can be useful to understand what's happening and to investigate the state at each step. Similar support was added early to hack/ginkgo-e2e.sh, so the same env variable is used again.
This commit is contained in:
		| @@ -269,6 +269,9 @@ define TEST_E2E_NODE_HELP_INFO | |||||||
| #  INSTANCE_TYPE: For REMOTE=true only. Machine type to use. | #  INSTANCE_TYPE: For REMOTE=true only. Machine type to use. | ||||||
| #  NODE_ENV: For REMOTE=true only. Additional metadata keys to add the instance. | #  NODE_ENV: For REMOTE=true only. Additional metadata keys to add the instance. | ||||||
| #  RUNTIME_CONFIG: The runtime configuration for the API server on the node e2e tests. | #  RUNTIME_CONFIG: The runtime configuration for the API server on the node e2e tests. | ||||||
|  | #  E2E_TEST_DEBUG_TOOL: one of dlv/delve/gdb. Runs the test/e2e_node test binary | ||||||
|  | #    interactively under the chosen debugger. Only works for REMOTE=false and | ||||||
|  | #    in combination with DBG=1. | ||||||
| # | # | ||||||
| # Example: | # Example: | ||||||
| #   make test-e2e-node FOCUS=Kubelet SKIP=container | #   make test-e2e-node FOCUS=Kubelet SKIP=container | ||||||
|   | |||||||
| @@ -53,6 +53,18 @@ ssh_key=${SSH_KEY:-} | |||||||
| ssh_options=${SSH_OPTIONS:-} | ssh_options=${SSH_OPTIONS:-} | ||||||
| kubelet_config_file=${KUBELET_CONFIG_FILE:-"test/e2e_node/jenkins/default-kubelet-config.yaml"} | kubelet_config_file=${KUBELET_CONFIG_FILE:-"test/e2e_node/jenkins/default-kubelet-config.yaml"} | ||||||
|  |  | ||||||
|  | # If set, the command executed will be: | ||||||
|  | # - `dlv exec` if set to "delve" | ||||||
|  | # - `gdb` if set to "gdb" | ||||||
|  | # NOTE: for this to work the e2e_node.test binary has to be compiled with DBG=1. | ||||||
|  | # | ||||||
|  | # The name of this variable is the same as in ginkgo-e2e.sh. | ||||||
|  | debug_tool=${E2E_TEST_DEBUG_TOOL:-} | ||||||
|  | if [ "${remote}" = true ] && [ -n "${debug_tool}" ]; then | ||||||
|  |     echo "Support for E2E_TEST_DEBUG_TOOL=${debug_tool} is only implemented for REMOTE=false." | ||||||
|  |     exit 1 | ||||||
|  | fi | ||||||
|  |  | ||||||
| # Parse the flags to pass to ginkgo | # Parse the flags to pass to ginkgo | ||||||
| ginkgoflags="-timeout=24h" | ginkgoflags="-timeout=24h" | ||||||
| if [[ ${parallelism} -gt 1 ]]; then | if [[ ${parallelism} -gt 1 ]]; then | ||||||
| @@ -247,6 +259,7 @@ else | |||||||
|   # Test using the host the script was run on |   # Test using the host the script was run on | ||||||
|   # Provided for backwards compatibility |   # Provided for backwards compatibility | ||||||
|   go run test/e2e_node/runner/local/run_local.go \ |   go run test/e2e_node/runner/local/run_local.go \ | ||||||
|  |     --debug-tool="${debug_tool}" \ | ||||||
|     --system-spec-name="${system_spec_name}" --extra-envs="${extra_envs}" \ |     --system-spec-name="${system_spec_name}" --extra-envs="${extra_envs}" \ | ||||||
|     --ginkgo-flags="${ginkgoflags}" \ |     --ginkgo-flags="${ginkgoflags}" \ | ||||||
|     --test-flags="--v 4 --report-dir=${artifacts} --node-name $(hostname) ${test_args}" \ |     --test-flags="--v 4 --report-dir=${artifacts} --node-name $(hostname) ${test_args}" \ | ||||||
|   | |||||||
| @@ -22,6 +22,7 @@ import ( | |||||||
| 	"os" | 	"os" | ||||||
| 	"os/exec" | 	"os/exec" | ||||||
| 	"path/filepath" | 	"path/filepath" | ||||||
|  | 	"regexp" | ||||||
| 	"strings" | 	"strings" | ||||||
|  |  | ||||||
| 	"k8s.io/kubernetes/test/e2e_node/builder" | 	"k8s.io/kubernetes/test/e2e_node/builder" | ||||||
| @@ -32,12 +33,13 @@ import ( | |||||||
| ) | ) | ||||||
|  |  | ||||||
| var buildDependencies = flag.Bool("build-dependencies", true, "If true, build all dependencies.") | var buildDependencies = flag.Bool("build-dependencies", true, "If true, build all dependencies.") | ||||||
| var ginkgoFlags = flag.String("ginkgo-flags", "", "Space-separated list of arguments to pass to Ginkgo test runner.") | var ginkgoFlags = flag.String("ginkgo-flags", "", "Space-separated list of arguments to pass to Ginkgo test runner, with shell-style quoting and escaping.") | ||||||
| var testFlags = flag.String("test-flags", "", "Space-separated list of arguments to pass to node e2e test.") | var testFlags = flag.String("test-flags", "", "Space-separated list of arguments to pass to node e2e test, with shell-style quoting and escaping.") | ||||||
| var systemSpecName = flag.String("system-spec-name", "", fmt.Sprintf("The name of the system spec used for validating the image in the node conformance test. The specs are at %s. If unspecified, the default built-in spec (system.DefaultSpec) will be used.", system.SystemSpecPath)) | var systemSpecName = flag.String("system-spec-name", "", fmt.Sprintf("The name of the system spec used for validating the image in the node conformance test. The specs are at %s. If unspecified, the default built-in spec (system.DefaultSpec) will be used.", system.SystemSpecPath)) | ||||||
| var extraEnvs = flag.String("extra-envs", "", "The extra environment variables needed for node e2e tests. Format: a list of key=value pairs, e.g., env1=val1,env2=val2") | var extraEnvs = flag.String("extra-envs", "", "The extra environment variables needed for node e2e tests. Format: a list of key=value pairs, e.g., env1=val1,env2=val2") | ||||||
| var runtimeConfig = flag.String("runtime-config", "", "The runtime configuration for the API server on the node e2e tests. Format: a list of key=value pairs, e.g., env1=val1,env2=val2") | var runtimeConfig = flag.String("runtime-config", "", "The runtime configuration for the API server on the node e2e tests. Format: a list of key=value pairs, e.g., env1=val1,env2=val2") | ||||||
| var kubeletConfigFile = flag.String("kubelet-config-file", "", "The KubeletConfiguration file that should be applied to the kubelet") | var kubeletConfigFile = flag.String("kubelet-config-file", "", "The KubeletConfiguration file that should be applied to the kubelet") | ||||||
|  | var debugTool = flag.String("debug-tool", "", "'delve', 'dlv' or 'gdb': run e2e_node.test under that debugger") | ||||||
|  |  | ||||||
| func main() { | func main() { | ||||||
| 	klog.InitFlags(nil) | 	klog.InitFlags(nil) | ||||||
| @@ -56,10 +58,34 @@ func main() { | |||||||
| 		klog.Fatalf("Failed to get build output directory: %v", err) | 		klog.Fatalf("Failed to get build output directory: %v", err) | ||||||
| 	} | 	} | ||||||
| 	klog.Infof("Got build output dir: %v", outputDir) | 	klog.Infof("Got build output dir: %v", outputDir) | ||||||
| 	ginkgo := filepath.Join(outputDir, "ginkgo") |  | ||||||
| 	test := filepath.Join(outputDir, "e2e_node.test") | 	test := filepath.Join(outputDir, "e2e_node.test") | ||||||
|  | 	interactive := false | ||||||
|  | 	var cmd string | ||||||
|  | 	var args []string | ||||||
|  | 	switch *debugTool { | ||||||
|  | 	case "": | ||||||
|  | 		// No debugger, run gingko directly. | ||||||
|  | 		cmd = filepath.Join(outputDir, "ginkgo") | ||||||
|  | 		args = []string{*ginkgoFlags, test, "--"} | ||||||
|  | 	case "delve", "dlv": | ||||||
|  | 		dlv, err := exec.LookPath("dlv") | ||||||
|  | 		if err != nil { | ||||||
|  | 			klog.Fatalf("'dlv' not found: %v", err) | ||||||
|  | 		} | ||||||
|  | 		interactive = true | ||||||
|  | 		cmd = dlv | ||||||
|  | 		args = []string{"exec", test, "--", addGinkgoArgPrefix(*ginkgoFlags)} | ||||||
|  | 	case "gdb": | ||||||
|  | 		gdb, err := exec.LookPath("gdb") | ||||||
|  | 		if err != nil { | ||||||
|  | 			klog.Fatalf("'gdb' not found: %v", err) | ||||||
|  | 		} | ||||||
|  | 		interactive = true | ||||||
|  | 		cmd = gdb | ||||||
|  | 		args = []string{test, "--", addGinkgoArgPrefix(*ginkgoFlags)} | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	args := []string{*ginkgoFlags, test, "--", *testFlags, fmt.Sprintf("--runtime-config=%s", *runtimeConfig)} | 	args = append(args, *testFlags, fmt.Sprintf("--runtime-config=%s", *runtimeConfig)) | ||||||
| 	if *systemSpecName != "" { | 	if *systemSpecName != "" { | ||||||
| 		rootDir, err := utils.GetK8sRootDir() | 		rootDir, err := utils.GetK8sRootDir() | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| @@ -71,16 +97,40 @@ func main() { | |||||||
| 	if *kubeletConfigFile != "" { | 	if *kubeletConfigFile != "" { | ||||||
| 		args = append(args, fmt.Sprintf("--kubelet-config-file=\"%s\"", *kubeletConfigFile)) | 		args = append(args, fmt.Sprintf("--kubelet-config-file=\"%s\"", *kubeletConfigFile)) | ||||||
| 	} | 	} | ||||||
| 	if err := runCommand(ginkgo, args...); err != nil { | 	if err := runCommand(interactive, cmd, args...); err != nil { | ||||||
| 		klog.Exitf("Test failed: %v", err) | 		klog.Exitf("Test failed: %v", err) | ||||||
| 	} | 	} | ||||||
| 	return | 	return | ||||||
| } | } | ||||||
|  |  | ||||||
| func runCommand(name string, args ...string) error { | func addGinkgoArgPrefix(ginkgoFlags string) string { | ||||||
|  | 	// Warning, hack! This simplistic search/replace assumes that hyphens do not appear | ||||||
|  | 	// inside argument values. | ||||||
|  | 	// | ||||||
|  | 	// The right solution would be to use github.com/anmitsu/go-shlex to split | ||||||
|  | 	// the -ginkgo-flags and -test-flags strings into individual arguments, then invoke | ||||||
|  | 	// exec.Command with the resulting string slice instead of passing a single string | ||||||
|  | 	// to sh. But github.com/anmitsu/go-shlex is not a Kubernetes dependency and not | ||||||
|  | 	// worth adding. | ||||||
|  |  | ||||||
|  | 	ginkgoFlags = regexp.MustCompile(`(^| )--?`).ReplaceAllString(ginkgoFlags, `$1--ginkgo.`) | ||||||
|  | 	return ginkgoFlags | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func runCommand(interactive bool, name string, args ...string) error { | ||||||
| 	klog.Infof("Running command: %v %v", name, strings.Join(args, " ")) | 	klog.Infof("Running command: %v %v", name, strings.Join(args, " ")) | ||||||
|  | 	// Using sh is necessary because the args are using POSIX quoting. | ||||||
|  | 	// sh has to parse that. | ||||||
| 	cmd := exec.Command("sudo", "sh", "-c", strings.Join(append([]string{name}, args...), " ")) | 	cmd := exec.Command("sudo", "sh", "-c", strings.Join(append([]string{name}, args...), " ")) | ||||||
| 	cmd.Stdout = os.Stdout | 	if interactive { | ||||||
| 	cmd.Stderr = os.Stderr | 		// stdin must be a console. | ||||||
|  | 		cmd.Stdin = os.Stdin | ||||||
|  | 		cmd.Stdout = os.Stdin | ||||||
|  | 		cmd.Stderr = os.Stdin | ||||||
|  | 	} else { | ||||||
|  | 		cmd.Stdout = os.Stdout | ||||||
|  | 		cmd.Stderr = os.Stderr | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	return cmd.Run() | 	return cmd.Run() | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Patrick Ohly
					Patrick Ohly