better HostExec

- separate stdout and stderr
- return command exit code
- remove kubectl dependency
This commit is contained in:
Yecheng Fu 2019-10-15 11:50:33 +08:00
parent 9832418870
commit d8c465c8d7
3 changed files with 68 additions and 10 deletions

View File

@ -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
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)
res, err := config.hostExec.IssueCommandWithResult(ssdCmd, node)
res, err := config.hostExec.Execute(ssdCmd, node)
utils.LogResult(res)
framework.ExpectNoError(err)
num, err := strconv.Atoi(strings.TrimSpace(res))
num, err := strconv.Atoi(strings.TrimSpace(res.Stdout))
framework.ExpectNoError(err)
if num < 1 {
framework.Skipf("Requires at least 1 %s %s localSSD ", ssdInterface, filesystemType)

View File

@ -26,7 +26,9 @@ go_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/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/log:go_default_library",
"//test/e2e/framework/node:go_default_library",
"//test/e2e/framework/pod:go_default_library",
"//test/e2e/framework/ssh:go_default_library",

View File

@ -20,12 +20,33 @@ import (
"fmt"
v1 "k8s.io/api/core/v1"
"k8s.io/client-go/util/exec"
"k8s.io/kubernetes/test/e2e/framework"
e2elog "k8s.io/kubernetes/test/e2e/framework/log"
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.
type HostExec interface {
Execute(cmd string, node *v1.Node) (Result, error)
IssueCommandWithResult(cmd string, node *v1.Node) (string, error)
IssueCommand(cmd string, node *v1.Node) error
Cleanup()
@ -84,21 +105,35 @@ func (h *hostExecutor) launchNodeExecPod(node string) *v1.Pod {
return pod
}
// IssueCommandWithResult issues command on given node and returns stdout.
func (h *hostExecutor) IssueCommandWithResult(cmd string, node *v1.Node) (string, error) {
// Execute executes the command on the given node. If there is no 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]
if !ok {
pod = h.launchNodeExecPod(node.Name)
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
}
args := []string{
"exec",
fmt.Sprintf("--namespace=%v", pod.Namespace),
pod.Name,
"--",
"nsenter",
"--mount=/rootfs/proc/1/ns/mnt",
"--",
@ -106,7 +141,27 @@ func (h *hostExecutor) IssueCommandWithResult(cmd string, node *v1.Node) (string
"-c",
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.