...

Source file src/github.com/chaos-mesh/chaos-mesh/pkg/ctrlserver/graph/exec.go

Documentation: github.com/chaos-mesh/chaos-mesh/pkg/ctrlserver/graph

     1  // Copyright 2021 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  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  //
    15  
    16  package graph
    17  
    18  import (
    19  	"bytes"
    20  	"context"
    21  	"fmt"
    22  	"io/ioutil"
    23  	"strings"
    24  
    25  	"github.com/pkg/errors"
    26  	"google.golang.org/grpc/grpclog"
    27  	v1 "k8s.io/api/core/v1"
    28  	"k8s.io/client-go/kubernetes"
    29  	"k8s.io/client-go/tools/remotecommand"
    30  	kubectlscheme "k8s.io/kubectl/pkg/scheme"
    31  	"sigs.k8s.io/controller-runtime/pkg/client/config"
    32  
    33  	"github.com/chaos-mesh/chaos-mesh/pkg/bpm"
    34  )
    35  
    36  // exec executes certain command and returns the result
    37  // Only commands in chaos-mesh components should use this way
    38  // for target pod, use ExecBypass
    39  func exec(ctx context.Context, pod *v1.Pod, cmd string, c *kubernetes.Clientset) (string, error) {
    40  	name := pod.GetObjectMeta().GetName()
    41  	namespace := pod.GetObjectMeta().GetNamespace()
    42  	// TODO: if `containerNames` is set and specific container is injected chaos,
    43  	// need to use THE name rather than the first one.
    44  	// till 20/11/10 only podchaos and kernelchaos support `containerNames`, so not set it for now
    45  	containerName := pod.Spec.Containers[0].Name
    46  
    47  	req := c.CoreV1().RESTClient().Post().
    48  		Resource("pods").
    49  		Name(name).
    50  		Namespace(namespace).
    51  		SubResource("exec")
    52  
    53  	req.VersionedParams(&v1.PodExecOptions{
    54  		Container: containerName,
    55  		Command:   []string{"/bin/sh", "-c", cmd},
    56  		Stdin:     false,
    57  		Stdout:    true,
    58  		Stderr:    true,
    59  		TTY:       false,
    60  	}, kubectlscheme.ParameterCodec)
    61  
    62  	var stdout, stderr bytes.Buffer
    63  	exec, err := remotecommand.NewSPDYExecutor(config.GetConfigOrDie(), "POST", req.URL())
    64  	if err != nil {
    65  		return "", errors.Wrapf(err, "error in creating NewSPDYExecutor for pod %s/%s", pod.GetNamespace(), pod.GetName())
    66  	}
    67  	err = exec.Stream(remotecommand.StreamOptions{
    68  		Stdin:  nil,
    69  		Stdout: &stdout,
    70  		Stderr: &stderr,
    71  	})
    72  	if err != nil {
    73  		if stderr.String() != "" {
    74  			return "", fmt.Errorf("error: %s\npod: %s\ncommand: %s", strings.TrimSuffix(stderr.String(), "\n"), pod.Name, cmd)
    75  		}
    76  		return "", errors.Wrapf(err, "error in streaming remotecommand: pod: %s/%s, command: %s", pod.GetNamespace(), pod.Name, cmd)
    77  	}
    78  	if stderr.String() != "" {
    79  		return "", fmt.Errorf("error of command %s: %s", cmd, stderr.String())
    80  	}
    81  	return stdout.String(), nil
    82  }
    83  
    84  // ExecBypass use chaos-daemon to enter namespace and execute command in target pod
    85  func (r *Resolver) ExecBypass(ctx context.Context, pod *v1.Pod, cmd string) (string, error) {
    86  	// To disable printing irrelevant log from grpc/clientconn.go
    87  	// See grpc/grpc-go#3918 for detail. Could be resolved in the future
    88  	grpclog.SetLoggerV2(grpclog.NewLoggerV2(ioutil.Discard, ioutil.Discard, ioutil.Discard))
    89  	pid, err := r.GetPidFromPod(ctx, pod)
    90  	if err != nil {
    91  		return "", err
    92  	}
    93  
    94  	podResolver := &podResolver{Resolver: r}
    95  	daemon, err := podResolver.Daemon(ctx, pod)
    96  	if err != nil {
    97  		return "", err
    98  	}
    99  
   100  	// enter all possible namespaces needed, since there's no bad effect to do so
   101  	cmdBuilder := bpm.DefaultProcessBuilder(cmd).
   102  		SetNS(pid, bpm.MountNS).
   103  		SetNS(pid, bpm.PidNS).
   104  		SetNS(pid, bpm.NetNS).
   105  		EnableLocalMnt().
   106  		SetContext(ctx)
   107  
   108  	return exec(ctx, daemon, cmdBuilder.Build(ctx).Cmd.String(), r.Clientset)
   109  }
   110