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  }