123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191 |
- /*
- Copyright 2014 The Kubernetes Authors.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
- package podutils
- import (
- "time"
- corev1 "k8s.io/api/core/v1"
- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- "k8s.io/utils/integer"
- )
- // IsPodAvailable returns true if a pod is available; false otherwise.
- // Precondition for an available pod is that it must be ready. On top
- // of that, there are two cases when a pod can be considered available:
- // 1. minReadySeconds == 0, or
- // 2. LastTransitionTime (is set) + minReadySeconds < current time
- func IsPodAvailable(pod *corev1.Pod, minReadySeconds int32, now metav1.Time) bool {
- if !IsPodReady(pod) {
- return false
- }
- c := getPodReadyCondition(pod.Status)
- minReadySecondsDuration := time.Duration(minReadySeconds) * time.Second
- if minReadySeconds == 0 || !c.LastTransitionTime.IsZero() && c.LastTransitionTime.Add(minReadySecondsDuration).Before(now.Time) {
- return true
- }
- return false
- }
- // IsPodReady returns true if a pod is ready; false otherwise.
- func IsPodReady(pod *corev1.Pod) bool {
- return isPodReadyConditionTrue(pod.Status)
- }
- // IsPodReadyConditionTrue returns true if a pod is ready; false otherwise.
- func isPodReadyConditionTrue(status corev1.PodStatus) bool {
- condition := getPodReadyCondition(status)
- return condition != nil && condition.Status == corev1.ConditionTrue
- }
- // GetPodReadyCondition extracts the pod ready condition from the given status and returns that.
- // Returns nil if the condition is not present.
- func getPodReadyCondition(status corev1.PodStatus) *corev1.PodCondition {
- _, condition := getPodCondition(&status, corev1.PodReady)
- return condition
- }
- // GetPodCondition extracts the provided condition from the given status and returns that.
- // Returns nil and -1 if the condition is not present, and the index of the located condition.
- func getPodCondition(status *corev1.PodStatus, conditionType corev1.PodConditionType) (int, *corev1.PodCondition) {
- if status == nil {
- return -1, nil
- }
- return getPodConditionFromList(status.Conditions, conditionType)
- }
- // GetPodConditionFromList extracts the provided condition from the given list of condition and
- // returns the index of the condition and the condition. Returns -1 and nil if the condition is not present.
- func getPodConditionFromList(conditions []corev1.PodCondition, conditionType corev1.PodConditionType) (int, *corev1.PodCondition) {
- if conditions == nil {
- return -1, nil
- }
- for i := range conditions {
- if conditions[i].Type == conditionType {
- return i, &conditions[i]
- }
- }
- return -1, nil
- }
- // ByLogging allows custom sorting of pods so the best one can be picked for getting its logs.
- type ByLogging []*corev1.Pod
- func (s ByLogging) Len() int { return len(s) }
- func (s ByLogging) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
- func (s ByLogging) Less(i, j int) bool {
- // 1. assigned < unassigned
- if s[i].Spec.NodeName != s[j].Spec.NodeName && (len(s[i].Spec.NodeName) == 0 || len(s[j].Spec.NodeName) == 0) {
- return len(s[i].Spec.NodeName) > 0
- }
- // 2. PodRunning < PodUnknown < PodPending
- m := map[corev1.PodPhase]int{corev1.PodRunning: 0, corev1.PodUnknown: 1, corev1.PodPending: 2}
- if m[s[i].Status.Phase] != m[s[j].Status.Phase] {
- return m[s[i].Status.Phase] < m[s[j].Status.Phase]
- }
- // 3. ready < not ready
- if IsPodReady(s[i]) != IsPodReady(s[j]) {
- return IsPodReady(s[i])
- }
- // TODO: take availability into account when we push minReadySeconds information from deployment into pods,
- // see https://github.com/kubernetes/kubernetes/issues/22065
- // 4. Been ready for more time < less time < empty time
- if IsPodReady(s[i]) && IsPodReady(s[j]) && !podReadyTime(s[i]).Equal(podReadyTime(s[j])) {
- return afterOrZero(podReadyTime(s[j]), podReadyTime(s[i]))
- }
- // 5. Pods with containers with higher restart counts < lower restart counts
- if maxContainerRestarts(s[i]) != maxContainerRestarts(s[j]) {
- return maxContainerRestarts(s[i]) > maxContainerRestarts(s[j])
- }
- // 6. older pods < newer pods < empty timestamp pods
- if !s[i].CreationTimestamp.Equal(&s[j].CreationTimestamp) {
- return afterOrZero(&s[j].CreationTimestamp, &s[i].CreationTimestamp)
- }
- return false
- }
- // ActivePods type allows custom sorting of pods so a controller can pick the best ones to delete.
- type ActivePods []*corev1.Pod
- func (s ActivePods) Len() int { return len(s) }
- func (s ActivePods) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
- func (s ActivePods) Less(i, j int) bool {
- // 1. Unassigned < assigned
- // If only one of the pods is unassigned, the unassigned one is smaller
- if s[i].Spec.NodeName != s[j].Spec.NodeName && (len(s[i].Spec.NodeName) == 0 || len(s[j].Spec.NodeName) == 0) {
- return len(s[i].Spec.NodeName) == 0
- }
- // 2. PodPending < PodUnknown < PodRunning
- m := map[corev1.PodPhase]int{corev1.PodPending: 0, corev1.PodUnknown: 1, corev1.PodRunning: 2}
- if m[s[i].Status.Phase] != m[s[j].Status.Phase] {
- return m[s[i].Status.Phase] < m[s[j].Status.Phase]
- }
- // 3. Not ready < ready
- // If only one of the pods is not ready, the not ready one is smaller
- if IsPodReady(s[i]) != IsPodReady(s[j]) {
- return !IsPodReady(s[i])
- }
- // TODO: take availability into account when we push minReadySeconds information from deployment into pods,
- // see https://github.com/kubernetes/kubernetes/issues/22065
- // 4. Been ready for empty time < less time < more time
- // If both pods are ready, the latest ready one is smaller
- if IsPodReady(s[i]) && IsPodReady(s[j]) && !podReadyTime(s[i]).Equal(podReadyTime(s[j])) {
- return afterOrZero(podReadyTime(s[i]), podReadyTime(s[j]))
- }
- // 5. Pods with containers with higher restart counts < lower restart counts
- if maxContainerRestarts(s[i]) != maxContainerRestarts(s[j]) {
- return maxContainerRestarts(s[i]) > maxContainerRestarts(s[j])
- }
- // 6. Empty creation time pods < newer pods < older pods
- if !s[i].CreationTimestamp.Equal(&s[j].CreationTimestamp) {
- return afterOrZero(&s[i].CreationTimestamp, &s[j].CreationTimestamp)
- }
- return false
- }
- // afterOrZero checks if time t1 is after time t2; if one of them
- // is zero, the zero time is seen as after non-zero time.
- func afterOrZero(t1, t2 *metav1.Time) bool {
- if t1.Time.IsZero() || t2.Time.IsZero() {
- return t1.Time.IsZero()
- }
- return t1.After(t2.Time)
- }
- func podReadyTime(pod *corev1.Pod) *metav1.Time {
- if IsPodReady(pod) {
- for _, c := range pod.Status.Conditions {
- // we only care about pod ready conditions
- if c.Type == corev1.PodReady && c.Status == corev1.ConditionTrue {
- return &c.LastTransitionTime
- }
- }
- }
- return &metav1.Time{}
- }
- func maxContainerRestarts(pod *corev1.Pod) int {
- maxRestarts := 0
- for _, c := range pod.Status.ContainerStatuses {
- maxRestarts = integer.IntMax(maxRestarts, int(c.RestartCount))
- }
- return maxRestarts
- }
|