github.com/sealerio/sealer@v0.11.1-0.20240507115618-f4f89c5853ae/test/testhelper/client/k8s/client.go (about) 1 // Copyright © 2023 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 k8s 16 17 import ( 18 "bytes" 19 "context" 20 "fmt" 21 "io" 22 "net" 23 "path/filepath" 24 "regexp" 25 26 "github.com/sealerio/sealer/test/testhelper" 27 28 "github.com/pkg/errors" 29 v1 "k8s.io/api/core/v1" 30 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 31 "k8s.io/client-go/kubernetes" 32 "k8s.io/client-go/tools/clientcmd" 33 ) 34 35 const ( 36 ReadyStatus = "Ready" 37 TRUE = "True" 38 FALSE = "False" 39 ) 40 41 type Client struct { 42 client *kubernetes.Clientset 43 } 44 45 type NamespacePod struct { 46 Namespace v1.Namespace 47 PodList *v1.PodList 48 } 49 50 type EventPod struct { 51 Reason string 52 Message string 53 Count int32 54 Type string 55 Action string 56 Namespace string 57 } 58 59 func NewK8sClient(sshClient *testhelper.SSHClient) (*Client, error) { 60 kubeconfigPath := filepath.Join("/root", ".kube", "config") 61 62 data := testhelper.GetRemoteFileData(sshClient, kubeconfigPath) 63 reg := regexp.MustCompile(`server: https://(.*):6443`) 64 data = reg.ReplaceAll(data, []byte(fmt.Sprintf("server: https://%s:6443", sshClient.RemoteHostIP.String()))) 65 config, err := clientcmd.RESTConfigFromKubeConfig(data) 66 67 if err != nil { 68 return nil, errors.Wrap(err, "failed to build kube config") 69 } 70 71 clientSet, err := kubernetes.NewForConfig(config) 72 if err != nil { 73 return nil, err 74 } 75 76 return &Client{ 77 client: clientSet, 78 }, nil 79 } 80 81 func (c *Client) ListNodes() (*v1.NodeList, error) { 82 nodes, err := c.client.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{}) 83 if err != nil { 84 return nil, errors.Wrapf(err, "failed to get cluster nodes") 85 } 86 return nodes, nil 87 } 88 89 func (c *Client) listNamespaces() (*v1.NamespaceList, error) { 90 namespaceList, err := c.client.CoreV1().Namespaces().List(context.TODO(), metav1.ListOptions{}) 91 if err != nil { 92 return nil, errors.Wrapf(err, "failed to get namespaces") 93 } 94 return namespaceList, nil 95 } 96 97 func (c *Client) ListNodesByLabel(label string) (*v1.NodeList, error) { 98 nodes, err := c.client.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{LabelSelector: label}) 99 if err != nil { 100 return nil, errors.Wrapf(err, "failed to get cluster nodes") 101 } 102 return nodes, nil 103 } 104 105 func (c *Client) ListNodeIPByLabel(label string) ([]net.IP, error) { 106 var ips []net.IP 107 nodes, err := c.ListNodesByLabel(label) 108 if err != nil { 109 return nil, err 110 } 111 for _, node := range nodes.Items { 112 for _, v := range node.Status.Addresses { 113 if v.Type == v1.NodeInternalIP { 114 ips = append(ips, net.ParseIP(v.Address)) 115 } 116 } 117 } 118 return ips, nil 119 } 120 121 func (c *Client) ListAllNamespacesPods() ([]*NamespacePod, error) { 122 namespaceList, err := c.listNamespaces() 123 if err != nil { 124 return nil, err 125 } 126 var namespacePodList []*NamespacePod 127 for _, ns := range namespaceList.Items { 128 pods, err := c.client.CoreV1().Pods(ns.Name).List(context.TODO(), metav1.ListOptions{}) 129 if err != nil { 130 return nil, errors.Wrapf(err, "failed to get all namespace pods") 131 } 132 namespacePod := NamespacePod{ 133 Namespace: ns, 134 PodList: pods, 135 } 136 namespacePodList = append(namespacePodList, &namespacePod) 137 } 138 139 return namespacePodList, nil 140 } 141 142 // Check if all pods of kube-system are ready 143 func (c *Client) CheckAllKubeSystemPodsReady() (bool, error) { 144 pods, err := c.client.CoreV1().Pods("kube-system").List(context.TODO(), metav1.ListOptions{}) 145 if err != nil { 146 return false, errors.Wrapf(err, "failed to get kube-system namespace pods") 147 } 148 // pods.Items maybe nil 149 if len(pods.Items) == 0 { 150 return false, nil 151 } 152 for _, pod := range pods.Items { 153 // pod.Status.ContainerStatus == nil because of pod contain initcontainer 154 if len(pod.Status.ContainerStatuses) == 0 { 155 continue 156 } 157 if !pod.Status.ContainerStatuses[0].Ready { 158 return false, nil 159 } 160 } 161 return true, nil 162 } 163 164 func (c *Client) GetPodLog(namespace, podName string) (string, error) { 165 req := c.client.CoreV1().Pods(namespace).GetLogs(podName, &v1.PodLogOptions{}) 166 podLogs, err := req.Stream(context.TODO()) 167 if err != nil { 168 return "", err 169 } 170 171 defer func() { 172 _ = podLogs.Close() 173 }() 174 175 buf := new(bytes.Buffer) 176 _, err = io.Copy(buf, podLogs) 177 if err != nil { 178 return "", err 179 } 180 return buf.String(), nil 181 } 182 183 func (c *Client) GetPodEvents(namespace, podName string) ([]v1.Event, error) { 184 events, err := c.client.CoreV1(). 185 Events(namespace). 186 List(context.TODO(), metav1.ListOptions{FieldSelector: "involvedObject.name=" + podName, TypeMeta: metav1.TypeMeta{Kind: "Pod"}}) 187 if err != nil { 188 return nil, err 189 } 190 return events.Items, nil 191 } 192 193 func (c *Client) getPodReadyStatus(pod v1.Pod) bool { 194 for _, condition := range pod.Status.Conditions { 195 if condition.Type != ReadyStatus { 196 continue 197 } 198 if condition.Status == TRUE { 199 return true 200 } 201 } 202 return false 203 } 204 205 func (c *Client) GetNotReadyPodEvent() (map[string][]EventPod, error) { 206 namespacePodList, err := c.ListAllNamespacesPods() 207 if err != nil { 208 return nil, err 209 } 210 result := make(map[string][]EventPod) 211 for _, podNamespace := range namespacePodList { 212 for _, pod := range podNamespace.PodList.Items { 213 if c.getPodReadyStatus(pod) { 214 continue 215 } 216 events, err := c.GetPodEvents(podNamespace.Namespace.Name, pod.Name) 217 if err != nil { 218 return nil, err 219 } 220 var eventpods []EventPod 221 for _, event := range events { 222 eventpods = append(eventpods, EventPod{ 223 Reason: event.Reason, 224 Message: event.Message, 225 Count: event.Count, 226 Type: event.Type, 227 Action: event.Action, 228 Namespace: event.Namespace, 229 }) 230 } 231 result[pod.Name] = append(result[pod.Name], eventpods...) 232 } 233 } 234 return result, nil 235 } 236 237 // If exist pod not ready, show pod events and logs 238 func (c *Client) OutputNotReadyPodInfo() error { 239 podEvents, err := c.GetNotReadyPodEvent() 240 if err != nil { 241 return err 242 } 243 for podName, events := range podEvents { 244 fmt.Println("=========================================================================================================================================") 245 fmt.Println("PodName: " + podName) 246 fmt.Println("******************************************************Events*****************************************************************************") 247 var namespace string 248 for _, event := range events { 249 namespace = event.Namespace 250 fmt.Printf("Reason: %s\n", event.Reason) 251 fmt.Printf("Message: %s\n", event.Message) 252 fmt.Printf("Count: %v\n", event.Count) 253 fmt.Printf("Type: %s\n", event.Type) 254 fmt.Printf("Action: %s\n", event.Action) 255 fmt.Println("------------------------------------------------------------------------------------------------------------------------------------") 256 } 257 log, err := c.GetPodLog(namespace, podName) 258 if err != nil { 259 return err 260 } 261 fmt.Println("********************************************************Log*****************************************************************************") 262 fmt.Println(log) 263 fmt.Println("=========================================================================================================================================") 264 } 265 return nil 266 } 267 268 // Check if all nodes are ready 269 func (c *Client) CheckAllNodeReady() (bool, error) { 270 nodes, err := c.ListNodes() 271 if err != nil { 272 return false, err 273 } 274 for _, node := range nodes.Items { 275 for _, condition := range node.Status.Conditions { 276 if condition.Type != ReadyStatus { 277 continue 278 } 279 if condition.Status == FALSE { 280 fmt.Println("********************************************************Node*****************************************************************************") 281 fmt.Println(condition.Reason) 282 fmt.Println(condition.Message) 283 fmt.Println("=========================================================================================================================================") 284 return false, nil 285 } 286 } 287 } 288 return true, nil 289 }