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 }