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 }