k8s.io/kubernetes@v1.29.3/test/e2e/framework/pod/exec_util.go (about) 1 /* 2 Copyright 2016 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package pod 18 19 import ( 20 "bytes" 21 "context" 22 "io" 23 "net/url" 24 "strings" 25 26 v1 "k8s.io/api/core/v1" 27 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 28 "k8s.io/client-go/kubernetes/scheme" 29 restclient "k8s.io/client-go/rest" 30 "k8s.io/client-go/tools/remotecommand" 31 "k8s.io/kubernetes/test/e2e/framework" 32 33 "github.com/onsi/gomega" 34 ) 35 36 // ExecOptions passed to ExecWithOptions 37 type ExecOptions struct { 38 Command []string 39 Namespace string 40 PodName string 41 ContainerName string 42 Stdin io.Reader 43 CaptureStdout bool 44 CaptureStderr bool 45 // If false, whitespace in std{err,out} will be removed. 46 PreserveWhitespace bool 47 Quiet bool 48 } 49 50 // ExecWithOptions executes a command in the specified container, 51 // returning stdout, stderr and error. `options` allowed for 52 // additional parameters to be passed. 53 func ExecWithOptions(f *framework.Framework, options ExecOptions) (string, string, error) { 54 if !options.Quiet { 55 framework.Logf("ExecWithOptions %+v", options) 56 } 57 config, err := framework.LoadConfig() 58 framework.ExpectNoError(err, "failed to load restclient config") 59 60 const tty = false 61 62 framework.Logf("ExecWithOptions: Clientset creation") 63 req := f.ClientSet.CoreV1().RESTClient().Post(). 64 Resource("pods"). 65 Name(options.PodName). 66 Namespace(options.Namespace). 67 SubResource("exec"). 68 Param("container", options.ContainerName) 69 req.VersionedParams(&v1.PodExecOptions{ 70 Container: options.ContainerName, 71 Command: options.Command, 72 Stdin: options.Stdin != nil, 73 Stdout: options.CaptureStdout, 74 Stderr: options.CaptureStderr, 75 TTY: tty, 76 }, scheme.ParameterCodec) 77 78 var stdout, stderr bytes.Buffer 79 framework.Logf("ExecWithOptions: execute(POST %s)", req.URL()) 80 err = execute("POST", req.URL(), config, options.Stdin, &stdout, &stderr, tty) 81 if options.PreserveWhitespace { 82 return stdout.String(), stderr.String(), err 83 } 84 return strings.TrimSpace(stdout.String()), strings.TrimSpace(stderr.String()), err 85 } 86 87 // ExecCommandInContainerWithFullOutput executes a command in the 88 // specified container and return stdout, stderr and error 89 func ExecCommandInContainerWithFullOutput(f *framework.Framework, podName, containerName string, cmd ...string) (string, string, error) { 90 // TODO (pohly): add context support 91 return ExecWithOptions(f, ExecOptions{ 92 Command: cmd, 93 Namespace: f.Namespace.Name, 94 PodName: podName, 95 ContainerName: containerName, 96 Stdin: nil, 97 CaptureStdout: true, 98 CaptureStderr: true, 99 PreserveWhitespace: false, 100 }) 101 } 102 103 // ExecCommandInContainer executes a command in the specified container. 104 func ExecCommandInContainer(f *framework.Framework, podName, containerName string, cmd ...string) string { 105 stdout, stderr, err := ExecCommandInContainerWithFullOutput(f, podName, containerName, cmd...) 106 framework.Logf("Exec stderr: %q", stderr) 107 framework.ExpectNoError(err, 108 "failed to execute command in pod %v, container %v: %v", 109 podName, containerName, err) 110 return stdout 111 } 112 113 // ExecShellInContainer executes the specified command on the pod's container. 114 func ExecShellInContainer(f *framework.Framework, podName, containerName string, cmd string) string { 115 return ExecCommandInContainer(f, podName, containerName, "/bin/sh", "-c", cmd) 116 } 117 118 func execCommandInPod(ctx context.Context, f *framework.Framework, podName string, cmd ...string) string { 119 pod, err := NewPodClient(f).Get(ctx, podName, metav1.GetOptions{}) 120 framework.ExpectNoError(err, "failed to get pod %v", podName) 121 gomega.Expect(pod.Spec.Containers).NotTo(gomega.BeEmpty()) 122 return ExecCommandInContainer(f, podName, pod.Spec.Containers[0].Name, cmd...) 123 } 124 125 func execCommandInPodWithFullOutput(ctx context.Context, f *framework.Framework, podName string, cmd ...string) (string, string, error) { 126 pod, err := NewPodClient(f).Get(ctx, podName, metav1.GetOptions{}) 127 framework.ExpectNoError(err, "failed to get pod %v", podName) 128 gomega.Expect(pod.Spec.Containers).NotTo(gomega.BeEmpty()) 129 return ExecCommandInContainerWithFullOutput(f, podName, pod.Spec.Containers[0].Name, cmd...) 130 } 131 132 // ExecShellInPod executes the specified command on the pod. 133 func ExecShellInPod(ctx context.Context, f *framework.Framework, podName string, cmd string) string { 134 return execCommandInPod(ctx, f, podName, "/bin/sh", "-c", cmd) 135 } 136 137 // ExecShellInPodWithFullOutput executes the specified command on the Pod and returns stdout, stderr and error. 138 func ExecShellInPodWithFullOutput(ctx context.Context, f *framework.Framework, podName string, cmd string) (string, string, error) { 139 return execCommandInPodWithFullOutput(ctx, f, podName, "/bin/sh", "-c", cmd) 140 } 141 142 func execute(method string, url *url.URL, config *restclient.Config, stdin io.Reader, stdout, stderr io.Writer, tty bool) error { 143 exec, err := remotecommand.NewSPDYExecutor(config, method, url) 144 if err != nil { 145 return err 146 } 147 return exec.StreamWithContext(context.Background(), remotecommand.StreamOptions{ 148 Stdin: stdin, 149 Stdout: stdout, 150 Stderr: stderr, 151 Tty: tty, 152 }) 153 }