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 }