github.com/alibaba/sealer@v0.8.6-0.20220430115802-37a2bdaa8173/pkg/debug/clean.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  	"bytes"
    19  	"context"
    20  	"fmt"
    21  	"strings"
    22  
    23  	"github.com/pkg/errors"
    24  	"github.com/spf13/cobra"
    25  	corev1 "k8s.io/api/core/v1"
    26  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    27  	"k8s.io/cli-runtime/pkg/genericclioptions"
    28  	"k8s.io/client-go/kubernetes"
    29  	"k8s.io/client-go/kubernetes/scheme"
    30  	corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
    31  	restclient "k8s.io/client-go/rest"
    32  	"k8s.io/client-go/tools/clientcmd"
    33  
    34  	"github.com/alibaba/sealer/common"
    35  )
    36  
    37  // CleanOptions holds the the options for an invocation of debug clean.
    38  type CleanOptions struct {
    39  	PodName       string
    40  	Namespace     string
    41  	ContainerName string
    42  }
    43  
    44  // Cleaner cleans the debug containers and pods.
    45  type Cleaner struct {
    46  	*CleanOptions
    47  
    48  	AdminKubeConfigPath string
    49  
    50  	stdin bool
    51  	tty   bool
    52  
    53  	genericclioptions.IOStreams
    54  }
    55  
    56  func NewDebugCleanOptions() *CleanOptions {
    57  	return &CleanOptions{}
    58  }
    59  
    60  func NewDebugCleaner() *Cleaner {
    61  	return &Cleaner{
    62  		CleanOptions: NewDebugCleanOptions(),
    63  	}
    64  }
    65  
    66  var CleanCMD = &cobra.Command{
    67  	Use:   "clean",
    68  	Short: "Clean the debug container od pod",
    69  	Args:  cobra.ExactArgs(1),
    70  	RunE: func(cmd *cobra.Command, args []string) error {
    71  		cleaner := NewDebugCleaner()
    72  		cleaner.AdminKubeConfigPath = common.KubeAdminConf
    73  
    74  		if err := cleaner.CompleteAndVerifyOptions(args); err != nil {
    75  			return err
    76  		}
    77  		if err := cleaner.Run(); err != nil {
    78  			return err
    79  		}
    80  
    81  		return nil
    82  	},
    83  }
    84  
    85  // CompleteAndVerifyOptions completes and verifies DebugCleanOptions.
    86  func (cleaner *Cleaner) CompleteAndVerifyOptions(args []string) error {
    87  	ss := strings.Split(args[0], FSDebugID)
    88  	if len(ss) < 3 {
    89  		return fmt.Errorf("invaild debug ID")
    90  	}
    91  
    92  	cleaner.Namespace = ss[2]
    93  	cleaner.PodName = ss[1]
    94  	cleaner.ContainerName = ss[0]
    95  
    96  	return nil
    97  }
    98  
    99  // Run removes debug pods or exits debug containers.
   100  func (cleaner *Cleaner) Run() error {
   101  	ctx := context.Background()
   102  
   103  	// get the rest config
   104  	restConfig, err := clientcmd.BuildConfigFromFlags("", cleaner.AdminKubeConfigPath)
   105  	if err != nil {
   106  		return errors.Wrapf(err, "failed to get rest config from file %s", cleaner.AdminKubeConfigPath)
   107  	}
   108  	if err := SetKubernetesDefaults(restConfig); err != nil {
   109  		return err
   110  	}
   111  
   112  	// get the kube client set
   113  	kubeClientSet, err := kubernetes.NewForConfig(restConfig)
   114  	if err != nil {
   115  		return errors.Wrapf(err, "failed to create kubernetes client from file %s", cleaner.AdminKubeConfigPath)
   116  	}
   117  
   118  	if strings.HasPrefix(cleaner.PodName, NodeDebugPrefix) {
   119  		return cleaner.RemovePod(ctx, kubeClientSet.CoreV1())
   120  	}
   121  
   122  	return cleaner.ExitEphemeralContainer(restConfig)
   123  }
   124  
   125  // RemovePod removes the debug pods.
   126  func (cleaner *Cleaner) RemovePod(ctx context.Context, kubeClientCorev1 corev1client.CoreV1Interface) error {
   127  	if kubeClientCorev1 == nil {
   128  		return fmt.Errorf("clean must need a kubernetes client")
   129  	}
   130  
   131  	return kubeClientCorev1.Pods(cleaner.Namespace).Delete(ctx, cleaner.PodName, metav1.DeleteOptions{})
   132  }
   133  
   134  // ExitEphemeralContainer exits the ephemeral containers
   135  // and the ephemeral container's status will become terminated.
   136  func (cleaner *Cleaner) ExitEphemeralContainer(config *restclient.Config) error {
   137  	restClient, err := restclient.RESTClientFor(config)
   138  	if err != nil {
   139  		return err
   140  	}
   141  
   142  	cleaner.initExitEpheContainerOpts()
   143  
   144  	req := restClient.Post().
   145  		Resource("pods").
   146  		Name(cleaner.PodName).
   147  		Namespace(cleaner.Namespace).
   148  		SubResource("attach")
   149  	req.VersionedParams(&corev1.PodAttachOptions{
   150  		Container: cleaner.ContainerName,
   151  		Stdin:     cleaner.stdin,
   152  		Stdout:    cleaner.Out != nil,
   153  		Stderr:    cleaner.ErrOut != nil,
   154  		TTY:       cleaner.tty,
   155  	}, scheme.ParameterCodec)
   156  
   157  	connect := &Connector{
   158  		Config:    config,
   159  		IOStreams: cleaner.IOStreams,
   160  		TTY:       cleaner.tty,
   161  	}
   162  
   163  	if err := connect.DoConnect("POST", req.URL(), nil); err != nil {
   164  		return err
   165  	}
   166  
   167  	return nil
   168  }
   169  
   170  func (cleaner *Cleaner) initExitEpheContainerOpts() {
   171  	var stdout, stderr bytes.Buffer
   172  	stdin := bytes.NewBuffer([]byte("exit\n"))
   173  	cleaner.In = stdin
   174  	cleaner.Out = &stdout
   175  	cleaner.ErrOut = &stderr
   176  	cleaner.stdin = true
   177  	cleaner.tty = true
   178  }