diff --git a/pkg/controller/controller_utils.go b/pkg/controller/controller_utils.go index a9886568d7b..efdab55c137 100644 --- a/pkg/controller/controller_utils.go +++ b/pkg/controller/controller_utils.go @@ -525,6 +525,43 @@ func (f *FakePodControl) Clear() { f.Templates = []api.PodTemplateSpec{} } +// ByLogging allows custom sorting of pods so the best one can be picked for getting its logs. +type ByLogging []*api.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[api.PodPhase]int{api.PodRunning: 0, api.PodUnknown: 1, api.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 api.IsPodReady(s[i]) != api.IsPodReady(s[j]) { + return api.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 api.IsPodReady(s[i]) && api.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 []*api.Pod diff --git a/pkg/kubectl/cmd/util/factory.go b/pkg/kubectl/cmd/util/factory.go index 8a57c6ae6db..ac8dff7b32a 100644 --- a/pkg/kubectl/cmd/util/factory.go +++ b/pkg/kubectl/cmd/util/factory.go @@ -520,7 +520,7 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory { return nil, errors.New("provided options object is not a PodLogOptions") } selector := labels.SelectorFromSet(t.Spec.Selector) - sortBy := func(pods []*api.Pod) sort.Interface { return controller.ActivePods(pods) } + sortBy := func(pods []*api.Pod) sort.Interface { return controller.ByLogging(pods) } pod, numPods, err := GetFirstPod(c, t.Namespace, selector, 20*time.Second, sortBy) if err != nil { return nil, err @@ -540,7 +540,7 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory { if err != nil { return nil, fmt.Errorf("invalid label selector: %v", err) } - sortBy := func(pods []*api.Pod) sort.Interface { return controller.ActivePods(pods) } + sortBy := func(pods []*api.Pod) sort.Interface { return controller.ByLogging(pods) } pod, numPods, err := GetFirstPod(c, t.Namespace, selector, 20*time.Second, sortBy) if err != nil { return nil, err @@ -800,7 +800,7 @@ See http://releases.k8s.io/HEAD/docs/user-guide/services-firewalls.md for more d // GetFirstPod returns a pod matching the namespace and label selector // and the number of all pods that match the label selector. -func GetFirstPod(client client.Interface, namespace string, selector labels.Selector, timeout time.Duration, sortBy func([]*api.Pod) sort.Interface) (*api.Pod, int, error) { +func GetFirstPod(client client.PodsNamespacer, namespace string, selector labels.Selector, timeout time.Duration, sortBy func([]*api.Pod) sort.Interface) (*api.Pod, int, error) { options := api.ListOptions{LabelSelector: selector} podList, err := client.Pods(namespace).List(options) diff --git a/pkg/kubectl/cmd/util/factory_test.go b/pkg/kubectl/cmd/util/factory_test.go index 4af5fcb8883..6d8120cc2fc 100644 --- a/pkg/kubectl/cmd/util/factory_test.go +++ b/pkg/kubectl/cmd/util/factory_test.go @@ -476,12 +476,12 @@ func TestGetFirstPod(t *testing.T) { { name: "kubectl logs - two ready pods", podList: newPodList(2, -1, -1, labelSet), - sortBy: func(pods []*api.Pod) sort.Interface { return controller.ActivePods(pods) }, + sortBy: func(pods []*api.Pod) sort.Interface { return controller.ByLogging(pods) }, expected: &api.Pod{ ObjectMeta: api.ObjectMeta{ - Name: "pod-2", + Name: "pod-1", Namespace: api.NamespaceDefault, - CreationTimestamp: unversioned.Date(2016, time.April, 1, 1, 0, 1, 0, time.UTC), + CreationTimestamp: unversioned.Date(2016, time.April, 1, 1, 0, 0, 0, time.UTC), Labels: map[string]string{"test": "selector"}, }, Status: api.PodStatus{ @@ -498,7 +498,7 @@ func TestGetFirstPod(t *testing.T) { { name: "kubectl logs - one unhealthy, one healthy", podList: newPodList(2, -1, 1, labelSet), - sortBy: func(pods []*api.Pod) sort.Interface { return controller.ActivePods(pods) }, + sortBy: func(pods []*api.Pod) sort.Interface { return controller.ByLogging(pods) }, expected: &api.Pod{ ObjectMeta: api.ObjectMeta{ Name: "pod-2",