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 }