github.com/jenkins-x/jx/v2@v2.1.155/pkg/kube/pod.go (about)

     1  package kube
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"os"
     7  	"sort"
     8  	"strings"
     9  	"time"
    10  
    11  	"github.com/jenkins-x/jx/v2/pkg/kube/naming"
    12  	v1 "k8s.io/api/core/v1"
    13  	meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    14  	"k8s.io/apimachinery/pkg/fields"
    15  	"k8s.io/apimachinery/pkg/util/wait"
    16  	"k8s.io/apimachinery/pkg/watch"
    17  	"k8s.io/client-go/kubernetes"
    18  	tools_watch "k8s.io/client-go/tools/watch"
    19  )
    20  
    21  // credit https://github.com/kubernetes/kubernetes/blob/8719b4a/pkg/api/v1/pod/util.go
    22  // IsPodReady returns true if a pod is ready; false otherwise.
    23  func IsPodReady(pod *v1.Pod) bool {
    24  	phase := pod.Status.Phase
    25  	if phase != v1.PodRunning || pod.DeletionTimestamp != nil {
    26  		return false
    27  	}
    28  	return IsPodReadyConditionTrue(pod.Status)
    29  }
    30  
    31  // IsPodCompleted returns true if a pod is completed (succeeded or failed); false otherwise.
    32  func IsPodCompleted(pod *v1.Pod) bool {
    33  	phase := pod.Status.Phase
    34  	if phase == v1.PodSucceeded || phase == v1.PodFailed {
    35  		return true
    36  	}
    37  	return false
    38  }
    39  
    40  // IsPodSucceeded returns true if a pod is succeeded
    41  func IsPodSucceeded(pod *v1.Pod) bool {
    42  	phase := pod.Status.Phase
    43  	if phase == v1.PodSucceeded {
    44  		return true
    45  	}
    46  	return false
    47  }
    48  
    49  // credit https://github.com/kubernetes/kubernetes/blob/8719b4a/pkg/api/v1/pod/util.go
    50  // IsPodReady retruns true if a pod is ready; false otherwise.
    51  func IsPodReadyConditionTrue(status v1.PodStatus) bool {
    52  	condition := GetPodReadyCondition(status)
    53  	return condition != nil && condition.Status == v1.ConditionTrue
    54  }
    55  
    56  func PodStatus(pod *v1.Pod) string {
    57  	if pod.DeletionTimestamp != nil {
    58  		return "Terminating"
    59  	}
    60  	phase := pod.Status.Phase
    61  	if IsPodReady(pod) {
    62  		return "Ready"
    63  	}
    64  	return string(phase)
    65  }
    66  
    67  // credit https://github.com/kubernetes/kubernetes/blob/8719b4a/pkg/api/v1/pod/util.go
    68  // Extracts the pod ready condition from the given status and returns that.
    69  // Returns nil if the condition is not present.
    70  func GetPodReadyCondition(status v1.PodStatus) *v1.PodCondition {
    71  	_, condition := GetPodCondition(&status, v1.PodReady)
    72  	return condition
    73  }
    74  
    75  // credit https://github.com/kubernetes/kubernetes/blob/8719b4a/pkg/api/v1/pod/util.go
    76  // GetPodCondition extracts the provided condition from the given status and returns that.
    77  // Returns nil and -1 if the condition is not present, and the index of the located condition.
    78  func GetPodCondition(status *v1.PodStatus, conditionType v1.PodConditionType) (int, *v1.PodCondition) {
    79  	if status == nil {
    80  		return -1, nil
    81  	}
    82  	for i := range status.Conditions {
    83  		if status.Conditions[i].Type == conditionType {
    84  			return i, &status.Conditions[i]
    85  		}
    86  	}
    87  	return -1, nil
    88  }
    89  
    90  // GetCurrentPod returns the current pod the code is running in or nil if it cannot be deduced
    91  func GetCurrentPod(kubeClient kubernetes.Interface, ns string) (*v1.Pod, error) {
    92  	name := os.Getenv("HOSTNAME")
    93  	if name == "" {
    94  		return nil, nil
    95  	}
    96  	name = naming.ToValidName(name)
    97  	return kubeClient.CoreV1().Pods(ns).Get(name, meta_v1.GetOptions{})
    98  }
    99  
   100  func waitForPodSelector(client kubernetes.Interface, namespace string, options meta_v1.ListOptions,
   101  	timeout time.Duration, condition func(event watch.Event) (bool, error)) error {
   102  	w, err := client.CoreV1().Pods(namespace).Watch(options)
   103  	if err != nil {
   104  		return err
   105  	}
   106  	defer w.Stop()
   107  
   108  	ctx, _ := context.WithTimeout(context.Background(), timeout)
   109  	_, err = tools_watch.UntilWithoutRetry(ctx, w, condition)
   110  
   111  	if err == wait.ErrWaitTimeout {
   112  		return fmt.Errorf("pod %s never became ready", options.String())
   113  	}
   114  	return nil
   115  }
   116  
   117  // HasContainerStarted returns true if the given Container has started running
   118  func HasContainerStarted(pod *v1.Pod, idx int) bool {
   119  	if pod == nil {
   120  		return false
   121  	}
   122  	_, statuses, _ := GetContainersWithStatusAndIsInit(pod)
   123  	if idx >= len(statuses) {
   124  		return false
   125  	}
   126  	ic := statuses[idx]
   127  	if ic.State.Running != nil || ic.State.Terminated != nil {
   128  		return true
   129  	}
   130  	return false
   131  }
   132  
   133  // waits for the pod to become ready using the pod name
   134  func WaitForPodNameToBeReady(client kubernetes.Interface, namespace string, name string, timeout time.Duration) error {
   135  	options := meta_v1.ListOptions{
   136  		// TODO
   137  		//FieldSelector: fields.OneTermEqualSelector(api.ObjectNameField, name).String(),
   138  		FieldSelector: fields.OneTermEqualSelector("metadata.name", name).String(),
   139  	}
   140  	condition := func(event watch.Event) (bool, error) {
   141  		pod := event.Object.(*v1.Pod)
   142  
   143  		return IsPodReady(pod), nil
   144  	}
   145  	return waitForPodSelector(client, namespace, options, timeout, condition)
   146  }
   147  
   148  // WaitForPodNameToBeComplete waits for the pod to complete (succeed or fail) using the pod name
   149  func WaitForPodNameToBeComplete(client kubernetes.Interface, namespace string, name string,
   150  	timeout time.Duration) error {
   151  	options := meta_v1.ListOptions{
   152  		// TODO
   153  		//FieldSelector: fields.OneTermEqualSelector(api.ObjectNameField, name).String(),
   154  		FieldSelector: fields.OneTermEqualSelector("metadata.name", name).String(),
   155  	}
   156  	condition := func(event watch.Event) (bool, error) {
   157  		pod := event.Object.(*v1.Pod)
   158  
   159  		return IsPodCompleted(pod), nil
   160  	}
   161  	return waitForPodSelector(client, namespace, options, timeout, condition)
   162  }
   163  
   164  func GetPodNames(client kubernetes.Interface, ns string, filter string) ([]string, error) {
   165  	names := []string{}
   166  	list, err := client.CoreV1().Pods(ns).List(meta_v1.ListOptions{})
   167  	if err != nil {
   168  		return names, fmt.Errorf("Failed to load Pods %s", err)
   169  	}
   170  	for _, d := range list.Items {
   171  		name := d.Name
   172  		if filter == "" || strings.Contains(name, filter) {
   173  			names = append(names, name)
   174  		}
   175  	}
   176  	sort.Strings(names)
   177  	return names, nil
   178  }
   179  
   180  func GetPods(client kubernetes.Interface, ns string, filter string) ([]string, map[string]*v1.Pod, error) {
   181  	names := []string{}
   182  	m := map[string]*v1.Pod{}
   183  	list, err := client.CoreV1().Pods(ns).List(meta_v1.ListOptions{})
   184  	if err != nil {
   185  		return names, m, fmt.Errorf("Failed to load Pods %s", err)
   186  	}
   187  	for _, d := range list.Items {
   188  		c := d
   189  		name := d.Name
   190  		m[name] = &c
   191  		if filter == "" || strings.Contains(name, filter) && d.DeletionTimestamp == nil {
   192  			names = append(names, name)
   193  		}
   194  	}
   195  	sort.Strings(names)
   196  	return names, m, nil
   197  }
   198  
   199  func GetPodsWithLabels(client kubernetes.Interface, ns string, selector string) ([]string, map[string]*v1.Pod, error) {
   200  	names := []string{}
   201  	m := map[string]*v1.Pod{}
   202  	list, err := client.CoreV1().Pods(ns).List(meta_v1.ListOptions{
   203  		LabelSelector: selector,
   204  	})
   205  	if err != nil {
   206  		return names, m, fmt.Errorf("Failed to load Pods %s", err)
   207  	}
   208  	for _, d := range list.Items {
   209  		c := d
   210  		name := d.Name
   211  		m[name] = &c
   212  		if d.DeletionTimestamp == nil {
   213  			names = append(names, name)
   214  		}
   215  	}
   216  	sort.Strings(names)
   217  	return names, m, nil
   218  }
   219  
   220  // GetDevPodNames returns the users dev pod names. If username is blank, all devpod names will be returned
   221  func GetDevPodNames(client kubernetes.Interface, ns string, username string) ([]string, map[string]*v1.Pod, error) {
   222  	names := []string{}
   223  	m := map[string]*v1.Pod{}
   224  	listOptions := meta_v1.ListOptions{}
   225  	if username != "" {
   226  		listOptions.LabelSelector = LabelDevPodUsername + "=" + username
   227  	} else {
   228  		listOptions.LabelSelector = LabelDevPodName
   229  	}
   230  	list, err := client.CoreV1().Pods(ns).List(listOptions)
   231  	if err != nil {
   232  		return names, m, fmt.Errorf("Failed to load Pods %s", err)
   233  	}
   234  	for _, d := range list.Items {
   235  		c := d
   236  		name := d.Name
   237  		m[name] = &c
   238  		names = append(names, name)
   239  	}
   240  	sort.Strings(names)
   241  	return names, m, nil
   242  }
   243  
   244  // GetPodRestars returns the number of restarts of a POD
   245  func GetPodRestarts(pod *v1.Pod) int32 {
   246  	var restarts int32
   247  	statuses := pod.Status.ContainerStatuses
   248  	if len(statuses) == 0 {
   249  		return restarts
   250  	}
   251  	for _, status := range statuses {
   252  		restarts += status.RestartCount
   253  	}
   254  	return restarts
   255  }
   256  
   257  // GetContainersWithStatusAndIsInit gets the containers in the pod, either init containers or non-init depending on whether
   258  // non-init containers are present, and a flag as to whether this list of containers are init containers or not.
   259  func GetContainersWithStatusAndIsInit(pod *v1.Pod) ([]v1.Container, []v1.ContainerStatus, bool) {
   260  	isInit := true
   261  	allContainers := pod.Spec.InitContainers
   262  	statuses := pod.Status.InitContainerStatuses
   263  	containers := pod.Spec.Containers
   264  
   265  	if len(containers) > 1 && len(pod.Status.ContainerStatuses) == len(containers) {
   266  		isInit = false
   267  		// Add the non-init containers
   268  		// If there's a "nop" container at the end, the pod was created with before Tekton 0.5.x, so trim off the no-op container at the end of the list.
   269  		if containers[len(containers)-1].Name == "nop" {
   270  			allContainers = append(allContainers, containers[:len(containers)-1]...)
   271  		} else {
   272  			allContainers = append(allContainers, containers...)
   273  		}
   274  		// Since status ordering is unpredictable, don't trim here - we'll be sorting/filtering below anyway.
   275  		statuses = append(statuses, pod.Status.ContainerStatuses...)
   276  	}
   277  
   278  	var sortedStatuses []v1.ContainerStatus
   279  	for _, c := range allContainers {
   280  		for _, cs := range statuses {
   281  			if cs.Name == c.Name {
   282  				sortedStatuses = append(sortedStatuses, cs)
   283  				break
   284  			}
   285  		}
   286  	}
   287  	return allContainers, sortedStatuses, isInit
   288  }