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