diff --git a/pkg/kubectl/describe.go b/pkg/kubectl/describe.go index a399bcbad13..3e94d350824 100644 --- a/pkg/kubectl/describe.go +++ b/pkg/kubectl/describe.go @@ -409,7 +409,7 @@ func describePod(pod *api.Pod, rcs []api.ReplicationController, events *api.Even fmt.Fprintf(out, "IP:\t%s\n", pod.Status.PodIP) fmt.Fprintf(out, "Replication Controllers:\t%s\n", printReplicationControllersByLabels(rcs)) fmt.Fprintf(out, "Containers:\n") - describeContainers(pod.Status.ContainerStatuses, out) + describeContainers(pod, out) if len(pod.Status.Conditions) > 0 { fmt.Fprint(out, "Conditions:\n Type\tStatus\n") for _, c := range pod.Status.Conditions { @@ -473,39 +473,55 @@ func (d *PersistentVolumeClaimDescriber) Describe(namespace, name string) (strin }) } -func describeContainers(containers []api.ContainerStatus, out io.Writer) { - for _, container := range containers { +func describeContainers(pod *api.Pod, out io.Writer) { + statuses := map[string]api.ContainerStatus{} + for _, status := range pod.Status.ContainerStatuses { + statuses[status.Name] = status + } + + for _, container := range pod.Spec.Containers { + status := statuses[container.Name] + state := status.State + fmt.Fprintf(out, " %v:\n", container.Name) fmt.Fprintf(out, " Image:\t%s\n", container.Image) + + if len(container.Resources.Limits) > 0 { + fmt.Fprintf(out, " Limits:\n") + } + for name, quantity := range container.Resources.Limits { + fmt.Fprintf(out, " %s:\t%s\n", name, quantity.String()) + } + switch { - case container.State.Running != nil: + case 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, " Started:\t%v\n", state.Running.StartedAt.Time.Format(time.RFC1123Z)) + case 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) + if state.Waiting.Reason != "" { + fmt.Fprintf(out, " Reason:\t%s\n", state.Waiting.Reason) } - case container.State.Terminated != nil: + case state.Terminated != nil: fmt.Fprintf(out, " State:\tTerminated\n") - if container.State.Terminated.Reason != "" { - fmt.Fprintf(out, " Reason:\t%s\n", container.State.Terminated.Reason) + if state.Terminated.Reason != "" { + fmt.Fprintf(out, " Reason:\t%s\n", state.Terminated.Reason) } - if container.State.Terminated.Message != "" { - fmt.Fprintf(out, " Message:\t%s\n", container.State.Terminated.Message) + if state.Terminated.Message != "" { + fmt.Fprintf(out, " Message:\t%s\n", state.Terminated.Message) } - fmt.Fprintf(out, " Exit Code:\t%d\n", container.State.Terminated.ExitCode) - if container.State.Terminated.Signal > 0 { - fmt.Fprintf(out, " Signal:\t%d\n", container.State.Terminated.Signal) + fmt.Fprintf(out, " Exit Code:\t%d\n", state.Terminated.ExitCode) + if state.Terminated.Signal > 0 { + fmt.Fprintf(out, " Signal:\t%d\n", state.Terminated.Signal) } - fmt.Fprintf(out, " Started:\t%s\n", container.State.Terminated.StartedAt.Time.Format(time.RFC1123Z)) - fmt.Fprintf(out, " Finished:\t%s\n", container.State.Terminated.FinishedAt.Time.Format(time.RFC1123Z)) + fmt.Fprintf(out, " Started:\t%s\n", state.Terminated.StartedAt.Time.Format(time.RFC1123Z)) + fmt.Fprintf(out, " Finished:\t%s\n", state.Terminated.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) + fmt.Fprintf(out, " Ready:\t%v\n", printBool(status.Ready)) + fmt.Fprintf(out, " Restart Count:\t%d\n", status.RestartCount) } } diff --git a/pkg/kubectl/describe_test.go b/pkg/kubectl/describe_test.go index d7481d6a0b1..4ca08ac6e8b 100644 --- a/pkg/kubectl/describe_test.go +++ b/pkg/kubectl/describe_test.go @@ -25,6 +25,7 @@ import ( "time" "github.com/GoogleCloudPlatform/kubernetes/pkg/api" + "github.com/GoogleCloudPlatform/kubernetes/pkg/api/resource" "github.com/GoogleCloudPlatform/kubernetes/pkg/client" "github.com/GoogleCloudPlatform/kubernetes/pkg/client/testclient" "github.com/GoogleCloudPlatform/kubernetes/pkg/util" @@ -119,12 +120,14 @@ func TestPodDescribeResultsSorted(t *testing.T) { func TestDescribeContainers(t *testing.T) { testCases := []struct { - input api.ContainerStatus + container api.Container + status api.ContainerStatus expectedElements []string }{ // Running state. { - input: api.ContainerStatus{ + container: api.Container{Name: "test", Image: "image"}, + status: api.ContainerStatus{ Name: "test", State: api.ContainerState{ Running: &api.ContainerStateRunning{ @@ -133,13 +136,13 @@ func TestDescribeContainers(t *testing.T) { }, Ready: true, RestartCount: 7, - Image: "image", }, expectedElements: []string{"test", "State", "Running", "Ready", "True", "Restart Count", "7", "Image", "image", "Started"}, }, // Waiting state. { - input: api.ContainerStatus{ + container: api.Container{Name: "test", Image: "image"}, + status: api.ContainerStatus{ Name: "test", State: api.ContainerState{ Waiting: &api.ContainerStateWaiting{ @@ -148,13 +151,13 @@ func TestDescribeContainers(t *testing.T) { }, 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{ + container: api.Container{Name: "test", Image: "image"}, + status: api.ContainerStatus{ Name: "test", State: api.ContainerState{ Terminated: &api.ContainerStateTerminated{ @@ -166,25 +169,52 @@ func TestDescribeContainers(t *testing.T) { }, 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{ + container: api.Container{Name: "test", Image: "image"}, + status: api.ContainerStatus{ Name: "test", Ready: true, RestartCount: 7, - Image: "image", }, expectedElements: []string{"test", "State", "Waiting", "Ready", "True", "Restart Count", "7", "Image", "image"}, }, + // Using limits. + { + container: api.Container{ + Name: "test", + Image: "image", + Resources: api.ResourceRequirements{ + Limits: api.ResourceList{ + api.ResourceName(api.ResourceCPU): resource.MustParse("1000"), + api.ResourceName(api.ResourceMemory): resource.MustParse("4G"), + api.ResourceName(api.ResourceStorage): resource.MustParse("20G"), + }, + }, + }, + status: api.ContainerStatus{ + Name: "test", + Ready: true, + RestartCount: 7, + }, + expectedElements: []string{"cpu", "1k", "memory", "4G", "storage", "20G"}, + }, } for i, testCase := range testCases { out := new(bytes.Buffer) - describeContainers([]api.ContainerStatus{testCase.input}, out) + pod := api.Pod{ + Spec: api.PodSpec{ + Containers: []api.Container{testCase.container}, + }, + Status: api.PodStatus{ + ContainerStatuses: []api.ContainerStatus{testCase.status}, + }, + } + describeContainers(&pod, out) output := out.String() for _, expected := range testCase.expectedElements { if !strings.Contains(output, expected) {