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  }