mirror of https://github.com/k3s-io/k3s
kubectl: copy pod utils into util/podutils package
parent
539bdbc355
commit
5f8f607c27
|
@ -109,7 +109,6 @@ go_library(
|
||||||
],
|
],
|
||||||
importpath = "k8s.io/kubernetes/pkg/kubectl",
|
importpath = "k8s.io/kubernetes/pkg/kubectl",
|
||||||
deps = [
|
deps = [
|
||||||
"//pkg/api/v1/pod:go_default_library",
|
|
||||||
"//pkg/apis/core:go_default_library",
|
"//pkg/apis/core:go_default_library",
|
||||||
"//pkg/apis/core/v1:go_default_library",
|
"//pkg/apis/core/v1:go_default_library",
|
||||||
"//pkg/controller/deployment/util:go_default_library",
|
"//pkg/controller/deployment/util:go_default_library",
|
||||||
|
@ -118,6 +117,7 @@ go_library(
|
||||||
"//pkg/kubectl/scheme:go_default_library",
|
"//pkg/kubectl/scheme:go_default_library",
|
||||||
"//pkg/kubectl/util:go_default_library",
|
"//pkg/kubectl/util:go_default_library",
|
||||||
"//pkg/kubectl/util/hash:go_default_library",
|
"//pkg/kubectl/util/hash:go_default_library",
|
||||||
|
"//pkg/kubectl/util/podutils:go_default_library",
|
||||||
"//pkg/kubectl/util/slice:go_default_library",
|
"//pkg/kubectl/util/slice:go_default_library",
|
||||||
"//pkg/printers:go_default_library",
|
"//pkg/printers:go_default_library",
|
||||||
"//pkg/printers/internalversion:go_default_library",
|
"//pkg/printers/internalversion:go_default_library",
|
||||||
|
|
|
@ -22,9 +22,9 @@ go_library(
|
||||||
importpath = "k8s.io/kubernetes/pkg/kubectl/polymorphichelpers",
|
importpath = "k8s.io/kubernetes/pkg/kubectl/polymorphichelpers",
|
||||||
visibility = ["//visibility:public"],
|
visibility = ["//visibility:public"],
|
||||||
deps = [
|
deps = [
|
||||||
"//pkg/controller:go_default_library",
|
|
||||||
"//pkg/kubectl:go_default_library",
|
"//pkg/kubectl:go_default_library",
|
||||||
"//pkg/kubectl/scheme:go_default_library",
|
"//pkg/kubectl/scheme:go_default_library",
|
||||||
|
"//pkg/kubectl/util/podutils:go_default_library",
|
||||||
"//staging/src/k8s.io/api/apps/v1:go_default_library",
|
"//staging/src/k8s.io/api/apps/v1:go_default_library",
|
||||||
"//staging/src/k8s.io/api/apps/v1beta1:go_default_library",
|
"//staging/src/k8s.io/api/apps/v1beta1:go_default_library",
|
||||||
"//staging/src/k8s.io/api/apps/v1beta2:go_default_library",
|
"//staging/src/k8s.io/api/apps/v1beta2:go_default_library",
|
||||||
|
@ -63,7 +63,7 @@ go_test(
|
||||||
],
|
],
|
||||||
embed = [":go_default_library"],
|
embed = [":go_default_library"],
|
||||||
deps = [
|
deps = [
|
||||||
"//pkg/controller:go_default_library",
|
"//pkg/kubectl/util/podutils:go_default_library",
|
||||||
"//staging/src/k8s.io/api/apps/v1:go_default_library",
|
"//staging/src/k8s.io/api/apps/v1:go_default_library",
|
||||||
"//staging/src/k8s.io/api/apps/v1beta1:go_default_library",
|
"//staging/src/k8s.io/api/apps/v1beta1:go_default_library",
|
||||||
"//staging/src/k8s.io/api/apps/v1beta2:go_default_library",
|
"//staging/src/k8s.io/api/apps/v1beta2:go_default_library",
|
||||||
|
|
|
@ -25,7 +25,7 @@ import (
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/cli-runtime/pkg/genericclioptions"
|
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||||
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
|
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||||
"k8s.io/kubernetes/pkg/controller"
|
"k8s.io/kubernetes/pkg/kubectl/util/podutils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// attachablePodForObject returns the pod to which to attach given an object.
|
// attachablePodForObject returns the pod to which to attach given an object.
|
||||||
|
@ -48,7 +48,7 @@ func attachablePodForObject(restClientGetter genericclioptions.RESTClientGetter,
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("cannot attach to %T: %v", object, err)
|
return nil, fmt.Errorf("cannot attach to %T: %v", object, err)
|
||||||
}
|
}
|
||||||
sortBy := func(pods []*corev1.Pod) sort.Interface { return sort.Reverse(controller.ActivePods(pods)) }
|
sortBy := func(pods []*corev1.Pod) sort.Interface { return sort.Reverse(podutils.ActivePods(pods)) }
|
||||||
pod, _, err := GetFirstPod(clientset, namespace, selector.String(), timeout, sortBy)
|
pod, _, err := GetFirstPod(clientset, namespace, selector.String(), timeout, sortBy)
|
||||||
return pod, err
|
return pod, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ import (
|
||||||
"k8s.io/apimachinery/pkg/watch"
|
"k8s.io/apimachinery/pkg/watch"
|
||||||
fakeexternal "k8s.io/client-go/kubernetes/fake"
|
fakeexternal "k8s.io/client-go/kubernetes/fake"
|
||||||
testcore "k8s.io/client-go/testing"
|
testcore "k8s.io/client-go/testing"
|
||||||
"k8s.io/kubernetes/pkg/controller"
|
"k8s.io/kubernetes/pkg/kubectl/util/podutils"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGetFirstPod(t *testing.T) {
|
func TestGetFirstPod(t *testing.T) {
|
||||||
|
@ -48,7 +48,7 @@ func TestGetFirstPod(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "kubectl logs - two ready pods",
|
name: "kubectl logs - two ready pods",
|
||||||
podList: newPodList(2, -1, -1, labelSet),
|
podList: newPodList(2, -1, -1, labelSet),
|
||||||
sortBy: func(pods []*corev1.Pod) sort.Interface { return controller.ByLogging(pods) },
|
sortBy: func(pods []*corev1.Pod) sort.Interface { return podutils.ByLogging(pods) },
|
||||||
expected: &corev1.Pod{
|
expected: &corev1.Pod{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "pod-1",
|
Name: "pod-1",
|
||||||
|
@ -70,7 +70,7 @@ func TestGetFirstPod(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "kubectl logs - one unhealthy, one healthy",
|
name: "kubectl logs - one unhealthy, one healthy",
|
||||||
podList: newPodList(2, -1, 1, labelSet),
|
podList: newPodList(2, -1, 1, labelSet),
|
||||||
sortBy: func(pods []*corev1.Pod) sort.Interface { return controller.ByLogging(pods) },
|
sortBy: func(pods []*corev1.Pod) sort.Interface { return podutils.ByLogging(pods) },
|
||||||
expected: &corev1.Pod{
|
expected: &corev1.Pod{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "pod-2",
|
Name: "pod-2",
|
||||||
|
@ -93,7 +93,7 @@ func TestGetFirstPod(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "kubectl attach - two ready pods",
|
name: "kubectl attach - two ready pods",
|
||||||
podList: newPodList(2, -1, -1, labelSet),
|
podList: newPodList(2, -1, -1, labelSet),
|
||||||
sortBy: func(pods []*corev1.Pod) sort.Interface { return sort.Reverse(controller.ActivePods(pods)) },
|
sortBy: func(pods []*corev1.Pod) sort.Interface { return sort.Reverse(podutils.ActivePods(pods)) },
|
||||||
expected: &corev1.Pod{
|
expected: &corev1.Pod{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "pod-1",
|
Name: "pod-1",
|
||||||
|
@ -136,7 +136,7 @@ func TestGetFirstPod(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
sortBy: func(pods []*corev1.Pod) sort.Interface { return sort.Reverse(controller.ActivePods(pods)) },
|
sortBy: func(pods []*corev1.Pod) sort.Interface { return sort.Reverse(podutils.ActivePods(pods)) },
|
||||||
expected: &corev1.Pod{
|
expected: &corev1.Pod{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "pod-1",
|
Name: "pod-1",
|
||||||
|
|
|
@ -29,7 +29,7 @@ import (
|
||||||
"k8s.io/cli-runtime/pkg/genericclioptions"
|
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||||
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
|
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||||
"k8s.io/client-go/rest"
|
"k8s.io/client-go/rest"
|
||||||
"k8s.io/kubernetes/pkg/controller"
|
"k8s.io/kubernetes/pkg/kubectl/util/podutils"
|
||||||
)
|
)
|
||||||
|
|
||||||
func logsForObject(restClientGetter genericclioptions.RESTClientGetter, object, options runtime.Object, timeout time.Duration, allContainers bool) ([]*rest.Request, error) {
|
func logsForObject(restClientGetter genericclioptions.RESTClientGetter, object, options runtime.Object, timeout time.Duration, allContainers bool) ([]*rest.Request, error) {
|
||||||
|
@ -99,7 +99,7 @@ func logsForObjectWithClient(clientset corev1client.CoreV1Interface, object, opt
|
||||||
return nil, fmt.Errorf("cannot get the logs from %T: %v", object, err)
|
return nil, fmt.Errorf("cannot get the logs from %T: %v", object, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
sortBy := func(pods []*v1.Pod) sort.Interface { return controller.ByLogging(pods) }
|
sortBy := func(pods []*v1.Pod) sort.Interface { return podutils.ByLogging(pods) }
|
||||||
pod, numPods, err := GetFirstPod(clientset, namespace, selector.String(), timeout, sortBy)
|
pod, numPods, err := GetFirstPod(clientset, namespace, selector.String(), timeout, sortBy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -35,9 +35,9 @@ import (
|
||||||
scaleclient "k8s.io/client-go/scale"
|
scaleclient "k8s.io/client-go/scale"
|
||||||
"k8s.io/client-go/util/integer"
|
"k8s.io/client-go/util/integer"
|
||||||
"k8s.io/client-go/util/retry"
|
"k8s.io/client-go/util/retry"
|
||||||
podutil "k8s.io/kubernetes/pkg/api/v1/pod"
|
|
||||||
deploymentutil "k8s.io/kubernetes/pkg/controller/deployment/util"
|
deploymentutil "k8s.io/kubernetes/pkg/controller/deployment/util"
|
||||||
"k8s.io/kubernetes/pkg/kubectl/util"
|
"k8s.io/kubernetes/pkg/kubectl/util"
|
||||||
|
"k8s.io/kubernetes/pkg/kubectl/util/podutils"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newInt32Ptr(val int) *int32 {
|
func newInt32Ptr(val int) *int32 {
|
||||||
|
@ -443,7 +443,7 @@ func (r *RollingUpdater) readyPods(oldRc, newRc *corev1.ReplicationController, m
|
||||||
if v1Pod.DeletionTimestamp != nil {
|
if v1Pod.DeletionTimestamp != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if !podutil.IsPodAvailable(&v1Pod, minReadySeconds, r.nowFn()) {
|
if !podutils.IsPodAvailable(&v1Pod, minReadySeconds, r.nowFn()) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
switch controller.Name {
|
switch controller.Name {
|
||||||
|
|
|
@ -69,6 +69,7 @@ filegroup(
|
||||||
"//pkg/kubectl/util/hash:all-srcs",
|
"//pkg/kubectl/util/hash:all-srcs",
|
||||||
"//pkg/kubectl/util/i18n:all-srcs",
|
"//pkg/kubectl/util/i18n:all-srcs",
|
||||||
"//pkg/kubectl/util/logs:all-srcs",
|
"//pkg/kubectl/util/logs:all-srcs",
|
||||||
|
"//pkg/kubectl/util/podutils:all-srcs",
|
||||||
"//pkg/kubectl/util/slice:all-srcs",
|
"//pkg/kubectl/util/slice:all-srcs",
|
||||||
"//pkg/kubectl/util/term:all-srcs",
|
"//pkg/kubectl/util/term:all-srcs",
|
||||||
],
|
],
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||||
|
|
||||||
|
go_library(
|
||||||
|
name = "go_default_library",
|
||||||
|
srcs = ["podutils.go"],
|
||||||
|
importpath = "k8s.io/kubernetes/pkg/kubectl/util/podutils",
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
deps = [
|
||||||
|
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||||
|
"//staging/src/k8s.io/client-go/util/integer:go_default_library",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
filegroup(
|
||||||
|
name = "package-srcs",
|
||||||
|
srcs = glob(["**"]),
|
||||||
|
tags = ["automanaged"],
|
||||||
|
visibility = ["//visibility:private"],
|
||||||
|
)
|
||||||
|
|
||||||
|
filegroup(
|
||||||
|
name = "all-srcs",
|
||||||
|
srcs = [":package-srcs"],
|
||||||
|
tags = ["automanaged"],
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
)
|
|
@ -0,0 +1,190 @@
|
||||||
|
/*
|
||||||
|
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/client-go/util/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
|
||||||
|
}
|
Loading…
Reference in New Issue