...

Source file src/github.com/chaos-mesh/chaos-mesh/pkg/chaosctl/common/exec.go

Documentation: github.com/chaos-mesh/chaos-mesh/pkg/chaosctl/common

     1  // Copyright 2019 Chaos Mesh Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package common
    15  
    16  import (
    17  	"bytes"
    18  	"context"
    19  	"fmt"
    20  	"io/ioutil"
    21  	"strings"
    22  
    23  	"github.com/pkg/errors"
    24  
    25  	"google.golang.org/grpc/grpclog"
    26  	v1 "k8s.io/api/core/v1"
    27  	"k8s.io/client-go/kubernetes"
    28  	"k8s.io/client-go/tools/remotecommand"
    29  	kubectlscheme "k8s.io/kubectl/pkg/scheme"
    30  	"sigs.k8s.io/controller-runtime/pkg/client/config"
    31  
    32  	"github.com/chaos-mesh/chaos-mesh/pkg/bpm"
    33  )
    34  
    35  // Exec executes certain command and returns the result
    36  // Only commands in chaos-mesh components should use this way
    37  // for target pod, use ExecBypass
    38  func Exec(ctx context.Context, pod v1.Pod, cmd string, c *kubernetes.Clientset) (string, error) {
    39  	name := pod.GetObjectMeta().GetName()
    40  	namespace := pod.GetObjectMeta().GetNamespace()
    41  	// TODO: if `containerNames` is set and specific container is injected chaos,
    42  	// need to use THE name rather than the first one.
    43  	// till 20/11/10 only podchaos and kernelchaos support `containerNames`, so not set it for now
    44  	containerName := pod.Spec.Containers[0].Name
    45  
    46  	req := c.CoreV1().RESTClient().Post().
    47  		Resource("pods").
    48  		Name(name).
    49  		Namespace(namespace).
    50  		SubResource("exec")
    51  
    52  	req.VersionedParams(&v1.PodExecOptions{
    53  		Container: containerName,
    54  		Command:   []string{"/bin/sh", "-c", cmd},
    55  		Stdin:     false,
    56  		Stdout:    true,
    57  		Stderr:    true,
    58  		TTY:       false,
    59  	}, kubectlscheme.ParameterCodec)
    60  
    61  	var stdout, stderr bytes.Buffer
    62  	exec, err := remotecommand.NewSPDYExecutor(config.GetConfigOrDie(), "POST", req.URL())
    63  	if err != nil {
    64  		return "", errors.Wrapf(err, "error in creating NewSPDYExecutor for pod %s/%s", pod.GetNamespace(), pod.GetName())
    65  	}
    66  	err = exec.Stream(remotecommand.StreamOptions{
    67  		Stdin:  nil,
    68  		Stdout: &stdout,
    69  		Stderr: &stderr,
    70  	})
    71  	if err != nil {
    72  		if stderr.String() != "" {
    73  			return "", fmt.Errorf("error: %s\npod: %s\ncommand: %s", strings.TrimSuffix(stderr.String(), "\n"), pod.Name, cmd)
    74  		}
    75  		return "", errors.Wrapf(err, "error in streaming remotecommand: pod: %s/%s, command: %s", pod.GetNamespace(), pod.Name, cmd)
    76  	}
    77  	if stderr.String() != "" {
    78  		return "", fmt.Errorf("error of command %s: %s", cmd, stderr.String())
    79  	}
    80  	return stdout.String(), nil
    81  }
    82  
    83  // ExecBypass use chaos-daemon to enter namespace and execute command in target pod
    84  func ExecBypass(ctx context.Context, pod v1.Pod, daemon v1.Pod, cmd string, c *kubernetes.Clientset) (string, error) {
    85  	// To disable printing irrelevant log from grpc/clientconn.go
    86  	// See grpc/grpc-go#3918 for detail. Could be resolved in the future
    87  	grpclog.SetLoggerV2(grpclog.NewLoggerV2(ioutil.Discard, ioutil.Discard, ioutil.Discard))
    88  	pid, err := GetPidFromPod(ctx, pod, daemon)
    89  	if err != nil {
    90  		return "", err
    91  	}
    92  	// enter all possible namespaces needed, since there's no bad effect to do so
    93  	cmdBuilder := bpm.DefaultProcessBuilder(cmd).SetNS(pid, bpm.MountNS).SetNS(pid, bpm.PidNS).SetContext(ctx)
    94  	return Exec(ctx, daemon, cmdBuilder.Build().Cmd.String(), c)
    95  }
    96