Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve e2e HostExec utility #84444

Merged
merged 1 commit into from Oct 30, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 3 additions & 2 deletions test/e2e/storage/persistent_volumes-local.go
Expand Up @@ -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)
Copy link
Member Author

@cofyc cofyc Oct 28, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as an example usage of Execute method. I didn't remove all IssueCommand/IssueCommandWithResult use cases. These two methods are simple to use sometimes.

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)
Expand Down
2 changes: 2 additions & 0 deletions test/e2e/storage/utils/BUILD
Expand Up @@ -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",
Expand Down
71 changes: 63 additions & 8 deletions test/e2e/storage/utils/host_exec.go
Expand Up @@ -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 {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Like ssh.Result except no User field

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)
Copy link
Member Author

@cofyc cofyc Oct 28, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

like ssh.LogResult, here are some examples:

Oct 28 19:14:21.053: INFO: exec k8s-testing-worker: command:   ls -1 /mnt/disks/by-uuid/google-local-ssds-scsi-fs/ | wc -l
Oct 28 19:14:21.053: INFO: exec k8s-testing-worker: stdout:    "0\n"
Oct 28 19:14:21.053: INFO: exec k8s-testing-worker: stderr:    "ls: cannot access '/mnt/disks/by-uuid/google-local-ssds-scsi-fs/': No such file or directory\n"
Oct 28 19:14:21.053: INFO: exec k8s-testing-worker: exit code: 0

failed command log:

Oct 28 18:59:52.926: INFO: exec k8s-testing-worker: command:   ls -1 /mnt/disks/by-uuid/google-local-ssds-scsi-fs/
Oct 28 18:59:52.926: INFO: exec k8s-testing-worker: stdout:    ""
Oct 28 18:59:52.926: INFO: exec k8s-testing-worker: stderr:    "ls: cannot access '/mnt/disks/by-uuid/google-local-ssds-scsi-fs/': No such file or directory\n"
Oct 28 18:59:52.926: INFO: exec k8s-testing-worker: exit code: 2

}

// HostExec represents interface we require to execute commands on remote host.
type HostExec interface {
Execute(cmd string, node *v1.Node) (Result, error)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New method to replace ssh.SSH.

IssueCommandWithResult(cmd string, node *v1.Node) (string, error)
IssueCommand(cmd string, node *v1.Node) error
Cleanup()
Expand Down Expand Up @@ -84,29 +105,63 @@ 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 {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// 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]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I remember @jsafrane hit some issue with creating two host exec pods in the same test. Did that ever get resolved? How about hostexec for two tests running in parallel?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It had been resolved. Originally, it used a static pod name. If a test has two instances of HostExec, pod names will conflict. @jsafrane switched to use GenerateName to avoid pod name conflict here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A limitation is this utility cannot be shared by multiple parallel tests. We can add support when necessary.

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",
"--",
"sh",
"-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.
Expand Down