better HostExec
- separate stdout and stderr - return command exit code - remove kubectl dependency
This commit is contained in:
		@@ -1182,9 +1182,10 @@ func validateStatefulSet(config *localTestConfig, ss *appsv1.StatefulSet, anti b
 | 
				
			|||||||
// and skips if a disk of that type does not exist on the node
 | 
					// and skips if a disk of that type does not exist on the node
 | 
				
			||||||
func SkipUnlessLocalSSDExists(config *localTestConfig, ssdInterface, filesystemType string, node *v1.Node) {
 | 
					func SkipUnlessLocalSSDExists(config *localTestConfig, ssdInterface, filesystemType string, node *v1.Node) {
 | 
				
			||||||
	ssdCmd := fmt.Sprintf("ls -1 /mnt/disks/by-uuid/google-local-ssds-%s-%s/ | wc -l", ssdInterface, filesystemType)
 | 
						ssdCmd := fmt.Sprintf("ls -1 /mnt/disks/by-uuid/google-local-ssds-%s-%s/ | wc -l", ssdInterface, filesystemType)
 | 
				
			||||||
	res, err := config.hostExec.IssueCommandWithResult(ssdCmd, node)
 | 
						res, err := config.hostExec.Execute(ssdCmd, node)
 | 
				
			||||||
 | 
						utils.LogResult(res)
 | 
				
			||||||
	framework.ExpectNoError(err)
 | 
						framework.ExpectNoError(err)
 | 
				
			||||||
	num, err := strconv.Atoi(strings.TrimSpace(res))
 | 
						num, err := strconv.Atoi(strings.TrimSpace(res.Stdout))
 | 
				
			||||||
	framework.ExpectNoError(err)
 | 
						framework.ExpectNoError(err)
 | 
				
			||||||
	if num < 1 {
 | 
						if num < 1 {
 | 
				
			||||||
		framework.Skipf("Requires at least 1 %s %s localSSD ", ssdInterface, filesystemType)
 | 
							framework.Skipf("Requires at least 1 %s %s localSSD ", ssdInterface, filesystemType)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -26,7 +26,9 @@ go_library(
 | 
				
			|||||||
        "//staging/src/k8s.io/apimachinery/pkg/util/uuid:go_default_library",
 | 
					        "//staging/src/k8s.io/apimachinery/pkg/util/uuid:go_default_library",
 | 
				
			||||||
        "//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library",
 | 
					        "//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library",
 | 
				
			||||||
        "//staging/src/k8s.io/client-go/kubernetes:go_default_library",
 | 
					        "//staging/src/k8s.io/client-go/kubernetes:go_default_library",
 | 
				
			||||||
 | 
					        "//staging/src/k8s.io/client-go/util/exec:go_default_library",
 | 
				
			||||||
        "//test/e2e/framework:go_default_library",
 | 
					        "//test/e2e/framework:go_default_library",
 | 
				
			||||||
 | 
					        "//test/e2e/framework/log:go_default_library",
 | 
				
			||||||
        "//test/e2e/framework/node:go_default_library",
 | 
					        "//test/e2e/framework/node:go_default_library",
 | 
				
			||||||
        "//test/e2e/framework/pod:go_default_library",
 | 
					        "//test/e2e/framework/pod:go_default_library",
 | 
				
			||||||
        "//test/e2e/framework/ssh:go_default_library",
 | 
					        "//test/e2e/framework/ssh:go_default_library",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,12 +20,33 @@ import (
 | 
				
			|||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	v1 "k8s.io/api/core/v1"
 | 
						v1 "k8s.io/api/core/v1"
 | 
				
			||||||
 | 
						"k8s.io/client-go/util/exec"
 | 
				
			||||||
	"k8s.io/kubernetes/test/e2e/framework"
 | 
						"k8s.io/kubernetes/test/e2e/framework"
 | 
				
			||||||
 | 
						e2elog "k8s.io/kubernetes/test/e2e/framework/log"
 | 
				
			||||||
	e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
 | 
						e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Result holds the execution result of remote execution command.
 | 
				
			||||||
 | 
					type Result struct {
 | 
				
			||||||
 | 
						Host   string
 | 
				
			||||||
 | 
						Cmd    string
 | 
				
			||||||
 | 
						Stdout string
 | 
				
			||||||
 | 
						Stderr string
 | 
				
			||||||
 | 
						Code   int
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// LogResult records result log
 | 
				
			||||||
 | 
					func LogResult(result Result) {
 | 
				
			||||||
 | 
						remote := result.Host
 | 
				
			||||||
 | 
						e2elog.Logf("exec %s: command:   %s", remote, result.Cmd)
 | 
				
			||||||
 | 
						e2elog.Logf("exec %s: stdout:    %q", remote, result.Stdout)
 | 
				
			||||||
 | 
						e2elog.Logf("exec %s: stderr:    %q", remote, result.Stderr)
 | 
				
			||||||
 | 
						e2elog.Logf("exec %s: exit code: %d", remote, result.Code)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// HostExec represents interface we require to execute commands on remote host.
 | 
					// HostExec represents interface we require to execute commands on remote host.
 | 
				
			||||||
type HostExec interface {
 | 
					type HostExec interface {
 | 
				
			||||||
 | 
						Execute(cmd string, node *v1.Node) (Result, error)
 | 
				
			||||||
	IssueCommandWithResult(cmd string, node *v1.Node) (string, error)
 | 
						IssueCommandWithResult(cmd string, node *v1.Node) (string, error)
 | 
				
			||||||
	IssueCommand(cmd string, node *v1.Node) error
 | 
						IssueCommand(cmd string, node *v1.Node) error
 | 
				
			||||||
	Cleanup()
 | 
						Cleanup()
 | 
				
			||||||
@@ -84,21 +105,35 @@ func (h *hostExecutor) launchNodeExecPod(node string) *v1.Pod {
 | 
				
			|||||||
	return pod
 | 
						return pod
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// IssueCommandWithResult issues command on given node and returns stdout.
 | 
					// Execute executes the command on the given node. If there is no error
 | 
				
			||||||
func (h *hostExecutor) IssueCommandWithResult(cmd string, node *v1.Node) (string, error) {
 | 
					// performing the remote command execution, the stdout, stderr and exit code
 | 
				
			||||||
 | 
					// are returned.
 | 
				
			||||||
 | 
					// This works like ssh.SSH(...) utility.
 | 
				
			||||||
 | 
					func (h *hostExecutor) Execute(cmd string, node *v1.Node) (Result, error) {
 | 
				
			||||||
 | 
						result, err := h.exec(cmd, node)
 | 
				
			||||||
 | 
						if codeExitErr, ok := err.(exec.CodeExitError); ok {
 | 
				
			||||||
 | 
							// extract the exit code of remote command and silence the command
 | 
				
			||||||
 | 
							// non-zero exit code error
 | 
				
			||||||
 | 
							result.Code = codeExitErr.ExitStatus()
 | 
				
			||||||
 | 
							err = nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return result, err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (h *hostExecutor) exec(cmd string, node *v1.Node) (Result, error) {
 | 
				
			||||||
 | 
						result := Result{
 | 
				
			||||||
 | 
							Host: node.Name,
 | 
				
			||||||
 | 
							Cmd:  cmd,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	pod, ok := h.nodeExecPods[node.Name]
 | 
						pod, ok := h.nodeExecPods[node.Name]
 | 
				
			||||||
	if !ok {
 | 
						if !ok {
 | 
				
			||||||
		pod = h.launchNodeExecPod(node.Name)
 | 
							pod = h.launchNodeExecPod(node.Name)
 | 
				
			||||||
		if pod == nil {
 | 
							if pod == nil {
 | 
				
			||||||
			return "", fmt.Errorf("failed to create hostexec pod for node %q", node)
 | 
								return result, fmt.Errorf("failed to create hostexec pod for node %q", node)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		h.nodeExecPods[node.Name] = pod
 | 
							h.nodeExecPods[node.Name] = pod
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	args := []string{
 | 
						args := []string{
 | 
				
			||||||
		"exec",
 | 
					 | 
				
			||||||
		fmt.Sprintf("--namespace=%v", pod.Namespace),
 | 
					 | 
				
			||||||
		pod.Name,
 | 
					 | 
				
			||||||
		"--",
 | 
					 | 
				
			||||||
		"nsenter",
 | 
							"nsenter",
 | 
				
			||||||
		"--mount=/rootfs/proc/1/ns/mnt",
 | 
							"--mount=/rootfs/proc/1/ns/mnt",
 | 
				
			||||||
		"--",
 | 
							"--",
 | 
				
			||||||
@@ -106,7 +141,27 @@ func (h *hostExecutor) IssueCommandWithResult(cmd string, node *v1.Node) (string
 | 
				
			|||||||
		"-c",
 | 
							"-c",
 | 
				
			||||||
		cmd,
 | 
							cmd,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return framework.RunKubectl(args...)
 | 
						containerName := pod.Spec.Containers[0].Name
 | 
				
			||||||
 | 
						var err error
 | 
				
			||||||
 | 
						result.Stdout, result.Stderr, err = h.Framework.ExecWithOptions(framework.ExecOptions{
 | 
				
			||||||
 | 
							Command:            args,
 | 
				
			||||||
 | 
							Namespace:          pod.Namespace,
 | 
				
			||||||
 | 
							PodName:            pod.Name,
 | 
				
			||||||
 | 
							ContainerName:      containerName,
 | 
				
			||||||
 | 
							Stdin:              nil,
 | 
				
			||||||
 | 
							CaptureStdout:      true,
 | 
				
			||||||
 | 
							CaptureStderr:      true,
 | 
				
			||||||
 | 
							PreserveWhitespace: true,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						return result, err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// IssueCommandWithResult issues command on the given node and returns stdout as
 | 
				
			||||||
 | 
					// result. It returns error if there are some issues executing the command or
 | 
				
			||||||
 | 
					// the command exits non-zero.
 | 
				
			||||||
 | 
					func (h *hostExecutor) IssueCommandWithResult(cmd string, node *v1.Node) (string, error) {
 | 
				
			||||||
 | 
						result, err := h.exec(cmd, node)
 | 
				
			||||||
 | 
						return result.Stdout, err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// IssueCommand works like IssueCommandWithResult, but discards result.
 | 
					// IssueCommand works like IssueCommandWithResult, but discards result.
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user