...

Source file src/github.com/chaos-mesh/chaos-mesh/pkg/ctrl/server/exec.go

Documentation: github.com/chaos-mesh/chaos-mesh/pkg/ctrl/server

     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 server
    17  
    18  import (
    19  	"bytes"
    20  	"context"
    21  	"io"
    22  	"strings"
    23  
    24  	"github.com/pkg/errors"
    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.StreamWithContext(ctx, remotecommand.StreamOptions{
    67  		Stdin:  nil,
    68  		Stdout: &stdout,
    69  		Stderr: &stderr,
    70  	})
    71  	if err != nil {
    72  		if stderr.String() != "" {
    73  			return "", errors.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 "", errors.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 (r *Resolver) ExecBypass(ctx context.Context, pod *v1.Pod, cmd string, nsTypes ...bpm.NsType) (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(io.Discard, io.Discard, io.Discard))
    88  	pid, err := r.GetPidFromPod(ctx, pod)
    89  	if err != nil {
    90  		return "", err
    91  	}
    92  
    93  	podResolver := &podResolver{Resolver: r}
    94  	daemon, err := podResolver.Daemon(ctx, pod)
    95  	if err != nil {
    96  		return "", err
    97  	}
    98  
    99  	cmdBuilder := bpm.DefaultProcessBuilder(cmd)
   100  	for _, nsType := range nsTypes {
   101  		cmdBuilder = cmdBuilder.SetNS(pid, nsType)
   102  	}
   103  
   104  	return exec(
   105  		ctx, daemon,
   106  		cmdBuilder.EnableLocalMnt().SetContext(ctx).Build(ctx).Cmd.String(),
   107  		r.Clientset,
   108  	)
   109  }
   110