podutils.go 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. /*
  2. Copyright 2014 The Kubernetes Authors.
  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. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package podutils
  14. import (
  15. "time"
  16. corev1 "k8s.io/api/core/v1"
  17. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  18. "k8s.io/utils/integer"
  19. )
  20. // IsPodAvailable returns true if a pod is available; false otherwise.
  21. // Precondition for an available pod is that it must be ready. On top
  22. // of that, there are two cases when a pod can be considered available:
  23. // 1. minReadySeconds == 0, or
  24. // 2. LastTransitionTime (is set) + minReadySeconds < current time
  25. func IsPodAvailable(pod *corev1.Pod, minReadySeconds int32, now metav1.Time) bool {
  26. if !IsPodReady(pod) {
  27. return false
  28. }
  29. c := getPodReadyCondition(pod.Status)
  30. minReadySecondsDuration := time.Duration(minReadySeconds) * time.Second
  31. if minReadySeconds == 0 || !c.LastTransitionTime.IsZero() && c.LastTransitionTime.Add(minReadySecondsDuration).Before(now.Time) {
  32. return true
  33. }
  34. return false
  35. }
  36. // IsPodReady returns true if a pod is ready; false otherwise.
  37. func IsPodReady(pod *corev1.Pod) bool {
  38. return isPodReadyConditionTrue(pod.Status)
  39. }
  40. // IsPodReadyConditionTrue returns true if a pod is ready; false otherwise.
  41. func isPodReadyConditionTrue(status corev1.PodStatus) bool {
  42. condition := getPodReadyCondition(status)
  43. return condition != nil && condition.Status == corev1.ConditionTrue
  44. }
  45. // GetPodReadyCondition extracts the pod ready condition from the given status and returns that.
  46. // Returns nil if the condition is not present.
  47. func getPodReadyCondition(status corev1.PodStatus) *corev1.PodCondition {
  48. _, condition := getPodCondition(&status, corev1.PodReady)
  49. return condition
  50. }
  51. // GetPodCondition extracts the provided condition from the given status and returns that.
  52. // Returns nil and -1 if the condition is not present, and the index of the located condition.
  53. func getPodCondition(status *corev1.PodStatus, conditionType corev1.PodConditionType) (int, *corev1.PodCondition) {
  54. if status == nil {
  55. return -1, nil
  56. }
  57. return getPodConditionFromList(status.Conditions, conditionType)
  58. }
  59. // GetPodConditionFromList extracts the provided condition from the given list of condition and
  60. // returns the index of the condition and the condition. Returns -1 and nil if the condition is not present.
  61. func getPodConditionFromList(conditions []corev1.PodCondition, conditionType corev1.PodConditionType) (int, *corev1.PodCondition) {
  62. if conditions == nil {
  63. return -1, nil
  64. }
  65. for i := range conditions {
  66. if conditions[i].Type == conditionType {
  67. return i, &conditions[i]
  68. }
  69. }
  70. return -1, nil
  71. }
  72. // ByLogging allows custom sorting of pods so the best one can be picked for getting its logs.
  73. type ByLogging []*corev1.Pod
  74. func (s ByLogging) Len() int { return len(s) }
  75. func (s ByLogging) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
  76. func (s ByLogging) Less(i, j int) bool {
  77. // 1. assigned < unassigned
  78. if s[i].Spec.NodeName != s[j].Spec.NodeName && (len(s[i].Spec.NodeName) == 0 || len(s[j].Spec.NodeName) == 0) {
  79. return len(s[i].Spec.NodeName) > 0
  80. }
  81. // 2. PodRunning < PodUnknown < PodPending
  82. m := map[corev1.PodPhase]int{corev1.PodRunning: 0, corev1.PodUnknown: 1, corev1.PodPending: 2}
  83. if m[s[i].Status.Phase] != m[s[j].Status.Phase] {
  84. return m[s[i].Status.Phase] < m[s[j].Status.Phase]
  85. }
  86. // 3. ready < not ready
  87. if IsPodReady(s[i]) != IsPodReady(s[j]) {
  88. return IsPodReady(s[i])
  89. }
  90. // TODO: take availability into account when we push minReadySeconds information from deployment into pods,
  91. // see https://github.com/kubernetes/kubernetes/issues/22065
  92. // 4. Been ready for more time < less time < empty time
  93. if IsPodReady(s[i]) && IsPodReady(s[j]) && !podReadyTime(s[i]).Equal(podReadyTime(s[j])) {
  94. return afterOrZero(podReadyTime(s[j]), podReadyTime(s[i]))
  95. }
  96. // 5. Pods with containers with higher restart counts < lower restart counts
  97. if maxContainerRestarts(s[i]) != maxContainerRestarts(s[j]) {
  98. return maxContainerRestarts(s[i]) > maxContainerRestarts(s[j])
  99. }
  100. // 6. older pods < newer pods < empty timestamp pods
  101. if !s[i].CreationTimestamp.Equal(&s[j].CreationTimestamp) {
  102. return afterOrZero(&s[j].CreationTimestamp, &s[i].CreationTimestamp)
  103. }
  104. return false
  105. }
  106. // ActivePods type allows custom sorting of pods so a controller can pick the best ones to delete.
  107. type ActivePods []*corev1.Pod
  108. func (s ActivePods) Len() int { return len(s) }
  109. func (s ActivePods) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
  110. func (s ActivePods) Less(i, j int) bool {
  111. // 1. Unassigned < assigned
  112. // If only one of the pods is unassigned, the unassigned one is smaller
  113. if s[i].Spec.NodeName != s[j].Spec.NodeName && (len(s[i].Spec.NodeName) == 0 || len(s[j].Spec.NodeName) == 0) {
  114. return len(s[i].Spec.NodeName) == 0
  115. }
  116. // 2. PodPending < PodUnknown < PodRunning
  117. m := map[corev1.PodPhase]int{corev1.PodPending: 0, corev1.PodUnknown: 1, corev1.PodRunning: 2}
  118. if m[s[i].Status.Phase] != m[s[j].Status.Phase] {
  119. return m[s[i].Status.Phase] < m[s[j].Status.Phase]
  120. }
  121. // 3. Not ready < ready
  122. // If only one of the pods is not ready, the not ready one is smaller
  123. if IsPodReady(s[i]) != IsPodReady(s[j]) {
  124. return !IsPodReady(s[i])
  125. }
  126. // TODO: take availability into account when we push minReadySeconds information from deployment into pods,
  127. // see https://github.com/kubernetes/kubernetes/issues/22065
  128. // 4. Been ready for empty time < less time < more time
  129. // If both pods are ready, the latest ready one is smaller
  130. if IsPodReady(s[i]) && IsPodReady(s[j]) && !podReadyTime(s[i]).Equal(podReadyTime(s[j])) {
  131. return afterOrZero(podReadyTime(s[i]), podReadyTime(s[j]))
  132. }
  133. // 5. Pods with containers with higher restart counts < lower restart counts
  134. if maxContainerRestarts(s[i]) != maxContainerRestarts(s[j]) {
  135. return maxContainerRestarts(s[i]) > maxContainerRestarts(s[j])
  136. }
  137. // 6. Empty creation time pods < newer pods < older pods
  138. if !s[i].CreationTimestamp.Equal(&s[j].CreationTimestamp) {
  139. return afterOrZero(&s[i].CreationTimestamp, &s[j].CreationTimestamp)
  140. }
  141. return false
  142. }
  143. // afterOrZero checks if time t1 is after time t2; if one of them
  144. // is zero, the zero time is seen as after non-zero time.
  145. func afterOrZero(t1, t2 *metav1.Time) bool {
  146. if t1.Time.IsZero() || t2.Time.IsZero() {
  147. return t1.Time.IsZero()
  148. }
  149. return t1.After(t2.Time)
  150. }
  151. func podReadyTime(pod *corev1.Pod) *metav1.Time {
  152. if IsPodReady(pod) {
  153. for _, c := range pod.Status.Conditions {
  154. // we only care about pod ready conditions
  155. if c.Type == corev1.PodReady && c.Status == corev1.ConditionTrue {
  156. return &c.LastTransitionTime
  157. }
  158. }
  159. }
  160. return &metav1.Time{}
  161. }
  162. func maxContainerRestarts(pod *corev1.Pod) int {
  163. maxRestarts := 0
  164. for _, c := range pod.Status.ContainerStatuses {
  165. maxRestarts = integer.IntMax(maxRestarts, int(c.RestartCount))
  166. }
  167. return maxRestarts
  168. }