From d60a0ddcc1784b2dd807cc8e4d4f3deb0c074171 Mon Sep 17 00:00:00 2001 From: Jhon Honce Date: Wed, 28 Oct 2020 11:03:09 -0700 Subject: Restore --format table header support Signed-off-by: Jhon Honce --- cmd/podman/containers/ps.go | 138 ++++++++++++++++++++++++++------------ cmd/podman/containers/stats.go | 17 ++--- cmd/podman/images/history.go | 3 +- cmd/podman/images/list.go | 13 ++-- cmd/podman/images/search.go | 19 ++++-- cmd/podman/networks/list.go | 46 +++++++------ cmd/podman/parse/template.go | 22 ++++++ cmd/podman/parse/template_test.go | 30 +++++++++ cmd/podman/pods/ps.go | 9 ++- cmd/podman/pods/stats.go | 3 +- cmd/podman/system/df.go | 28 +++----- cmd/podman/volumes/list.go | 5 +- 12 files changed, 221 insertions(+), 112 deletions(-) create mode 100644 cmd/podman/parse/template.go create mode 100644 cmd/podman/parse/template_test.go (limited to 'cmd') diff --git a/cmd/podman/containers/ps.go b/cmd/podman/containers/ps.go index 90f4db19c..7da430bc6 100644 --- a/cmd/podman/containers/ps.go +++ b/cmd/podman/containers/ps.go @@ -12,6 +12,7 @@ import ( tm "github.com/buger/goterm" "github.com/containers/common/pkg/report" + "github.com/containers/podman/v2/cmd/podman/parse" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/utils" "github.com/containers/podman/v2/cmd/podman/validate" @@ -40,9 +41,8 @@ var ( listOpts = entities.ContainerListOptions{ Filters: make(map[string][]string), } - filters []string - noTrunc bool - defaultHeaders = "CONTAINER ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tPORTS\tNAMES" + filters []string + noTrunc bool ) func init() { @@ -91,10 +91,6 @@ func checkFlags(c *cobra.Command) error { if listOpts.Size || listOpts.Namespace { return errors.Errorf("quiet conflicts with size and namespace") } - if c.Flag("format").Changed && !report.IsJSON(listOpts.Format) { - // Quiet is overridden by Go template output. - listOpts.Quiet = false - } } // Size and namespace conflict with each other if listOpts.Size && listOpts.Namespace { @@ -155,7 +151,7 @@ func getResponses() ([]entities.ListContainer, error) { return responses, nil } -func ps(cmd *cobra.Command, args []string) error { +func ps(cmd *cobra.Command, _ []string) error { if err := checkFlags(cmd); err != nil { return err } @@ -180,24 +176,22 @@ func ps(cmd *cobra.Command, args []string) error { switch { case report.IsJSON(listOpts.Format): return jsonOut(listContainers) - case listOpts.Quiet: + case listOpts.Quiet && !cmd.Flags().Changed("format"): return quietOut(listContainers) } - // Output table Watch > 0 will refresh screen responses := make([]psReporter, 0, len(listContainers)) for _, r := range listContainers { responses = append(responses, psReporter{r}) } - var headers, format string + hdrs, format := createPsOut() if cmd.Flags().Changed("format") { - headers = "" format = report.NormalizeFormat(listOpts.Format) - } else { - headers, format = createPsOut() + format = parse.EnforceRange(format) } - format = headers + "{{range . }}" + format + "{{end}}" + ns := strings.NewReplacer(".Namespaces.", ".") + format = ns.Replace(format) tmpl, err := template.New("listContainers").Parse(format) if err != nil { @@ -206,13 +200,19 @@ func ps(cmd *cobra.Command, args []string) error { w := tabwriter.NewWriter(os.Stdout, 8, 2, 2, ' ', 0) defer w.Flush() - if listOpts.Watch > 0 { - for { - var responses []psReporter - tm.Clear() - tm.MoveCursor(1, 1) - tm.Flush() + headers := func() error { return nil } + if !(listOpts.Quiet || cmd.Flags().Changed("format")) { + headers = func() error { + return tmpl.Execute(w, hdrs) + } + } + switch { + // Output table Watch > 0 will refresh screen + case listOpts.Watch > 0: + // responses will grow to the largest number of processes reported on, but will not thrash the gc + var responses []psReporter + for ; ; responses = responses[:0] { if ctnrs, err := getResponses(); err != nil { return err } else { @@ -221,18 +221,27 @@ func ps(cmd *cobra.Command, args []string) error { } } + tm.Clear() + tm.MoveCursor(1, 1) + tm.Flush() + + if err := headers(); err != nil { + return err + } if err := tmpl.Execute(w, responses); err != nil { return err } if err := w.Flush(); err != nil { + // we usually do not care about Flush() failures but here do not loop if Flush() has failed return err } + time.Sleep(time.Duration(listOpts.Watch) * time.Second) - tm.Clear() - tm.MoveCursor(1, 1) - tm.Flush() } - } else if listOpts.Watch < 1 { + default: + if err := headers(); err != nil { + return err + } if err := tmpl.Execute(w, responses); err != nil { return err } @@ -241,30 +250,36 @@ func ps(cmd *cobra.Command, args []string) error { } // cannot use report.Headers() as it doesn't support structures as fields -func createPsOut() (string, string) { +func createPsOut() ([]map[string]string, string) { + hdrs := report.Headers(psReporter{}, map[string]string{ + "Cgroup": "cgroupns", + "CreatedHuman": "created", + "ID": "container id", + "IPC": "ipc", + "MNT": "mnt", + "NET": "net", + "PIDNS": "pidns", + "Pod": "pod id", + "PodName": "podname", // undo camelcase space break + "UTS": "uts", + "User": "userns", + }) + var row string if listOpts.Namespace { - headers := "CONTAINER ID\tNAMES\tPID\tCGROUPNS\tIPC\tMNT\tNET\tPIDNS\tUSERNS\tUTS\n" - row := "{{.ID}}\t{{.Names}}\t{{.Pid}}\t{{.Namespaces.Cgroup}}\t{{.Namespaces.IPC}}\t{{.Namespaces.MNT}}\t{{.Namespaces.NET}}\t{{.Namespaces.PIDNS}}\t{{.Namespaces.User}}\t{{.Namespaces.UTS}}\n" - return headers, row - } - headers := defaultHeaders - row += "{{.ID}}" - row += "\t{{.Image}}\t{{.Command}}\t{{.CreatedHuman}}\t{{.Status}}\t{{.Ports}}\t{{.Names}}" + row = "{{.ID}}\t{{.Names}}\t{{.Pid}}\t{{.Namespaces.Cgroup}}\t{{.Namespaces.IPC}}\t{{.Namespaces.MNT}}\t{{.Namespaces.NET}}\t{{.Namespaces.PIDNS}}\t{{.Namespaces.User}}\t{{.Namespaces.UTS}}" + } else { + row = "{{.ID}}\t{{.Image}}\t{{.Command}}\t{{.CreatedHuman}}\t{{.Status}}\t{{.Ports}}\t{{.Names}}" - if listOpts.Pod { - headers += "\tPOD ID\tPODNAME" - row += "\t{{.Pod}}\t{{.PodName}}" - } + if listOpts.Pod { + row += "\t{{.Pod}}\t{{.PodName}}" + } - if listOpts.Size { - headers += "\tSIZE" - row += "\t{{.Size}}" + if listOpts.Size { + row += "\t{{.Size}}" + } } - - headers = report.NormalizeFormat(headers) - row = report.NormalizeFormat(row) - return headers, row + return hdrs, "{{range .}}" + row + "\n{{end}}" } type psReporter struct { @@ -367,6 +382,41 @@ func (l psReporter) CreatedHuman() string { return units.HumanDuration(time.Since(time.Unix(l.Created, 0))) + " ago" } +// Cgroup exposes .Namespaces.Cgroup +func (l psReporter) Cgroup() string { + return l.Namespaces.Cgroup +} + +// IPC exposes .Namespaces.IPC +func (l psReporter) IPC() string { + return l.Namespaces.IPC +} + +// MNT exposes .Namespaces.MNT +func (l psReporter) MNT() string { + return l.Namespaces.MNT +} + +// NET exposes .Namespaces.NET +func (l psReporter) NET() string { + return l.Namespaces.NET +} + +// PIDNS exposes .Namespaces.PIDNS +func (l psReporter) PIDNS() string { + return l.Namespaces.PIDNS +} + +// User exposes .Namespaces.User +func (l psReporter) User() string { + return l.Namespaces.User +} + +// UTS exposes .Namespaces.UTS +func (l psReporter) UTS() string { + return l.Namespaces.UTS +} + // portsToString converts the ports used to a string of the from "port1, port2" // and also groups a continuous list of ports into a readable format. func portsToString(ports []ocicni.PortMapping) string { diff --git a/cmd/podman/containers/stats.go b/cmd/podman/containers/stats.go index 85e7a1e82..bfab469ca 100644 --- a/cmd/podman/containers/stats.go +++ b/cmd/podman/containers/stats.go @@ -8,6 +8,7 @@ import ( tm "github.com/buger/goterm" "github.com/containers/common/pkg/report" + "github.com/containers/podman/v2/cmd/podman/parse" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/validate" "github.com/containers/podman/v2/libpod/define" @@ -58,8 +59,7 @@ type statsOptionsCLI struct { } var ( - statsOptions statsOptionsCLI - defaultStatsRow = "{{.ID}}\t{{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.MemPerc}}\t{{.NetIO}}\t{{.BlockIO}}\t{{.PIDS}}\n" + statsOptions statsOptionsCLI ) func statFlags(flags *pflag.FlagSet) { @@ -159,19 +159,19 @@ func outputStats(reports []define.ContainerStats) error { if report.IsJSON(statsOptions.Format) { return outputJSON(stats) } - format := defaultStatsRow - + format := "{{.ID}}\t{{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.MemPerc}}\t{{.NetIO}}\t{{.BlockIO}}\t{{.PIDS}}\n" if len(statsOptions.Format) > 0 { format = report.NormalizeFormat(statsOptions.Format) - } else if len(statsOptions.Format) < 1 { - format = defaultStatsRow } - format = "{{range . }}" + format + "{{end}}" + format = parse.EnforceRange(format) + tmpl, err := template.New("stats").Parse(format) if err != nil { return err } w := tabwriter.NewWriter(os.Stdout, 8, 2, 2, ' ', 0) + defer w.Flush() + if len(statsOptions.Format) < 1 { if err := tmpl.Execute(w, headers); err != nil { return err @@ -180,9 +180,6 @@ func outputStats(reports []define.ContainerStats) error { if err := tmpl.Execute(w, stats); err != nil { return err } - if err := w.Flush(); err != nil { - return err - } return nil } diff --git a/cmd/podman/images/history.go b/cmd/podman/images/history.go index 3075218d1..e9751b365 100644 --- a/cmd/podman/images/history.go +++ b/cmd/podman/images/history.go @@ -11,6 +11,7 @@ import ( "unicode" "github.com/containers/common/pkg/report" + "github.com/containers/podman/v2/cmd/podman/parse" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/docker/go-units" @@ -119,7 +120,7 @@ func history(cmd *cobra.Command, args []string) error { case opts.quiet: row = "{{.ID}}\n" } - format := "{{range . }}" + row + "{{end}}" + format := parse.EnforceRange(row) tmpl, err := template.New("report").Parse(format) if err != nil { diff --git a/cmd/podman/images/list.go b/cmd/podman/images/list.go index 489b15086..e24631b24 100644 --- a/cmd/podman/images/list.go +++ b/cmd/podman/images/list.go @@ -12,6 +12,7 @@ import ( "github.com/containers/common/pkg/report" "github.com/containers/image/v5/docker/reference" + "github.com/containers/podman/v2/cmd/podman/parse" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/docker/go-units" @@ -105,10 +106,10 @@ func images(cmd *cobra.Command, args []string) error { return err } switch { - case listFlag.quiet: - return writeID(imgs) case report.IsJSON(listFlag.format): return writeJSON(imgs) + case listFlag.quiet: + return writeID(imgs) default: if cmd.Flag("format").Changed { listFlag.noHeading = true // V1 compatibility @@ -171,9 +172,13 @@ func writeTemplate(imgs []imageReporter) error { } else { row = report.NormalizeFormat(listFlag.format) } + format := parse.EnforceRange(row) + + tmpl, err := template.New("list").Parse(format) + if err != nil { + return err + } - format := "{{range . }}" + row + "{{end}}" - tmpl := template.Must(template.New("list").Parse(format)) w := tabwriter.NewWriter(os.Stdout, 8, 2, 2, ' ', 0) defer w.Flush() diff --git a/cmd/podman/images/search.go b/cmd/podman/images/search.go index b1a1442a6..66256aaa0 100644 --- a/cmd/podman/images/search.go +++ b/cmd/podman/images/search.go @@ -8,6 +8,7 @@ import ( "github.com/containers/common/pkg/auth" "github.com/containers/common/pkg/report" "github.com/containers/image/v5/types" + "github.com/containers/podman/v2/cmd/podman/parse" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/pkg/errors" @@ -130,26 +131,30 @@ func imageSearch(cmd *cobra.Command, args []string) error { } hdrs := report.Headers(entities.ImageSearchReport{}, nil) - row := "{{.Index}}\t{{.Name}}\t{{.Description}}\t{{.Stars}}\t{{.Official}}\t{{.Automated}}\n" - if searchOptions.ListTags { + renderHeaders := true + var row string + switch { + case searchOptions.ListTags: if len(searchOptions.Filters) != 0 { return errors.Errorf("filters are not applicable to list tags result") } row = "{{.Name}}\t{{.Tag}}\n" - } - if cmd.Flags().Changed("format") { + case cmd.Flags().Changed("format"): + renderHeaders = parse.HasTable(searchOptions.Format) row = report.NormalizeFormat(searchOptions.Format) + default: + row = "{{.Index}}\t{{.Name}}\t{{.Description}}\t{{.Stars}}\t{{.Official}}\t{{.Automated}}\n" } - row = "{{range .}}" + row + "{{end}}" + format := parse.EnforceRange(row) - tmpl, err := template.New("search").Parse(row) + tmpl, err := template.New("search").Parse(format) if err != nil { return err } w := tabwriter.NewWriter(os.Stdout, 8, 2, 2, ' ', 0) defer w.Flush() - if !cmd.Flags().Changed("format") { + if renderHeaders { if err := tmpl.Execute(w, hdrs); err != nil { return errors.Wrapf(err, "failed to write search column headers") } diff --git a/cmd/podman/networks/list.go b/cmd/podman/networks/list.go index 532af631e..f68e4ed75 100644 --- a/cmd/podman/networks/list.go +++ b/cmd/podman/networks/list.go @@ -8,6 +8,8 @@ import ( "text/tabwriter" "text/template" + "github.com/containers/common/pkg/report" + "github.com/containers/podman/v2/cmd/podman/parse" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/validate" "github.com/containers/podman/v2/libpod/network" @@ -30,8 +32,6 @@ var ( var ( networkListOptions entities.NetworkListOptions - headers = "NAME\tVERSION\tPLUGINS\n" - defaultListRow = "{{.Name}}\t{{.Version}}\t{{.Plugins}}\n" ) func networkListFlags(flags *pflag.FlagSet) { @@ -66,13 +66,12 @@ func networkList(cmd *cobra.Command, args []string) error { return err } - // quiet means we only print the network names - if networkListOptions.Quiet { - return quietOut(responses) - } - - if strings.ToLower(networkListOptions.Format) == "json" { + switch { + case report.IsJSON(networkListOptions.Format): return jsonOut(responses) + case networkListOptions.Quiet: + // quiet means we only print the network names + return quietOut(responses) } nlprs := make([]ListPrintReports, 0, len(responses)) @@ -80,27 +79,32 @@ func networkList(cmd *cobra.Command, args []string) error { nlprs = append(nlprs, ListPrintReports{r}) } - row := networkListOptions.Format - if len(row) < 1 { - row = defaultListRow - } - if !strings.HasSuffix(row, "\n") { - row += "\n" + headers := report.Headers(ListPrintReports{}, map[string]string{ + "CNIVersion": "version", + "Plugins": "plugins", + }) + renderHeaders := true + row := "{{.Name}}\t{{.Version}}\t{{.Plugins}}\n" + if cmd.Flags().Changed("format") { + renderHeaders = parse.HasTable(networkListOptions.Format) + row = report.NormalizeFormat(networkListOptions.Format) } + format := parse.EnforceRange(row) - format := "{{range . }}" + row + "{{end}}" - if !cmd.Flag("format").Changed { - format = headers + format - } tmpl, err := template.New("listNetworks").Parse(format) if err != nil { return err } w := tabwriter.NewWriter(os.Stdout, 8, 2, 2, ' ', 0) - if err := tmpl.Execute(w, nlprs); err != nil { - return err + defer w.Flush() + + if renderHeaders { + if err := tmpl.Execute(w, headers); err != nil { + return err + } + } - return w.Flush() + return tmpl.Execute(w, nlprs) } func quietOut(responses []*entities.NetworkListReport) error { diff --git a/cmd/podman/parse/template.go b/cmd/podman/parse/template.go new file mode 100644 index 000000000..0b80f1b3a --- /dev/null +++ b/cmd/podman/parse/template.go @@ -0,0 +1,22 @@ +package parse + +import ( + "regexp" + "strings" +) + +var rangeRegex = regexp.MustCompile(`{{\s*range\s*\.\s*}}.*{{\s*end\s*}}`) + +// TODO move to github.com/containers/common/pkg/report +// EnforceRange ensures that the format string contains a range +func EnforceRange(format string) string { + if !rangeRegex.MatchString(format) { + return "{{range .}}" + format + "{{end}}" + } + return format +} + +// EnforceRange ensures that the format string contains a range +func HasTable(format string) bool { + return strings.HasPrefix(format, "table ") +} diff --git a/cmd/podman/parse/template_test.go b/cmd/podman/parse/template_test.go new file mode 100644 index 000000000..7880d9bec --- /dev/null +++ b/cmd/podman/parse/template_test.go @@ -0,0 +1,30 @@ +package parse + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestEnforceRange(t *testing.T) { + tests := []struct { + input string + expected string + }{ + {"{{range .}}{{.ID}}{{end}}", "{{range .}}{{.ID}}{{end}}"}, + {"{{.ID}}", "{{range .}}{{.ID}}{{end}}"}, + {"{{ range . }}{{ .ID }}{{ end }}", "{{ range . }}{{ .ID }}{{ end }}"}, + // EnforceRange does not verify syntax or semantics, that will happen later + {"{{range .}}{{.ID}}", "{{range .}}{{range .}}{{.ID}}{{end}}"}, + {".ID", "{{range .}}.ID{{end}}"}, + } + + for _, tc := range tests { + tc := tc + label := "TestEnforceRange_" + tc.input + t.Run(label, func(t *testing.T) { + t.Parallel() + assert.Equal(t, tc.expected, EnforceRange(tc.input)) + }) + } +} diff --git a/cmd/podman/pods/ps.go b/cmd/podman/pods/ps.go index 688108c1a..40fc71780 100644 --- a/cmd/podman/pods/ps.go +++ b/cmd/podman/pods/ps.go @@ -11,6 +11,7 @@ import ( "time" "github.com/containers/common/pkg/report" + "github.com/containers/podman/v2/cmd/podman/parse" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/validate" "github.com/containers/podman/v2/pkg/domain/entities" @@ -113,20 +114,22 @@ func pods(cmd *cobra.Command, _ []string) error { "Created": "CREATED", "InfraID": "INFRA ID", }) + renderHeaders := true row := podPsFormat() if cmd.Flags().Changed("format") { + renderHeaders = parse.HasTable(psInput.Format) row = report.NormalizeFormat(psInput.Format) } - row = "{{range . }}" + row + "{{end}}" + format := parse.EnforceRange(row) - tmpl, err := template.New("listPods").Parse(row) + tmpl, err := template.New("listPods").Parse(format) if err != nil { return err } w := tabwriter.NewWriter(os.Stdout, 8, 2, 2, ' ', 0) defer w.Flush() - if !psInput.Quiet && !cmd.Flag("format").Changed { + if renderHeaders { if err := tmpl.Execute(w, headers); err != nil { return err } diff --git a/cmd/podman/pods/stats.go b/cmd/podman/pods/stats.go index 338f13d3e..c5d1e7f07 100644 --- a/cmd/podman/pods/stats.go +++ b/cmd/podman/pods/stats.go @@ -10,6 +10,7 @@ import ( "github.com/buger/goterm" "github.com/containers/common/pkg/report" + "github.com/containers/podman/v2/cmd/podman/parse" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/validate" "github.com/containers/podman/v2/pkg/domain/entities" @@ -135,7 +136,7 @@ func printFormattedPodStatsLines(headerNames []map[string]string, row string, st return nil } - row = "{{range .}}" + row + "{{end}}" + row = parse.EnforceRange(row) tmpl, err := template.New("pod stats").Parse(row) if err != nil { diff --git a/cmd/podman/system/df.go b/cmd/podman/system/df.go index b11167938..fbdf274fb 100644 --- a/cmd/podman/system/df.go +++ b/cmd/podman/system/df.go @@ -9,6 +9,7 @@ import ( "time" "github.com/containers/common/pkg/report" + "github.com/containers/podman/v2/cmd/podman/parse" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/validate" "github.com/containers/podman/v2/pkg/domain/entities" @@ -55,7 +56,7 @@ func df(cmd *cobra.Command, args []string) error { w := tabwriter.NewWriter(os.Stdout, 8, 2, 2, ' ', 0) if dfOptions.Verbose { - return printVerbose(cmd, w, reports) + return printVerbose(w, cmd, reports) } return printSummary(w, cmd, reports) } @@ -131,20 +132,16 @@ func printSummary(w *tabwriter.Writer, cmd *cobra.Command, reports *entities.Sys "Size": "SIZE", "Reclaimable": "RECLAIMABLE", }) - row := "{{.Type}}\t{{.Total}}\t{{.Active}}\t{{.Size}}\t{{.Reclaimable}}\n" if cmd.Flags().Changed("format") { row = report.NormalizeFormat(dfOptions.Format) } - row = "{{range . }}" + row + "{{end}}" - - return writeTemplate(cmd, w, hdrs, row, dfSummaries) + return writeTemplate(w, cmd, hdrs, row, dfSummaries) } -func printVerbose(cmd *cobra.Command, w *tabwriter.Writer, reports *entities.SystemDfReport) error { +func printVerbose(w *tabwriter.Writer, cmd *cobra.Command, reports *entities.SystemDfReport) error { defer w.Flush() - // Images fmt.Fprint(w, "Images space usage:\n\n") // convert to dfImage for output dfImages := make([]*dfImage, 0, len(reports.Images)) @@ -157,14 +154,11 @@ func printVerbose(cmd *cobra.Command, w *tabwriter.Writer, reports *entities.Sys "UniqueSize": "UNIQUE SIZE", }) imageRow := "{{.Repository}}\t{{.Tag}}\t{{.ImageID}}\t{{.Created}}\t{{.Size}}\t{{.SharedSize}}\t{{.UniqueSize}}\t{{.Containers}}\n" - format := "{{range . }}" + imageRow + "{{end}}" - if err := writeTemplate(cmd, w, hdrs, format, dfImages); err != nil { + if err := writeTemplate(w, cmd, hdrs, imageRow, dfImages); err != nil { return nil } - // Containers fmt.Fprint(w, "\nContainers space usage:\n\n") - // convert to dfContainers for output dfContainers := make([]*dfContainer, 0, len(reports.Containers)) for _, d := range reports.Containers { @@ -176,14 +170,11 @@ func printVerbose(cmd *cobra.Command, w *tabwriter.Writer, reports *entities.Sys "RWSize": "SIZE", }) containerRow := "{{.ContainerID}}\t{{.Image}}\t{{.Command}}\t{{.LocalVolumes}}\t{{.RWSize}}\t{{.Created}}\t{{.Status}}\t{{.Names}}\n" - format = "{{range . }}" + containerRow + "{{end}}" - if err := writeTemplate(cmd, w, hdrs, format, dfContainers); err != nil { + if err := writeTemplate(w, cmd, hdrs, containerRow, dfContainers); err != nil { return nil } - // Volumes fmt.Fprint(w, "\nLocal Volumes space usage:\n\n") - dfVolumes := make([]*dfVolume, 0, len(reports.Volumes)) // convert to dfVolume for output for _, d := range reports.Volumes { @@ -193,14 +184,13 @@ func printVerbose(cmd *cobra.Command, w *tabwriter.Writer, reports *entities.Sys "VolumeName": "VOLUME NAME", }) volumeRow := "{{.VolumeName}}\t{{.Links}}\t{{.Size}}\n" - format = "{{range . }}" + volumeRow + "{{end}}" - return writeTemplate(cmd, w, hdrs, format, dfVolumes) + return writeTemplate(w, cmd, hdrs, volumeRow, dfVolumes) } -func writeTemplate(cmd *cobra.Command, w *tabwriter.Writer, hdrs []map[string]string, format string, - output interface{}) error { +func writeTemplate(w *tabwriter.Writer, cmd *cobra.Command, hdrs []map[string]string, format string, output interface{}) error { defer w.Flush() + format = parse.EnforceRange(format) tmpl, err := template.New("df").Parse(format) if err != nil { return err diff --git a/cmd/podman/volumes/list.go b/cmd/podman/volumes/list.go index b3b2b8ea1..ce0b7997d 100644 --- a/cmd/podman/volumes/list.go +++ b/cmd/podman/volumes/list.go @@ -9,6 +9,7 @@ import ( "text/template" "github.com/containers/common/pkg/report" + "github.com/containers/podman/v2/cmd/podman/parse" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/validate" "github.com/containers/podman/v2/pkg/domain/entities" @@ -91,9 +92,9 @@ func outputTemplate(cmd *cobra.Command, responses []*entities.VolumeListReport) if cliOpts.Quiet { row = "{{.Name}}\n" } - row = "{{range . }}" + row + "{{end}}" + format := parse.EnforceRange(row) - tmpl, err := template.New("list volume").Parse(row) + tmpl, err := template.New("list volume").Parse(format) if err != nil { return err } -- cgit v1.2.3-54-g00ecf