diff --git a/pkg/kubectl/resource_printer.go b/pkg/kubectl/resource_printer.go index c704ad158ae..ce923790def 100644 --- a/pkg/kubectl/resource_printer.go +++ b/pkg/kubectl/resource_printer.go @@ -2075,13 +2075,29 @@ func printDeployment(deployment *extensions.Deployment, w io.Writer, options Pri updatedReplicas := deployment.Status.UpdatedReplicas availableReplicas := deployment.Status.AvailableReplicas age := translateTimestamp(deployment.CreationTimestamp) + containers := deployment.Spec.Template.Spec.Containers + selector, err := metav1.LabelSelectorAsSelector(deployment.Spec.Selector) + if err != nil { + // this shouldn't happen if LabelSelector passed validation + return err + } + if _, err := fmt.Fprintf(w, "%s\t%d\t%d\t%d\t%d\t%s", name, desiredReplicas, currentReplicas, updatedReplicas, availableReplicas, age); err != nil { return err } + if options.Wide { + if err := layoutContainers(containers, w); err != nil { + return err + } + if _, err := fmt.Fprintf(w, "\t%s", selector.String()); err != nil { + return err + } + } + if _, err := fmt.Fprint(w, AppendLabels(deployment.Labels, options.ColumnLabels)); err != nil { return err } - _, err := fmt.Fprint(w, AppendAllLabels(options.ShowLabels, deployment.Labels)) + _, err = fmt.Fprint(w, AppendAllLabels(options.ShowLabels, deployment.Labels)) return err } @@ -2352,6 +2368,9 @@ func formatWideHeaders(wide bool, t reflect.Type) []string { if t.String() == "*extensions.DaemonSet" || t.String() == "*extensions.DaemonSetList" { return []string{"CONTAINER(S)", "IMAGE(S)", "SELECTOR"} } + if t.String() == "*extensions.Deployment" || t.String() == "*extensions.DeploymentList" { + return []string{"CONTAINER(S)", "IMAGE(S)", "SELECTOR"} + } if t.String() == "*extensions.ReplicaSet" || t.String() == "*extensions.ReplicaSetList" { return []string{"CONTAINER(S)", "IMAGE(S)", "SELECTOR"} } diff --git a/pkg/kubectl/resource_printer_test.go b/pkg/kubectl/resource_printer_test.go index 079a73023eb..b4d77775a14 100644 --- a/pkg/kubectl/resource_printer_test.go +++ b/pkg/kubectl/resource_printer_test.go @@ -255,9 +255,7 @@ func ErrorPrintHandler(obj *TestPrintType, w io.Writer, options PrintOptions) er func TestCustomTypePrinting(t *testing.T) { columns := []string{"Data"} - printer := NewHumanReadablePrinter(PrintOptions{ - ColumnLabels: []string{}, - }) + printer := NewHumanReadablePrinter(PrintOptions{}) printer.Handler(columns, PrintCustomType) obj := TestPrintType{"test object"} @@ -274,9 +272,7 @@ func TestCustomTypePrinting(t *testing.T) { func TestCustomTypePrintingWithKind(t *testing.T) { columns := []string{"Data"} - printer := NewHumanReadablePrinter(PrintOptions{ - ColumnLabels: []string{}, - }) + printer := NewHumanReadablePrinter(PrintOptions{}) printer.Handler(columns, PrintCustomType) printer.EnsurePrintWithKind("test") @@ -294,9 +290,7 @@ func TestCustomTypePrintingWithKind(t *testing.T) { func TestPrintHandlerError(t *testing.T) { columns := []string{"Data"} - printer := NewHumanReadablePrinter(PrintOptions{ - ColumnLabels: []string{}, - }) + printer := NewHumanReadablePrinter(PrintOptions{}) printer.Handler(columns, ErrorPrintHandler) obj := TestPrintType{"test object"} buffer := &bytes.Buffer{} @@ -307,9 +301,7 @@ func TestPrintHandlerError(t *testing.T) { } func TestUnknownTypePrinting(t *testing.T) { - printer := NewHumanReadablePrinter(PrintOptions{ - ColumnLabels: []string{}, - }) + printer := NewHumanReadablePrinter(PrintOptions{}) buffer := &bytes.Buffer{} err := printer.PrintObj(&TestUnknownType{}, buffer) if err == nil { @@ -528,17 +520,14 @@ func TestPrinters(t *testing.T) { printers := map[string]ResourcePrinter{ "humanReadable": NewHumanReadablePrinter(PrintOptions{ - NoHeaders: true, - ColumnLabels: []string{}, + NoHeaders: true, }), - "humanReadableHeaders": NewHumanReadablePrinter(PrintOptions{ - ColumnLabels: []string{}, - }), - "json": &JSONPrinter{}, - "yaml": &YAMLPrinter{}, - "template": templatePrinter, - "template2": templatePrinter2, - "jsonpath": jsonpathPrinter, + "humanReadableHeaders": NewHumanReadablePrinter(PrintOptions{}), + "json": &JSONPrinter{}, + "yaml": &YAMLPrinter{}, + "template": templatePrinter, + "template2": templatePrinter2, + "jsonpath": jsonpathPrinter, "name": &NamePrinter{ Typer: api.Scheme, Decoder: api.Codecs.UniversalDecoder(), @@ -576,9 +565,7 @@ func TestPrinters(t *testing.T) { func TestPrintEventsResultSorted(t *testing.T) { // Arrange - printer := NewHumanReadablePrinter(PrintOptions{ - ColumnLabels: []string{}, - }) + printer := NewHumanReadablePrinter(PrintOptions{}) obj := api.EventList{ Items: []api.Event{ @@ -622,9 +609,7 @@ func TestPrintEventsResultSorted(t *testing.T) { } func TestPrintNodeStatus(t *testing.T) { - printer := NewHumanReadablePrinter(PrintOptions{ - ColumnLabels: []string{}, - }) + printer := NewHumanReadablePrinter(PrintOptions{}) table := []struct { node api.Node status string @@ -744,8 +729,7 @@ func TestPrintNodeStatus(t *testing.T) { func TestPrintNodeExternalIP(t *testing.T) { printer := NewHumanReadablePrinter(PrintOptions{ - ColumnLabels: []string{}, - Wide: true, + Wide: true, }) table := []struct { node api.Node @@ -952,7 +936,7 @@ func TestPrintHumanReadableService(t *testing.T) { for _, svc := range tests { for _, wide := range []bool{false, true} { buff := bytes.Buffer{} - printService(&svc, &buff, PrintOptions{false, false, false, wide, false, false, false, "", []string{}}) + printService(&svc, &buff, PrintOptions{Wide: wide}) output := string(buff.Bytes()) ip := svc.Spec.ClusterIP if !strings.Contains(output, ip) { @@ -1138,7 +1122,6 @@ func TestPrintHumanReadableWithNamespace(t *testing.T) { // Expect output to include namespace when requested. printer := NewHumanReadablePrinter(PrintOptions{ WithNamespace: true, - ColumnLabels: []string{}, }) buffer := &bytes.Buffer{} err := printer.PrintObj(test.obj, buffer) @@ -1153,7 +1136,6 @@ func TestPrintHumanReadableWithNamespace(t *testing.T) { // Expect error when trying to get all namespaces for un-namespaced object. printer := NewHumanReadablePrinter(PrintOptions{ WithNamespace: true, - ColumnLabels: []string{}, }) buffer := &bytes.Buffer{} err := printer.PrintObj(test.obj, buffer) @@ -1250,7 +1232,7 @@ func TestPrintPod(t *testing.T) { buf := bytes.NewBuffer([]byte{}) printer := HumanReadablePrinter{} for _, test := range tests { - printer.printPod(&test.pod, buf, PrintOptions{false, false, false, false, true, false, false, "", []string{}}) + printer.printPod(&test.pod, buf, PrintOptions{ShowAll: true}) // We ignore time if !strings.HasPrefix(buf.String(), test.expect) { t.Fatalf("Expected: %s, got: %s", test.expect, buf.String()) @@ -1344,7 +1326,7 @@ func TestPrintNonTerminatedPod(t *testing.T) { buf := bytes.NewBuffer([]byte{}) printer := HumanReadablePrinter{} for _, test := range tests { - printer.printPod(&test.pod, buf, PrintOptions{false, false, false, false, false, false, false, "", []string{}}) + printer.printPod(&test.pod, buf, PrintOptions{}) // We ignore time if !strings.HasPrefix(buf.String(), test.expect) { t.Fatalf("Expected: %s, got: %s", test.expect, buf.String()) @@ -1405,7 +1387,7 @@ func TestPrintPodWithLabels(t *testing.T) { buf := bytes.NewBuffer([]byte{}) printer := HumanReadablePrinter{} for _, test := range tests { - printer.printPod(&test.pod, buf, PrintOptions{false, false, false, false, false, false, false, "", test.labelColumns}) + printer.printPod(&test.pod, buf, PrintOptions{ColumnLabels: test.labelColumns}) // We ignore time if !strings.HasPrefix(buf.String(), test.startsWith) || !strings.HasSuffix(buf.String(), test.endsWith) { t.Fatalf("Expected to start with: %s and end with: %s, but got: %s", test.startsWith, test.endsWith, buf.String()) @@ -1443,6 +1425,7 @@ func TestPrintDeployment(t *testing.T) { tests := []struct { deployment extensions.Deployment expect string + wideExpect string }{ { extensions.Deployment{ @@ -1453,8 +1436,20 @@ func TestPrintDeployment(t *testing.T) { Spec: extensions.DeploymentSpec{ Replicas: 5, Template: api.PodTemplateSpec{ - Spec: api.PodSpec{Containers: make([]api.Container, 2)}, + Spec: api.PodSpec{ + Containers: []api.Container{ + { + Name: "fake-container1", + Image: "fake-image1", + }, + { + Name: "fake-container2", + Image: "fake-image2", + }, + }, + }, }, + Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}}, }, Status: extensions.DeploymentStatus{ Replicas: 10, @@ -1464,16 +1459,23 @@ func TestPrintDeployment(t *testing.T) { }, }, "test1\t5\t10\t2\t1\t0s\n", + "test1\t5\t10\t2\t1\t0s\tfake-container1,fake-container2\tfake-image1,fake-image2\tfoo=bar\n", }, } buf := bytes.NewBuffer([]byte{}) for _, test := range tests { - printDeployment(&test.deployment, buf, PrintOptions{false, false, false, false, true, false, false, "", []string{}}) + printDeployment(&test.deployment, buf, PrintOptions{}) if buf.String() != test.expect { t.Fatalf("Expected: %s, got: %s", test.expect, buf.String()) } buf.Reset() + // print deployment with '-o wide' option + printDeployment(&test.deployment, buf, PrintOptions{Wide: true}) + if buf.String() != test.wideExpect { + t.Fatalf("Expected: %s, got: %s", test.wideExpect, buf.String()) + } + buf.Reset() } } @@ -1505,7 +1507,7 @@ func TestPrintDaemonSet(t *testing.T) { buf := bytes.NewBuffer([]byte{}) for _, test := range tests { - printDaemonSet(&test.ds, buf, PrintOptions{false, false, false, false, false, false, false, "", []string{}}) + printDaemonSet(&test.ds, buf, PrintOptions{}) if !strings.HasPrefix(buf.String(), test.startsWith) { t.Fatalf("Expected to start with %s but got %s", test.startsWith, buf.String()) } @@ -1553,7 +1555,7 @@ func TestPrintJob(t *testing.T) { buf := bytes.NewBuffer([]byte{}) for _, test := range tests { - printJob(&test.job, buf, PrintOptions{false, false, false, false, true, false, false, "", []string{}}) + printJob(&test.job, buf, PrintOptions{}) if buf.String() != test.expect { t.Fatalf("Expected: %s, got: %s", test.expect, buf.String()) } @@ -1614,7 +1616,7 @@ func TestPrintPodShowLabels(t *testing.T) { printer := HumanReadablePrinter{} for _, test := range tests { - printer.printPod(&test.pod, buf, PrintOptions{false, false, false, false, false, test.showLabels, false, "", []string{}}) + printer.printPod(&test.pod, buf, PrintOptions{ShowLabels: test.showLabels}) // We ignore time if !strings.HasPrefix(buf.String(), test.startsWith) || !strings.HasSuffix(buf.String(), test.endsWith) { t.Fatalf("Expected to start with: %s and end with: %s, but got: %s", test.startsWith, test.endsWith, buf.String()) @@ -1664,7 +1666,7 @@ func TestPrintService(t *testing.T) { buf := bytes.NewBuffer([]byte{}) for _, test := range tests { - printService(&test.service, buf, PrintOptions{false, false, false, false, true, false, false, "", []string{}}) + printService(&test.service, buf, PrintOptions{}) // We ignore time if buf.String() != test.expect { t.Fatalf("Expected: %s, got: %s %d", test.expect, buf.String(), strings.Compare(test.expect, buf.String())) @@ -1697,7 +1699,7 @@ func TestPrintPodDisruptionBudget(t *testing.T) { buf := bytes.NewBuffer([]byte{}) for _, test := range tests { - printPodDisruptionBudget(&test.pdb, buf, PrintOptions{false, false, false, false, true, false, false, "", []string{}}) + printPodDisruptionBudget(&test.pdb, buf, PrintOptions{}) if buf.String() != test.expect { t.Fatalf("Expected: %s, got: %s", test.expect, buf.String()) }