From 657bc33500bcb523affc5afdd439ec188207c0d0 Mon Sep 17 00:00:00 2001 From: Victor Marmol Date: Thu, 2 Apr 2015 08:42:19 -0700 Subject: [PATCH] Include ContainerStatus in "describe pod". Fixes #6076. --- pkg/kubectl/describe.go | 46 +++++++++++++++++++++ pkg/kubectl/describe_test.go | 78 ++++++++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+) diff --git a/pkg/kubectl/describe.go b/pkg/kubectl/describe.go index c8189642cd..0a6ef4d76f 100644 --- a/pkg/kubectl/describe.go +++ b/pkg/kubectl/describe.go @@ -242,6 +242,8 @@ func describePod(pod *api.Pod, rcs []api.ReplicationController, events *api.Even fmt.Fprintf(out, "Labels:\t%s\n", formatLabels(pod.Labels)) fmt.Fprintf(out, "Status:\t%s\n", string(pod.Status.Phase)) fmt.Fprintf(out, "Replication Controllers:\t%s\n", printReplicationControllersByLabels(rcs)) + fmt.Fprintf(out, "Containers:\n") + describeContainers(pod.Status.ContainerStatuses, out) if len(pod.Status.Conditions) > 0 { fmt.Fprint(out, "Conditions:\n Type\tStatus\n") for _, c := range pod.Status.Conditions { @@ -257,6 +259,50 @@ func describePod(pod *api.Pod, rcs []api.ReplicationController, events *api.Even }) } +func describeContainers(containers []api.ContainerStatus, out io.Writer) { + for _, container := range containers { + fmt.Fprintf(out, " %v:\n", container.Name) + fmt.Fprintf(out, " Image:\t%s\n", container.Image) + switch { + case container.State.Running != nil: + fmt.Fprintf(out, " State:\tRunning\n") + fmt.Fprintf(out, " Started:\t%v\n", container.State.Running.StartedAt.Time.Format(time.RFC1123Z)) + case container.State.Waiting != nil: + fmt.Fprintf(out, " State:\tWaiting\n") + if container.State.Waiting.Reason != "" { + fmt.Fprintf(out, " Reason:\t%s\n", container.State.Waiting.Reason) + } + case container.State.Termination != nil: + fmt.Fprintf(out, " State:\tTerminated\n") + if container.State.Termination.Reason != "" { + fmt.Fprintf(out, " Reason:\t%s\n", container.State.Termination.Reason) + } + if container.State.Termination.Message != "" { + fmt.Fprintf(out, " Message:\t%s\n", container.State.Termination.Message) + } + fmt.Fprintf(out, " Exit Code:\t%d\n", container.State.Termination.ExitCode) + if container.State.Termination.Signal > 0 { + fmt.Fprintf(out, " Signal:\t%d\n", container.State.Termination.Signal) + } + fmt.Fprintf(out, " Started:\t%s\n", container.State.Termination.StartedAt.Time.Format(time.RFC1123Z)) + fmt.Fprintf(out, " Finished:\t%s\n", container.State.Termination.FinishedAt.Time.Format(time.RFC1123Z)) + default: + fmt.Fprintf(out, " State:\tWaiting\n") + } + + fmt.Fprintf(out, " Ready:\t%v\n", printBool(container.Ready)) + fmt.Fprintf(out, " Restart Count:\t%d\n", container.RestartCount) + } +} + +func printBool(value bool) string { + if value { + return "True" + } + + return "False" +} + // ReplicationControllerDescriber generates information about a replication controller // and the pods it has created. type ReplicationControllerDescriber struct { diff --git a/pkg/kubectl/describe_test.go b/pkg/kubectl/describe_test.go index dd9b4680eb..86316e2a60 100644 --- a/pkg/kubectl/describe_test.go +++ b/pkg/kubectl/describe_test.go @@ -17,6 +17,7 @@ limitations under the License. package kubectl import ( + "bytes" "fmt" "reflect" "strings" @@ -107,6 +108,83 @@ func TestPodDescribeResultsSorted(t *testing.T) { VerifyDatesInOrder(out, "\n" /* rowDelimiter */, "\t" /* columnDelimiter */, t) } +func TestDescribeContainers(t *testing.T) { + testCases := []struct { + input api.ContainerStatus + expectedElements []string + }{ + // Running state. + { + input: api.ContainerStatus{ + Name: "test", + State: api.ContainerState{ + Running: &api.ContainerStateRunning{ + StartedAt: util.NewTime(time.Now()), + }, + }, + Ready: true, + RestartCount: 7, + Image: "image", + }, + expectedElements: []string{"test", "State", "Running", "Ready", "True", "Restart Count", "7", "Image", "image", "Started"}, + }, + // Waiting state. + { + input: api.ContainerStatus{ + Name: "test", + State: api.ContainerState{ + Waiting: &api.ContainerStateWaiting{ + Reason: "potato", + }, + }, + Ready: true, + RestartCount: 7, + Image: "image", + }, + expectedElements: []string{"test", "State", "Waiting", "Ready", "True", "Restart Count", "7", "Image", "image", "Reason", "potato"}, + }, + // Terminated state. + { + input: api.ContainerStatus{ + Name: "test", + State: api.ContainerState{ + Termination: &api.ContainerStateTerminated{ + StartedAt: util.NewTime(time.Now()), + FinishedAt: util.NewTime(time.Now()), + Reason: "potato", + ExitCode: 2, + }, + }, + Ready: true, + RestartCount: 7, + Image: "image", + }, + expectedElements: []string{"test", "State", "Terminated", "Ready", "True", "Restart Count", "7", "Image", "image", "Reason", "potato", "Started", "Finished", "Exit Code", "2"}, + }, + // No state defaults to waiting. + { + input: api.ContainerStatus{ + Name: "test", + Ready: true, + RestartCount: 7, + Image: "image", + }, + expectedElements: []string{"test", "State", "Waiting", "Ready", "True", "Restart Count", "7", "Image", "image"}, + }, + } + + for i, testCase := range testCases { + out := new(bytes.Buffer) + describeContainers([]api.ContainerStatus{testCase.input}, out) + output := out.String() + for _, expected := range testCase.expectedElements { + if !strings.Contains(output, expected) { + t.Errorf("Test case %d: expected to find %q in output: %q", i, expected, output) + } + } + } +} + func TestDescribers(t *testing.T) { first := &api.Event{} second := &api.Pod{}