github.com/alibaba/sealer@v0.8.6-0.20220430115802-37a2bdaa8173/pkg/debug/pod.go (about)

     1  // Copyright © 2021 Alibaba Group Holding Ltd.
     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  package debug
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  
    21  	"github.com/pkg/errors"
    22  	"github.com/spf13/cobra"
    23  	corev1 "k8s.io/api/core/v1"
    24  	apierrors "k8s.io/apimachinery/pkg/api/errors"
    25  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    26  	utilrand "k8s.io/apimachinery/pkg/util/rand"
    27  
    28  	"github.com/alibaba/sealer/common"
    29  )
    30  
    31  func NewDebugPodCommand(options *DebuggerOptions) *cobra.Command {
    32  	cmd := &cobra.Command{
    33  		Use:   "pod",
    34  		Short: "Debug pod or container",
    35  		Args:  cobra.MinimumNArgs(1),
    36  		RunE: func(cmd *cobra.Command, args []string) error {
    37  			debugger := NewDebugger(options)
    38  			debugger.AdminKubeConfigPath = common.KubeAdminConf
    39  			debugger.Type = TypeDebugPod
    40  			debugger.Motd = SealerDebugMotd
    41  
    42  			imager := NewDebugImagesManager()
    43  
    44  			if err := debugger.CompleteAndVerifyOptions(cmd, args, imager); err != nil {
    45  				return err
    46  			}
    47  			str, err := debugger.Run()
    48  			if err != nil {
    49  				return err
    50  			}
    51  			if len(str) != 0 {
    52  				fmt.Println("The debug ID:", str)
    53  			}
    54  
    55  			return nil
    56  		},
    57  	}
    58  
    59  	cmd.Flags().StringVarP(&options.TargetContainer, "container", "c", "", "The container to be debugged.")
    60  
    61  	return cmd
    62  }
    63  
    64  func (debugger *Debugger) DebugPod(ctx context.Context) (*corev1.Pod, error) {
    65  	// get the target pod object
    66  	targetPod, err := debugger.kubeClientCorev1.Pods(debugger.Namespace).Get(ctx, debugger.TargetName, metav1.GetOptions{})
    67  	if err != nil {
    68  		return nil, errors.Wrapf(err, "failed to get the target pod %s", debugger.TargetName)
    69  	}
    70  
    71  	if err := debugger.addPodInfoIntoEnv(targetPod); err != nil {
    72  		return nil, err
    73  	}
    74  	if err := debugger.addClusterInfoIntoEnv(ctx); err != nil {
    75  		return nil, err
    76  	}
    77  
    78  	// add an ephemeral container into target pod and used as a debug container
    79  	debugPod, err := debugger.debugPodByEphemeralContainer(ctx, targetPod)
    80  	if err != nil {
    81  		return nil, errors.Wrapf(err, "failed to add an ephemeral container into pod: %s", targetPod.Name)
    82  	}
    83  
    84  	return debugPod, nil
    85  }
    86  
    87  // debugPodByEphemeralContainer runs an ephemeral container in target pod and use as a debug container.
    88  func (debugger *Debugger) debugPodByEphemeralContainer(ctx context.Context, pod *corev1.Pod) (*corev1.Pod, error) {
    89  	// get ephemeral containers
    90  	pods := debugger.kubeClientCorev1.Pods(pod.Namespace)
    91  	ec, err := pods.GetEphemeralContainers(ctx, pod.Name, metav1.GetOptions{})
    92  	if err != nil {
    93  		if serr, ok := err.(*apierrors.StatusError); ok && serr.Status().Reason == metav1.StatusReasonNotFound && serr.ErrStatus.Details.Name == "" {
    94  			return nil, errors.Wrapf(err, "ephemeral container are disabled for this cluster")
    95  		}
    96  		return nil, err
    97  	}
    98  
    99  	// generate an ephemeral container
   100  	debugContainer := debugger.generateDebugContainer(pod)
   101  
   102  	// add the ephemeral container and update the pod
   103  	ec.EphemeralContainers = append(ec.EphemeralContainers, *debugContainer)
   104  	_, err = pods.UpdateEphemeralContainers(ctx, pod.Name, ec, metav1.UpdateOptions{})
   105  	if err != nil {
   106  		return nil, errors.Wrapf(err, "error updating ephermeral containers")
   107  	}
   108  
   109  	return pod, nil
   110  }
   111  
   112  // generateDebugContainer returns an ephemeral container suitable for use as a debug container in the given pod.
   113  func (debugger *Debugger) generateDebugContainer(pod *corev1.Pod) *corev1.EphemeralContainer {
   114  	debugContainerName := debugger.getDebugContainerName(pod)
   115  	debugger.DebugContainerName = debugContainerName
   116  
   117  	if len(debugger.TargetContainer) == 0 {
   118  		debugger.TargetContainer = pod.Spec.Containers[0].Name
   119  	}
   120  
   121  	ec := &corev1.EphemeralContainer{
   122  		EphemeralContainerCommon: corev1.EphemeralContainerCommon{
   123  			Name:                     debugContainerName,
   124  			Env:                      debugger.Env,
   125  			Image:                    debugger.Image,
   126  			ImagePullPolicy:          corev1.PullPolicy(debugger.PullPolicy),
   127  			Stdin:                    true,
   128  			TerminationMessagePolicy: corev1.TerminationMessageReadFile,
   129  			TTY:                      true,
   130  		},
   131  		TargetContainerName: debugger.TargetContainer,
   132  	}
   133  
   134  	return ec
   135  }
   136  
   137  // getDebugContainerName generates and returns the debug container name.
   138  func (debugger *Debugger) getDebugContainerName(pod *corev1.Pod) string {
   139  	if len(debugger.DebugContainerName) > 0 {
   140  		return debugger.DebugContainerName
   141  	}
   142  
   143  	name := debugger.DebugContainerName
   144  	containerByName := ContainerNameToRef(pod)
   145  	for len(name) == 0 || containerByName[name] != nil {
   146  		name = fmt.Sprintf("%s-%s", PodDebugPrefix, utilrand.String(5))
   147  	}
   148  
   149  	return name
   150  }
   151  
   152  // addPodInfoIntoEnv adds pod info into env
   153  func (debugger *Debugger) addPodInfoIntoEnv(pod *corev1.Pod) error {
   154  	if pod == nil {
   155  		return fmt.Errorf("pod must not nil")
   156  	}
   157  
   158  	debugger.Env = append(debugger.Env,
   159  		corev1.EnvVar{
   160  			Name:  "POD_NAME",
   161  			Value: pod.Name,
   162  		},
   163  		corev1.EnvVar{
   164  			Name:  "POD_IP",
   165  			Value: pod.Status.PodIP,
   166  		},
   167  	)
   168  
   169  	return nil
   170  }