1
16
17 package utils
18
19 import (
20 "context"
21 "fmt"
22
23 v1 "k8s.io/api/core/v1"
24 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
25 "k8s.io/client-go/util/exec"
26 "k8s.io/kubernetes/test/e2e/framework"
27 e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
28 )
29
30
31 type Result struct {
32 Host string
33 Cmd string
34 Stdout string
35 Stderr string
36 Code int
37 }
38
39
40 func LogResult(result Result) {
41 remote := result.Host
42 framework.Logf("exec %s: command: %s", remote, result.Cmd)
43 framework.Logf("exec %s: stdout: %q", remote, result.Stdout)
44 framework.Logf("exec %s: stderr: %q", remote, result.Stderr)
45 framework.Logf("exec %s: exit code: %d", remote, result.Code)
46 }
47
48
49 type HostExec interface {
50 Execute(ctx context.Context, cmd string, node *v1.Node) (Result, error)
51 IssueCommandWithResult(ctx context.Context, cmd string, node *v1.Node) (string, error)
52 IssueCommand(ctx context.Context, cmd string, node *v1.Node) error
53 Cleanup(ctx context.Context)
54 }
55
56
57 type hostExecutor struct {
58 *framework.Framework
59 nodeExecPods map[string]*v1.Pod
60 }
61
62
63 func NewHostExec(framework *framework.Framework) HostExec {
64 return &hostExecutor{
65 Framework: framework,
66 nodeExecPods: make(map[string]*v1.Pod),
67 }
68 }
69
70
71
72 func (h *hostExecutor) launchNodeExecPod(ctx context.Context, node string) *v1.Pod {
73 f := h.Framework
74 cs := f.ClientSet
75 ns := f.Namespace
76
77 hostExecPod := e2epod.NewExecPodSpec(ns.Name, "", true)
78 hostExecPod.GenerateName = fmt.Sprintf("hostexec-%s-", node)
79
80 if framework.TestContext.NodeE2E {
81
82 hostExecPod.Spec.NodeName = node
83 } else {
84
85
86
87
88 e2epod.SetNodeAffinity(&hostExecPod.Spec, node)
89
90 }
91 hostExecPod.Spec.Volumes = []v1.Volume{
92 {
93
94 Name: "rootfs",
95 VolumeSource: v1.VolumeSource{
96 HostPath: &v1.HostPathVolumeSource{
97 Path: "/",
98 },
99 },
100 },
101 }
102 hostExecPod.Spec.Containers[0].VolumeMounts = []v1.VolumeMount{
103 {
104 Name: "rootfs",
105 MountPath: "/rootfs",
106 ReadOnly: true,
107 },
108 }
109 hostExecPod.Spec.Containers[0].SecurityContext = &v1.SecurityContext{
110 Privileged: func(privileged bool) *bool {
111 return &privileged
112 }(true),
113 }
114 pod, err := cs.CoreV1().Pods(ns.Name).Create(ctx, hostExecPod, metav1.CreateOptions{})
115 framework.ExpectNoError(err)
116 err = e2epod.WaitTimeoutForPodRunningInNamespace(ctx, cs, pod.Name, pod.Namespace, f.Timeouts.PodStart)
117 framework.ExpectNoError(err)
118 return pod
119 }
120
121
122
123
124
125 func (h *hostExecutor) Execute(ctx context.Context, cmd string, node *v1.Node) (Result, error) {
126 result, err := h.exec(ctx, cmd, node)
127 if codeExitErr, ok := err.(exec.CodeExitError); ok {
128
129
130 result.Code = codeExitErr.ExitStatus()
131 err = nil
132 }
133 return result, err
134 }
135
136 func (h *hostExecutor) exec(ctx context.Context, cmd string, node *v1.Node) (Result, error) {
137 result := Result{
138 Host: node.Name,
139 Cmd: cmd,
140 }
141 pod, ok := h.nodeExecPods[node.Name]
142 if !ok {
143 pod = h.launchNodeExecPod(ctx, node.Name)
144 if pod == nil {
145 return result, fmt.Errorf("failed to create hostexec pod for node %q", node)
146 }
147 h.nodeExecPods[node.Name] = pod
148 }
149 args := []string{
150 "nsenter",
151 "--mount=/rootfs/proc/1/ns/mnt",
152 "--",
153 "sh",
154 "-c",
155 cmd,
156 }
157 containerName := pod.Spec.Containers[0].Name
158 var err error
159 result.Stdout, result.Stderr, err = e2epod.ExecWithOptions(h.Framework, e2epod.ExecOptions{
160 Command: args,
161 Namespace: pod.Namespace,
162 PodName: pod.Name,
163 ContainerName: containerName,
164 Stdin: nil,
165 CaptureStdout: true,
166 CaptureStderr: true,
167 PreserveWhitespace: true,
168 })
169 return result, err
170 }
171
172
173
174
175 func (h *hostExecutor) IssueCommandWithResult(ctx context.Context, cmd string, node *v1.Node) (string, error) {
176 result, err := h.exec(ctx, cmd, node)
177 if err != nil {
178 LogResult(result)
179 }
180 return result.Stdout, err
181 }
182
183
184 func (h *hostExecutor) IssueCommand(ctx context.Context, cmd string, node *v1.Node) error {
185 _, err := h.IssueCommandWithResult(ctx, cmd, node)
186 return err
187 }
188
189
190
191
192 func (h *hostExecutor) Cleanup(ctx context.Context) {
193 for _, pod := range h.nodeExecPods {
194 e2epod.DeletePodOrFail(ctx, h.Framework.ClientSet, pod.Namespace, pod.Name)
195 }
196 h.nodeExecPods = make(map[string]*v1.Pod)
197 }
198
View as plain text