From 4bd3274f433cc37bde2cff5875d61108cc97a7d2 Mon Sep 17 00:00:00 2001 From: lowang-bh Date: Sat, 25 Nov 2023 21:01:41 +0800 Subject: [PATCH] show pod Status as phase Failed or Succeeded if it has deletionTimestamp Signed-off-by: lowang-bh fix according to comments Signed-off-by: lowang-bh --- pkg/printers/internalversion/printers.go | 6 +- pkg/printers/internalversion/printers_test.go | 83 +++++++++++++++++++ .../k8s.io/kubectl/pkg/describe/describe.go | 2 +- .../kubectl/pkg/describe/describe_test.go | 51 +++++++++--- 4 files changed, 128 insertions(+), 14 deletions(-) diff --git a/pkg/printers/internalversion/printers.go b/pkg/printers/internalversion/printers.go index 26019cb3f80..a7c09864675 100644 --- a/pkg/printers/internalversion/printers.go +++ b/pkg/printers/internalversion/printers.go @@ -51,6 +51,7 @@ import ( "k8s.io/apimachinery/pkg/util/duration" "k8s.io/apimachinery/pkg/util/sets" "k8s.io/client-go/util/certificate/csr" + podutil "k8s.io/kubernetes/pkg/api/v1/pod" "k8s.io/kubernetes/pkg/apis/admissionregistration" "k8s.io/kubernetes/pkg/apis/apiserverinternal" "k8s.io/kubernetes/pkg/apis/apps" @@ -865,7 +866,8 @@ func printPod(pod *api.Pod, options printers.GenerateOptions) ([]metav1.TableRow lastRestartDate := metav1.NewTime(time.Time{}) lastRestartableInitContainerRestartDate := metav1.NewTime(time.Time{}) - reason := string(pod.Status.Phase) + podPhase := pod.Status.Phase + reason := string(podPhase) if pod.Status.Reason != "" { reason = pod.Status.Reason } @@ -988,7 +990,7 @@ func printPod(pod *api.Pod, options printers.GenerateOptions) ([]metav1.TableRow if pod.DeletionTimestamp != nil && pod.Status.Reason == node.NodeUnreachablePodReason { reason = "Unknown" - } else if pod.DeletionTimestamp != nil { + } else if pod.DeletionTimestamp != nil && !podutil.IsPodPhaseTerminal(apiv1.PodPhase(podPhase)) { reason = "Terminating" } diff --git a/pkg/printers/internalversion/printers_test.go b/pkg/printers/internalversion/printers_test.go index dd2bd30d109..2815b7a18b7 100644 --- a/pkg/printers/internalversion/printers_test.go +++ b/pkg/printers/internalversion/printers_test.go @@ -1183,6 +1183,7 @@ func TestPrintServiceLoadBalancer(t *testing.T) { } func TestPrintPod(t *testing.T) { + deleteTime := metav1.NewTime(time.Now().Add(-10 * time.Second)) tests := []struct { pod api.Pod expect []metav1.TableRow @@ -1526,6 +1527,88 @@ func TestPrintPod(t *testing.T) { }, []metav1.TableRow{{Cells: []interface{}{"test15", "0/2", apiv1.PodReasonSchedulingGated, "0", ""}}}, }, + { + // Test pod condition succeed + api.Pod{ + ObjectMeta: metav1.ObjectMeta{Name: "test16"}, + Spec: api.PodSpec{Containers: make([]api.Container, 1)}, + Status: api.PodStatus{ + Phase: api.PodSucceeded, + ContainerStatuses: []api.ContainerStatus{ + { + Ready: false, + RestartCount: 0, + State: api.ContainerState{Terminated: &api.ContainerStateTerminated{Reason: "Completed", ExitCode: 0}}, + }, + }, + }, + }, + []metav1.TableRow{{Conditions: podSuccessConditions, Cells: []interface{}{"test16", "0/1", "Completed", "0", ""}}}, + }, + { + // Test pod condition failed + api.Pod{ + ObjectMeta: metav1.ObjectMeta{Name: "test17"}, + Spec: api.PodSpec{Containers: make([]api.Container, 1)}, + Status: api.PodStatus{ + Phase: api.PodFailed, + ContainerStatuses: []api.ContainerStatus{ + { + Ready: false, + RestartCount: 0, + State: api.ContainerState{Terminated: &api.ContainerStateTerminated{Reason: "Error", ExitCode: 1}}, + }, + }, + }, + }, + []metav1.TableRow{{Conditions: podFailedConditions, Cells: []interface{}{"test17", "0/1", "Error", "0", ""}}}, + }, + { + // Test pod condition succeed with deletion + api.Pod{ + ObjectMeta: metav1.ObjectMeta{Name: "test18", DeletionTimestamp: &deleteTime}, + Spec: api.PodSpec{Containers: make([]api.Container, 1)}, + Status: api.PodStatus{ + Phase: api.PodSucceeded, + ContainerStatuses: []api.ContainerStatus{ + { + Ready: false, + RestartCount: 0, + State: api.ContainerState{Terminated: &api.ContainerStateTerminated{Reason: "Completed", ExitCode: 0}}, + }, + }, + }, + }, + []metav1.TableRow{{Conditions: podSuccessConditions, Cells: []interface{}{"test18", "0/1", "Completed", "0", ""}}}, + }, + { + // Test pod condition running with deletion + api.Pod{ + ObjectMeta: metav1.ObjectMeta{Name: "test19", DeletionTimestamp: &deleteTime}, + Spec: api.PodSpec{Containers: make([]api.Container, 1)}, + Status: api.PodStatus{ + Phase: "Running", + ContainerStatuses: []api.ContainerStatus{ + { + Ready: false, + RestartCount: 0, + State: api.ContainerState{Running: &api.ContainerStateRunning{}}, + }, + }, + }, + }, + []metav1.TableRow{{Cells: []interface{}{"test19", "0/1", "Terminating", "0", ""}}}, + }, + { // Test pod condition pending with deletion + api.Pod{ + ObjectMeta: metav1.ObjectMeta{Name: "test20", DeletionTimestamp: &deleteTime}, + Spec: api.PodSpec{Containers: make([]api.Container, 1)}, + Status: api.PodStatus{ + Phase: "Pending", + }, + }, + []metav1.TableRow{{Cells: []interface{}{"test20", "0/1", "Terminating", "0", ""}}}, + }, } for i, test := range tests { diff --git a/staging/src/k8s.io/kubectl/pkg/describe/describe.go b/staging/src/k8s.io/kubectl/pkg/describe/describe.go index b9aa38990b5..26a53827e22 100644 --- a/staging/src/k8s.io/kubectl/pkg/describe/describe.go +++ b/staging/src/k8s.io/kubectl/pkg/describe/describe.go @@ -811,7 +811,7 @@ func describePod(pod *corev1.Pod, events *corev1.EventList) (string, error) { } printLabelsMultiline(w, "Labels", pod.Labels) printAnnotationsMultiline(w, "Annotations", pod.Annotations) - if pod.DeletionTimestamp != nil { + if pod.DeletionTimestamp != nil && pod.Status.Phase != corev1.PodFailed && pod.Status.Phase != corev1.PodSucceeded { w.Write(LEVEL_0, "Status:\tTerminating (lasts %s)\n", translateTimestampSince(*pod.DeletionTimestamp)) w.Write(LEVEL_0, "Termination Grace Period:\t%ds\n", *pod.DeletionGracePeriodSeconds) } else { diff --git a/staging/src/k8s.io/kubectl/pkg/describe/describe_test.go b/staging/src/k8s.io/kubectl/pkg/describe/describe_test.go index f07250d2b55..9733f0b947e 100644 --- a/staging/src/k8s.io/kubectl/pkg/describe/describe_test.go +++ b/staging/src/k8s.io/kubectl/pkg/describe/describe_test.go @@ -67,7 +67,7 @@ func TestDescribePod(t *testing.T) { gracePeriod := int64(1234) condition1 := corev1.PodConditionType("condition1") condition2 := corev1.PodConditionType("condition2") - fake := fake.NewSimpleClientset(&corev1.Pod{ + runningPod := corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "bar", Namespace: "foo", @@ -85,6 +85,7 @@ func TestDescribePod(t *testing.T) { }, }, Status: corev1.PodStatus{ + Phase: corev1.PodRunning, Conditions: []corev1.PodCondition{ { Type: condition1, @@ -92,18 +93,46 @@ func TestDescribePod(t *testing.T) { }, }, }, - }) - c := &describeClient{T: t, Namespace: "foo", Interface: fake} - d := PodDescriber{c} - out, err := d.Describe("foo", "bar", DescriberSettings{ShowEvents: true}) - if err != nil { - t.Errorf("unexpected error: %v", err) } - if !strings.Contains(out, "bar") || !strings.Contains(out, "Status:") { - t.Errorf("unexpected out: %s", out) + tests := []struct { + name string + namespace string + phase corev1.PodPhase + wantOutput []string + }{ + { + name: "foo", namespace: "bar", phase: "Running", + wantOutput: []string{"bar", "Status:", "Terminating (lasts 10y)", "Termination Grace Period", "1234s"}, + }, + { + name: "pod1", namespace: "ns1", phase: "Pending", + wantOutput: []string{"pod1", "ns1", "Terminating (lasts 10y)", "Termination Grace Period", "1234s"}, + }, + { + name: "pod2", namespace: "ns2", phase: "Succeeded", + wantOutput: []string{"pod2", "ns2", "Succeeded"}, + }, + { + name: "pod3", namespace: "ns3", phase: "Failed", + wantOutput: []string{"pod3", "ns3", "Failed"}, + }, } - if !strings.Contains(out, "Terminating (lasts 10y)") || !strings.Contains(out, "1234s") { - t.Errorf("unexpected out: %s", out) + + for i, test := range tests { + pod := runningPod.DeepCopy() + pod.Name, pod.Namespace, pod.Status.Phase = test.name, test.namespace, test.phase + fake := fake.NewSimpleClientset(pod) + c := &describeClient{T: t, Namespace: pod.Namespace, Interface: fake} + d := PodDescriber{c} + out, err := d.Describe(pod.Namespace, pod.Name, DescriberSettings{ShowEvents: true}) + if err != nil { + t.Errorf("case %d: unexpected error: %v", i, err) + } + for _, wantStr := range test.wantOutput { + if !strings.Contains(out, wantStr) { + t.Errorf("case %d didn't contain want(%s): unexpected out:\n%s", i, wantStr, out) + } + } } }