...

Source file src/k8s.io/kubectl/pkg/util/podutils/podutils.go

Documentation: k8s.io/kubectl/pkg/util/podutils

     1  /*
     2  Copyright 2014 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package podutils
    18  
    19  import (
    20  	"time"
    21  
    22  	corev1 "k8s.io/api/core/v1"
    23  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    24  )
    25  
    26  // IsPodAvailable returns true if a pod is available; false otherwise.
    27  // Precondition for an available pod is that it must be ready. On top
    28  // of that, there are two cases when a pod can be considered available:
    29  // 1. minReadySeconds == 0, or
    30  // 2. LastTransitionTime (is set) + minReadySeconds < current time
    31  func IsPodAvailable(pod *corev1.Pod, minReadySeconds int32, now metav1.Time) bool {
    32  	if !IsPodReady(pod) {
    33  		return false
    34  	}
    35  
    36  	c := getPodReadyCondition(pod.Status)
    37  	minReadySecondsDuration := time.Duration(minReadySeconds) * time.Second
    38  	if minReadySeconds == 0 || !c.LastTransitionTime.IsZero() && c.LastTransitionTime.Add(minReadySecondsDuration).Before(now.Time) {
    39  		return true
    40  	}
    41  	return false
    42  }
    43  
    44  // IsPodReady returns true if a pod is ready; false otherwise.
    45  func IsPodReady(pod *corev1.Pod) bool {
    46  	return isPodReadyConditionTrue(pod.Status)
    47  }
    48  
    49  func isPodDeleting(pod *corev1.Pod) bool {
    50  	return pod.DeletionTimestamp != nil
    51  }
    52  
    53  // IsPodReadyConditionTrue returns true if a pod is ready; false otherwise.
    54  func isPodReadyConditionTrue(status corev1.PodStatus) bool {
    55  	condition := getPodReadyCondition(status)
    56  	return condition != nil && condition.Status == corev1.ConditionTrue
    57  }
    58  
    59  // GetPodReadyCondition extracts the pod ready condition from the given status and returns that.
    60  // Returns nil if the condition is not present.
    61  func getPodReadyCondition(status corev1.PodStatus) *corev1.PodCondition {
    62  	_, condition := getPodCondition(&status, corev1.PodReady)
    63  	return condition
    64  }
    65  
    66  // GetPodCondition extracts the provided condition from the given status and returns that.
    67  // Returns nil and -1 if the condition is not present, and the index of the located condition.
    68  func getPodCondition(status *corev1.PodStatus, conditionType corev1.PodConditionType) (int, *corev1.PodCondition) {
    69  	if status == nil {
    70  		return -1, nil
    71  	}
    72  	return getPodConditionFromList(status.Conditions, conditionType)
    73  }
    74  
    75  // GetPodConditionFromList extracts the provided condition from the given list of condition and
    76  // returns the index of the condition and the condition. Returns -1 and nil if the condition is not present.
    77  func getPodConditionFromList(conditions []corev1.PodCondition, conditionType corev1.PodConditionType) (int, *corev1.PodCondition) {
    78  	if conditions == nil {
    79  		return -1, nil
    80  	}
    81  	for i := range conditions {
    82  		if conditions[i].Type == conditionType {
    83  			return i, &conditions[i]
    84  		}
    85  	}
    86  	return -1, nil
    87  }
    88  
    89  // ByLogging allows custom sorting of pods so the best one can be picked for getting its logs.
    90  type ByLogging []*corev1.Pod
    91  
    92  func (s ByLogging) Len() int      { return len(s) }
    93  func (s ByLogging) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
    94  
    95  func (s ByLogging) Less(i, j int) bool {
    96  	// 1. assigned < unassigned
    97  	if s[i].Spec.NodeName != s[j].Spec.NodeName && (len(s[i].Spec.NodeName) == 0 || len(s[j].Spec.NodeName) == 0) {
    98  		return len(s[i].Spec.NodeName) > 0
    99  	}
   100  	// 2. PodRunning < PodUnknown < PodPending
   101  	m := map[corev1.PodPhase]int{corev1.PodRunning: 0, corev1.PodUnknown: 1, corev1.PodPending: 2}
   102  	if m[s[i].Status.Phase] != m[s[j].Status.Phase] {
   103  		return m[s[i].Status.Phase] < m[s[j].Status.Phase]
   104  	}
   105  	// 3. ready < not ready
   106  	if IsPodReady(s[i]) != IsPodReady(s[j]) {
   107  		return IsPodReady(s[i])
   108  	}
   109  	// TODO: take availability into account when we push minReadySeconds information from deployment into pods,
   110  	//       see https://github.com/kubernetes/kubernetes/issues/22065
   111  	// 4. Been ready for more time < less time < empty time
   112  	if IsPodReady(s[i]) && IsPodReady(s[j]) && !podReadyTime(s[i]).Equal(podReadyTime(s[j])) {
   113  		return afterOrZero(podReadyTime(s[j]), podReadyTime(s[i]))
   114  	}
   115  	// 5. Pods with containers with higher restart counts < lower restart counts
   116  	if maxContainerRestarts(s[i]) != maxContainerRestarts(s[j]) {
   117  		return maxContainerRestarts(s[i]) > maxContainerRestarts(s[j])
   118  	}
   119  	// 6. older pods < newer pods < empty timestamp pods
   120  	if !s[i].CreationTimestamp.Equal(&s[j].CreationTimestamp) {
   121  		return afterOrZero(&s[j].CreationTimestamp, &s[i].CreationTimestamp)
   122  	}
   123  	return false
   124  }
   125  
   126  // ActivePods type allows custom sorting of pods so a controller can pick the best ones to delete.
   127  type ActivePods []*corev1.Pod
   128  
   129  func (s ActivePods) Len() int      { return len(s) }
   130  func (s ActivePods) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
   131  
   132  func (s ActivePods) Less(i, j int) bool {
   133  	// 1. Unassigned < assigned
   134  	// If only one of the pods is unassigned, the unassigned one is smaller
   135  	if s[i].Spec.NodeName != s[j].Spec.NodeName && (len(s[i].Spec.NodeName) == 0 || len(s[j].Spec.NodeName) == 0) {
   136  		return len(s[i].Spec.NodeName) == 0
   137  	}
   138  	// 2. PodPending < PodUnknown < PodRunning
   139  	m := map[corev1.PodPhase]int{corev1.PodPending: 0, corev1.PodUnknown: 1, corev1.PodRunning: 2}
   140  	if m[s[i].Status.Phase] != m[s[j].Status.Phase] {
   141  		return m[s[i].Status.Phase] < m[s[j].Status.Phase]
   142  	}
   143  	// 3. Not ready < ready
   144  	// If only one of the pods is not ready, the not ready one is smaller
   145  	if IsPodReady(s[i]) != IsPodReady(s[j]) {
   146  		return !IsPodReady(s[i])
   147  	}
   148  	// 4. Deleting < Not deleting
   149  	if isPodDeleting(s[i]) != isPodDeleting(s[j]) {
   150  		return isPodDeleting(s[i])
   151  	}
   152  	// 5. Older deletion timestamp < newer deletion timestamp
   153  	if isPodDeleting(s[i]) && isPodDeleting(s[j]) && !s[i].ObjectMeta.DeletionTimestamp.Equal(s[j].ObjectMeta.DeletionTimestamp) {
   154  		return s[i].ObjectMeta.DeletionTimestamp.Before(s[j].ObjectMeta.DeletionTimestamp)
   155  	}
   156  	// TODO: take availability into account when we push minReadySeconds information from deployment into pods,
   157  	//       see https://github.com/kubernetes/kubernetes/issues/22065
   158  	// 6. Been ready for empty time < less time < more time
   159  	// If both pods are ready, the latest ready one is smaller
   160  	if IsPodReady(s[i]) && IsPodReady(s[j]) && !podReadyTime(s[i]).Equal(podReadyTime(s[j])) {
   161  		return afterOrZero(podReadyTime(s[i]), podReadyTime(s[j]))
   162  	}
   163  	// 7. Pods with containers with higher restart counts < lower restart counts
   164  	if maxContainerRestarts(s[i]) != maxContainerRestarts(s[j]) {
   165  		return maxContainerRestarts(s[i]) > maxContainerRestarts(s[j])
   166  	}
   167  	// 8. Empty creation time pods < newer pods < older pods
   168  	if !s[i].CreationTimestamp.Equal(&s[j].CreationTimestamp) {
   169  		return afterOrZero(&s[i].CreationTimestamp, &s[j].CreationTimestamp)
   170  	}
   171  	return false
   172  }
   173  
   174  // afterOrZero checks if time t1 is after time t2; if one of them
   175  // is zero, the zero time is seen as after non-zero time.
   176  func afterOrZero(t1, t2 *metav1.Time) bool {
   177  	if t1.Time.IsZero() || t2.Time.IsZero() {
   178  		return t1.Time.IsZero()
   179  	}
   180  	return t1.After(t2.Time)
   181  }
   182  
   183  func podReadyTime(pod *corev1.Pod) *metav1.Time {
   184  	for _, c := range pod.Status.Conditions {
   185  		// we only care about pod ready conditions
   186  		if c.Type == corev1.PodReady && c.Status == corev1.ConditionTrue {
   187  			return &c.LastTransitionTime
   188  		}
   189  	}
   190  	return &metav1.Time{}
   191  }
   192  
   193  func maxContainerRestarts(pod *corev1.Pod) int {
   194  	maxRestarts := 0
   195  	for _, c := range pod.Status.ContainerStatuses {
   196  		maxRestarts = max(maxRestarts, int(c.RestartCount))
   197  	}
   198  	return maxRestarts
   199  }
   200  
   201  // ContainerType and VisitContainers are taken from
   202  // https://github.com/kubernetes/kubernetes/blob/master/pkg/api/v1/pod/util.go
   203  // kubectl cannot directly import this due to project goals
   204  
   205  // ContainerType signifies container type
   206  type ContainerType int
   207  
   208  const (
   209  	// Containers is for normal containers
   210  	Containers ContainerType = 1 << iota
   211  	// InitContainers is for init containers
   212  	InitContainers
   213  	// EphemeralContainers is for ephemeral containers
   214  	EphemeralContainers
   215  )
   216  
   217  // AllContainers specifies that all containers be visited.
   218  const AllContainers ContainerType = (InitContainers | Containers | EphemeralContainers)
   219  
   220  // ContainerVisitor is called with each container spec, and returns true
   221  // if visiting should continue.
   222  type ContainerVisitor func(container *corev1.Container, containerType ContainerType) (shouldContinue bool)
   223  
   224  // VisitContainers invokes the visitor function with a pointer to every container
   225  // spec in the given pod spec with type set in mask. If visitor returns false,
   226  // visiting is short-circuited. VisitContainers returns true if visiting completes,
   227  // false if visiting was short-circuited.
   228  func VisitContainers(podSpec *corev1.PodSpec, mask ContainerType, visitor ContainerVisitor) bool {
   229  	if mask&InitContainers != 0 {
   230  		for i := range podSpec.InitContainers {
   231  			if !visitor(&podSpec.InitContainers[i], InitContainers) {
   232  				return false
   233  			}
   234  		}
   235  	}
   236  	if mask&Containers != 0 {
   237  		for i := range podSpec.Containers {
   238  			if !visitor(&podSpec.Containers[i], Containers) {
   239  				return false
   240  			}
   241  		}
   242  	}
   243  	if mask&EphemeralContainers != 0 {
   244  		for i := range podSpec.EphemeralContainers {
   245  			if !visitor((*corev1.Container)(&podSpec.EphemeralContainers[i].EphemeralContainerCommon), EphemeralContainers) {
   246  				return false
   247  			}
   248  		}
   249  	}
   250  	return true
   251  }
   252  

View as plain text