k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/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 return ExecWithOptionsContext(context.Background(), f, options) 55 } 56 57 func ExecWithOptionsContext(ctx context.Context, f *framework.Framework, options ExecOptions) (string, string, error) { 58 if !options.Quiet { 59 framework.Logf("ExecWithOptions %+v", options) 60 } 61 config, err := framework.LoadConfig() 62 framework.ExpectNoError(err, "failed to load restclient config") 63 64 const tty = false 65 66 framework.Logf("ExecWithOptions: Clientset creation") 67 req := f.ClientSet.CoreV1().RESTClient().Post(). 68 Resource("pods"). 69 Name(options.PodName). 70 Namespace(options.Namespace). 71 SubResource("exec"). 72 Param("container", options.ContainerName) 73 req.VersionedParams(&v1.PodExecOptions{ 74 Container: options.ContainerName, 75 Command: options.Command, 76 Stdin: options.Stdin != nil, 77 Stdout: options.CaptureStdout, 78 Stderr: options.CaptureStderr, 79 TTY: tty, 80 }, scheme.ParameterCodec) 81 82 var stdout, stderr bytes.Buffer 83 framework.Logf("ExecWithOptions: execute(POST %s)", req.URL()) 84 err = execute(ctx, "POST", req.URL(), config, options.Stdin, &stdout, &stderr, tty) 85 86 if options.PreserveWhitespace { 87 return stdout.String(), stderr.String(), err 88 } 89 return strings.TrimSpace(stdout.String()), strings.TrimSpace(stderr.String()), err 90 } 91 92 // ExecCommandInContainerWithFullOutput executes a command in the 93 // specified container and return stdout, stderr and error 94 func ExecCommandInContainerWithFullOutput(f *framework.Framework, podName, containerName string, cmd ...string) (string, string, error) { 95 // TODO (pohly): add context support 96 return ExecWithOptions(f, ExecOptions{ 97 Command: cmd, 98 Namespace: f.Namespace.Name, 99 PodName: podName, 100 ContainerName: containerName, 101 Stdin: nil, 102 CaptureStdout: true, 103 CaptureStderr: true, 104 PreserveWhitespace: false, 105 }) 106 } 107 108 // ExecCommandInContainer executes a command in the specified container. 109 func ExecCommandInContainer(f *framework.Framework, podName, containerName string, cmd ...string) string { 110 stdout, stderr, err := ExecCommandInContainerWithFullOutput(f, podName, containerName, cmd...) 111 framework.Logf("Exec stderr: %q", stderr) 112 framework.ExpectNoError(err, 113 "failed to execute command in pod %v, container %v: %v", 114 podName, containerName, err) 115 return stdout 116 } 117 118 // ExecShellInContainer executes the specified command on the pod's container. 119 func ExecShellInContainer(f *framework.Framework, podName, containerName string, cmd string) string { 120 return ExecCommandInContainer(f, podName, containerName, "/bin/sh", "-c", cmd) 121 } 122 123 func execCommandInPod(ctx context.Context, f *framework.Framework, podName string, cmd ...string) string { 124 pod, err := NewPodClient(f).Get(ctx, podName, metav1.GetOptions{}) 125 framework.ExpectNoError(err, "failed to get pod %v", podName) 126 gomega.Expect(pod.Spec.Containers).NotTo(gomega.BeEmpty()) 127 return ExecCommandInContainer(f, podName, pod.Spec.Containers[0].Name, cmd...) 128 } 129 130 func execCommandInPodWithFullOutput(ctx context.Context, f *framework.Framework, podName string, cmd ...string) (string, string, error) { 131 pod, err := NewPodClient(f).Get(ctx, podName, metav1.GetOptions{}) 132 framework.ExpectNoError(err, "failed to get pod %v", podName) 133 gomega.Expect(pod.Spec.Containers).NotTo(gomega.BeEmpty()) 134 return ExecCommandInContainerWithFullOutput(f, podName, pod.Spec.Containers[0].Name, cmd...) 135 } 136 137 // ExecShellInPod executes the specified command on the pod. 138 func ExecShellInPod(ctx context.Context, f *framework.Framework, podName string, cmd string) string { 139 return execCommandInPod(ctx, f, podName, "/bin/sh", "-c", cmd) 140 } 141 142 // ExecShellInPodWithFullOutput executes the specified command on the Pod and returns stdout, stderr and error. 143 func ExecShellInPodWithFullOutput(ctx context.Context, f *framework.Framework, podName string, cmd string) (string, string, error) { 144 return execCommandInPodWithFullOutput(ctx, f, podName, "/bin/sh", "-c", cmd) 145 } 146 147 func execute(ctx context.Context, method string, url *url.URL, config *restclient.Config, stdin io.Reader, stdout, stderr io.Writer, tty bool) error { 148 exec, err := remotecommand.NewSPDYExecutor(config, method, url) 149 if err != nil { 150 return err 151 } 152 return exec.StreamWithContext(ctx, remotecommand.StreamOptions{ 153 Stdin: stdin, 154 Stdout: stdout, 155 Stderr: stderr, 156 Tty: tty, 157 }) 158 }