From 61792de36ea2ea98f6e3aef3821d1b15beebf9e0 Mon Sep 17 00:00:00 2001 From: Jhon Honce Date: Thu, 18 Nov 2021 09:03:00 -0700 Subject: Refactor podman image command output Leverage new report.Formatter allowing better compatibility from podman command output. Follow on PR's will cover containers, etc. See #10974 Signed-off-by: Jhon Honce --- cmd/podman/images/history.go | 36 +++++++++++++++--------------------- cmd/podman/images/list.go | 33 +++++++++++++-------------------- cmd/podman/images/mount.go | 15 ++++----------- cmd/podman/images/search.go | 35 +++++++++++++---------------------- cmd/podman/images/trust_show.go | 27 +++++++++------------------ cmd/podman/inspect/inspect.go | 4 +++- cmd/podman/machine/list.go | 14 +++++++++----- cmd/podman/networks/list.go | 16 +++++++++------- cmd/podman/pods/inspect.go | 2 ++ cmd/podman/pods/ps.go | 3 ++- cmd/podman/pods/stats.go | 8 +++----- cmd/podman/secrets/list.go | 5 ++++- cmd/podman/system/connection/list.go | 9 ++++----- cmd/podman/system/version.go | 2 +- cmd/podman/volumes/list.go | 9 +++++++-- test/system/010-images.bats | 2 -- test/system/110-history.bats | 8 ++++++++ 17 files changed, 106 insertions(+), 122 deletions(-) diff --git a/cmd/podman/images/history.go b/cmd/podman/images/history.go index cc7b1b4eb..ac693a87b 100644 --- a/cmd/podman/images/history.go +++ b/cmd/podman/images/history.go @@ -1,7 +1,6 @@ package images import ( - "context" "fmt" "os" "strings" @@ -79,7 +78,7 @@ func historyFlags(cmd *cobra.Command) { } func history(cmd *cobra.Command, args []string) error { - results, err := registry.ImageEngine().History(context.Background(), args[0], entities.ImageHistoryOptions{}) + results, err := registry.ImageEngine().History(registry.Context(), args[0], entities.ImageHistoryOptions{}) if err != nil { return err } @@ -111,37 +110,32 @@ func history(cmd *cobra.Command, args []string) error { hr = append(hr, historyReporter{l}) } - hdrs := report.Headers(historyReporter{}, map[string]string{ - "CreatedBy": "CREATED BY", - }) + rpt := report.New(os.Stdout, cmd.Name()) + defer rpt.Flush() - // Defaults - row := "{{.ID}}\t{{.Created}}\t{{.CreatedBy}}\t{{.Size}}\t{{.Comment}}\n" switch { - case cmd.Flags().Changed("format"): - row = report.NormalizeFormat(opts.format) case opts.quiet: - row = "{{.ID}}\n" + rpt, err = rpt.Parse(report.OriginUser, "{{range .}}{{.ID}}\n{{end -}}") + case cmd.Flags().Changed("format"): + rpt, err = rpt.Parse(report.OriginUser, cmd.Flag("format").Value.String()) + default: + format := "{{range .}}{{.ID}}\t{{.Created}}\t{{.CreatedBy}}\t{{.Size}}\t{{.Comment}}\n{{end -}}" + rpt, err = rpt.Parse(report.OriginPodman, format) } - format := report.EnforceRange(row) - - tmpl, err := report.NewTemplate("history").Parse(format) if err != nil { return err } - w, err := report.NewWriterDefault(os.Stdout) - if err != nil { - return err - } - defer w.Flush() + if rpt.RenderHeaders { + hdrs := report.Headers(historyReporter{}, map[string]string{ + "CreatedBy": "CREATED BY", + }) - if !opts.quiet && !cmd.Flags().Changed("format") { - if err := tmpl.Execute(w, hdrs); err != nil { + if err := rpt.Execute(hdrs); err != nil { return errors.Wrapf(err, "failed to write report column headers") } } - return tmpl.Execute(w, hr) + return rpt.Execute(hr) } type historyReporter struct { diff --git a/cmd/podman/images/list.go b/cmd/podman/images/list.go index 01286daf2..61514daa7 100644 --- a/cmd/podman/images/list.go +++ b/cmd/podman/images/list.go @@ -117,7 +117,7 @@ func images(cmd *cobra.Command, args []string) error { listOptions.Filter = append(listOptions.Filter, "reference="+args[0]) } - if cmd.Flag("sort").Changed && !sortFields.Contains(listFlag.sort) { + if cmd.Flags().Changed("sort") && !sortFields.Contains(listFlag.sort) { return fmt.Errorf("\"%s\" is not a valid field for sorting. Choose from: %s", listFlag.sort, sortFields.String()) } @@ -140,7 +140,7 @@ func images(cmd *cobra.Command, args []string) error { if cmd.Flags().Changed("format") && !report.HasTable(listFlag.format) { listFlag.noHeading = true } - return writeTemplate(imgs) + return writeTemplate(cmd, imgs) } } @@ -186,38 +186,31 @@ func writeJSON(images []imageReporter) error { return nil } -func writeTemplate(imgs []imageReporter) error { +func writeTemplate(cmd *cobra.Command, imgs []imageReporter) error { hdrs := report.Headers(imageReporter{}, map[string]string{ "ID": "IMAGE ID", "ReadOnly": "R/O", }) - var format string - if listFlag.format == "" { - format = lsFormatFromFlags(listFlag) - } else { - format = report.NormalizeFormat(listFlag.format) - format = report.EnforceRange(format) - } + rpt := report.New(os.Stdout, cmd.Name()) + defer rpt.Flush() - tmpl, err := report.NewTemplate("list").Parse(format) - if err != nil { - return err + var err error + if cmd.Flags().Changed("format") { + rpt, err = rpt.Parse(report.OriginUser, cmd.Flag("format").Value.String()) + } else { + rpt, err = rpt.Parse(report.OriginPodman, lsFormatFromFlags(listFlag)) } - - w, err := report.NewWriterDefault(os.Stdout) if err != nil { return err } - defer w.Flush() - if !listFlag.noHeading { - if err := tmpl.Execute(w, hdrs); err != nil { + if rpt.RenderHeaders && !listFlag.noHeading { + if err := rpt.Execute(hdrs); err != nil { return err } } - - return tmpl.Execute(w, imgs) + return rpt.Execute(imgs) } func sortImages(imageS []*entities.ImageSummary) ([]imageReporter, error) { diff --git a/cmd/podman/images/mount.go b/cmd/podman/images/mount.go index 89c00cb70..cdeb9eecf 100644 --- a/cmd/podman/images/mount.go +++ b/cmd/podman/images/mount.go @@ -87,7 +87,7 @@ func mount(cmd *cobra.Command, args []string) error { case report.IsJSON(mountOpts.Format): return printJSON(reports) case mountOpts.Format == "": - break // default format + break // see default format below default: return errors.Errorf("unknown --format argument: %q", mountOpts.Format) } @@ -97,19 +97,12 @@ func mount(cmd *cobra.Command, args []string) error { mrs = append(mrs, mountReporter{r}) } - row := "{{range . }}{{.ID}}\t{{.Path}}\n{{end -}}" - tmpl, err := report.NewTemplate("mounts").Parse(row) + rpt, err := report.New(os.Stdout, cmd.Name()).Parse(report.OriginPodman, "{{range . }}{{.ID}}\t{{.Path}}\n{{end -}}") if err != nil { return err } - - w, err := report.NewWriterDefault(os.Stdout) - if err != nil { - return err - } - defer w.Flush() - - return tmpl.Execute(w, mrs) + defer rpt.Flush() + return rpt.Execute(mrs) } func printJSON(reports []*entities.ImageMountReport) error { diff --git a/cmd/podman/images/search.go b/cmd/podman/images/search.go index c9a4793aa..0791ac02c 100644 --- a/cmd/podman/images/search.go +++ b/cmd/podman/images/search.go @@ -149,9 +149,9 @@ func imageSearch(cmd *cobra.Command, args []string) error { searchReport[i].Description = d } - hdrs := report.Headers(entities.ImageSearchReport{}, nil) - renderHeaders := true - var row string + rpt := report.New(os.Stdout, cmd.Name()) + defer rpt.Flush() + switch { case searchOptions.ListTags: if len(searchOptions.Filters) != 0 { @@ -161,39 +161,30 @@ func imageSearch(cmd *cobra.Command, args []string) error { listTagsEntries := buildListTagsJSON(searchReport) return printArbitraryJSON(listTagsEntries) } - row = "{{.Name}}\t{{.Tag}}\n" + rpt, err = rpt.Parse(report.OriginPodman, "{{range .}}{{.Name}}\t{{.Tag}}\n{{end -}}") case isJSON: return printArbitraryJSON(searchReport) case cmd.Flags().Changed("format"): - renderHeaders = report.HasTable(searchOptions.Format) - row = report.NormalizeFormat(searchOptions.Format) + rpt, err = rpt.Parse(report.OriginUser, searchOptions.Format) default: - row = "{{.Name}}\t{{.Description}}" + row := "{{.Name}}\t{{.Description}}" if searchOptions.Compatible { row += "\t{{.Stars}}\t{{.Official}}\t{{.Automated}}" } - row += "\n" + row = "{{range . }}" + row + "\n{{end -}}" + rpt, err = rpt.Parse(report.OriginPodman, row) } - format := report.EnforceRange(row) - - tmpl, err := report.NewTemplate("search").Parse(format) if err != nil { return err } - w, err := report.NewWriterDefault(os.Stdout) - if err != nil { - return err - } - defer w.Flush() - - if renderHeaders { - if err := tmpl.Execute(w, hdrs); err != nil { - return errors.Wrapf(err, "failed to write search column headers") + if rpt.RenderHeaders { + hdrs := report.Headers(entities.ImageSearchReport{}, nil) + if err := rpt.Execute(hdrs); err != nil { + return errors.Wrapf(err, "failed to write report column headers") } } - - return tmpl.Execute(w, searchReport) + return rpt.Execute(searchReport) } func printArbitraryJSON(v interface{}) error { diff --git a/cmd/podman/images/trust_show.go b/cmd/podman/images/trust_show.go index c0e56f504..04ea24ca5 100644 --- a/cmd/podman/images/trust_show.go +++ b/cmd/podman/images/trust_show.go @@ -48,11 +48,12 @@ func showTrust(cmd *cobra.Command, args []string) error { if err != nil { return err } - if showTrustOptions.Raw { + + switch { + case showTrustOptions.Raw: fmt.Println(string(trust.Raw)) return nil - } - if showTrustOptions.JSON { + case showTrustOptions.JSON: b, err := json.MarshalIndent(trust.Policies, "", " ") if err != nil { return err @@ -60,23 +61,13 @@ func showTrust(cmd *cobra.Command, args []string) error { fmt.Println(string(b)) return nil } + rpt := report.New(os.Stdout, cmd.Name()) + defer rpt.Flush() - format := "{{range . }}{{.RepoName}}\t{{.Type}}\t{{.GPGId}}\t{{.SignatureStore}}\n{{end -}}" - tmpl, err := report.NewTemplate("list").Parse(format) - if err != nil { - return err - } - - w, err := report.NewWriterDefault(os.Stdout) + rpt, err = rpt.Parse(report.OriginPodman, + "{{range . }}{{.RepoName}}\t{{.Type}}\t{{.GPGId}}\t{{.SignatureStore}}\n{{end -}}") if err != nil { return err } - - if err := tmpl.Execute(w, trust.Policies); err != nil { - return err - } - if err := w.Flush(); err != nil { - return err - } - return nil + return rpt.Execute(trust.Policies) } diff --git a/cmd/podman/inspect/inspect.go b/cmd/podman/inspect/inspect.go index 64b586388..c982b1b7f 100644 --- a/cmd/podman/inspect/inspect.go +++ b/cmd/podman/inspect/inspect.go @@ -215,8 +215,10 @@ func (i *inspector) inspect(namesOrIDs []string) error { case report.IsJSON(i.options.Format) || i.options.Format == "": err = printJSON(data) default: + // Landing here implies user has given a custom --format row := inspectNormalize(i.options.Format) - row = "{{range . }}" + report.NormalizeFormat(row) + "{{end -}}" + row = report.NormalizeFormat(row) + row = report.EnforceRange(row) err = printTmpl(tmpType, row, data) } if err != nil { diff --git a/cmd/podman/machine/list.go b/cmd/podman/machine/list.go index d569f4db0..774ab4fd0 100644 --- a/cmd/podman/machine/list.go +++ b/cmd/podman/machine/list.go @@ -116,7 +116,15 @@ func outputTemplate(cmd *cobra.Command, responses []*machineReporter) error { "DiskSize": "DISK SIZE", }) - row := report.NormalizeFormat(listFlag.format) + var row string + switch { + case cmd.Flags().Changed("format"): + row = cmd.Flag("format").Value.String() + listFlag.noHeading = !report.HasTable(row) + row = report.NormalizeFormat(row) + default: + row = cmd.Flag("format").Value.String() + } format := report.EnforceRange(row) tmpl, err := report.NewTemplate("list").Parse(format) @@ -130,10 +138,6 @@ func outputTemplate(cmd *cobra.Command, responses []*machineReporter) error { } defer w.Flush() - if cmd.Flags().Changed("format") && !report.HasTable(listFlag.format) { - listFlag.noHeading = true - } - if !listFlag.noHeading { if err := tmpl.Execute(w, headers); err != nil { return errors.Wrapf(err, "failed to write report column headers") diff --git a/cmd/podman/networks/list.go b/cmd/podman/networks/list.go index 124a17d5d..6f1a7742a 100644 --- a/cmd/podman/networks/list.go +++ b/cmd/podman/networks/list.go @@ -84,7 +84,7 @@ func networkList(cmd *cobra.Command, args []string) error { // table or other format output default: - err = templateOut(responses, cmd) + err = templateOut(cmd, responses) } return err @@ -105,7 +105,7 @@ func jsonOut(responses []types.Network) error { return nil } -func templateOut(responses []types.Network, cmd *cobra.Command) error { +func templateOut(cmd *cobra.Command, responses []types.Network) error { nlprs := make([]ListPrintReports, 0, len(responses)) for _, r := range responses { nlprs = append(nlprs, ListPrintReports{r}) @@ -120,14 +120,16 @@ func templateOut(responses []types.Network, cmd *cobra.Command) error { }) renderHeaders := report.HasTable(networkListOptions.Format) - var row, format string - if cmd.Flags().Changed("format") { + var row string + switch { + case cmd.Flags().Changed("format"): row = report.NormalizeFormat(networkListOptions.Format) - } else { // 'podman network ls' equivalent to 'podman network ls --format="table {{.ID}} {{.Name}} {{.Version}} {{.Plugins}}" ' - renderHeaders = true + default: + // 'podman network ls' equivalent to 'podman network ls --format="table {{.ID}} {{.Name}} {{.Version}} {{.Plugins}}" ' row = "{{.ID}}\t{{.Name}}\t{{.Driver}}\n" + renderHeaders = true } - format = report.EnforceRange(row) + format := report.EnforceRange(row) tmpl, err := report.NewTemplate("list").Parse(format) if err != nil { diff --git a/cmd/podman/pods/inspect.go b/cmd/podman/pods/inspect.go index 96eaec3b9..d4b8df90e 100644 --- a/cmd/podman/pods/inspect.go +++ b/cmd/podman/pods/inspect.go @@ -64,11 +64,13 @@ func inspect(cmd *cobra.Command, args []string) error { } if report.IsJSON(inspectOptions.Format) { + json.MarshalIndent(responses, "", " ") enc := json.NewEncoder(os.Stdout) enc.SetIndent("", " ") return enc.Encode(responses) } + // cmd.Flags().Changed("format") must be true to reach this code row := report.NormalizeFormat(inspectOptions.Format) t, err := report.NewTemplate("inspect").Parse(row) diff --git a/cmd/podman/pods/ps.go b/cmd/podman/pods/ps.go index 60aadf224..808ec31b3 100644 --- a/cmd/podman/pods/ps.go +++ b/cmd/podman/pods/ps.go @@ -136,11 +136,12 @@ func pods(cmd *cobra.Command, _ []string) error { renderHeaders = report.HasTable(psInput.Format) row = report.NormalizeFormat(psInput.Format) } + format := report.EnforceRange(row) + noHeading, _ := cmd.Flags().GetBool("noheading") if noHeading { renderHeaders = false } - format := report.EnforceRange(row) tmpl, err := report.NewTemplate("list").Parse(format) if err != nil { diff --git a/cmd/podman/pods/stats.go b/cmd/podman/pods/stats.go index ba2c1495b..a7bba3064 100644 --- a/cmd/podman/pods/stats.go +++ b/cmd/podman/pods/stats.go @@ -67,9 +67,7 @@ func stats(cmd *cobra.Command, args []string) error { return err } - row := report.NormalizeFormat(statsOptions.Format) - doJSON := report.IsJSON(row) - + doJSON := report.IsJSON(cmd.Flag("format").Value.String()) headers := report.Headers(entities.PodStatsReport{}, map[string]string{ "CPU": "CPU %", "MemUsage": "MEM USAGE/ LIMIT", @@ -96,6 +94,8 @@ func stats(cmd *cobra.Command, args []string) error { goterm.Flush() } if cmd.Flags().Changed("format") { + row := report.NormalizeFormat(statsOptions.Format) + row = report.EnforceRange(row) if err := printFormattedPodStatsLines(headers, row, reports); err != nil { return err } @@ -143,8 +143,6 @@ func printFormattedPodStatsLines(headerNames []map[string]string, row string, st return nil } - row = report.EnforceRange(row) - tmpl, err := report.NewTemplate("stats").Parse(row) if err != nil { return err diff --git a/cmd/podman/secrets/list.go b/cmd/podman/secrets/list.go index f136de4ab..255d9ae1a 100644 --- a/cmd/podman/secrets/list.go +++ b/cmd/podman/secrets/list.go @@ -71,7 +71,10 @@ func outputTemplate(cmd *cobra.Command, responses []*entities.SecretListReport) "UpdatedAt": "UPDATED", }) - row := report.NormalizeFormat(listFlag.format) + row := cmd.Flag("format").Value.String() + if cmd.Flags().Changed("format") { + row = report.NormalizeFormat(row) + } format := report.EnforceRange(row) tmpl, err := report.NewTemplate("list").Parse(format) diff --git a/cmd/podman/system/connection/list.go b/cmd/podman/system/connection/list.go index f1f7657ad..2710142a8 100644 --- a/cmd/podman/system/connection/list.go +++ b/cmd/podman/system/connection/list.go @@ -82,7 +82,7 @@ func list(cmd *cobra.Command, _ []string) error { return rows[i].Name < rows[j].Name }) - format := "{{.Name}}\t{{.URI}}\t{{.Identity}}\t{{.Default}}\n" + var format string switch { case report.IsJSON(cmd.Flag("format").Value.String()): buf, err := registry.JSONLibrary().MarshalIndent(rows, "", " ") @@ -90,11 +90,10 @@ func list(cmd *cobra.Command, _ []string) error { fmt.Println(string(buf)) } return err + case cmd.Flags().Changed("format"): + format = report.NormalizeFormat(cmd.Flag("format").Value.String()) default: - if cmd.Flag("format").Changed { - format = cmd.Flag("format").Value.String() - format = report.NormalizeFormat(format) - } + format = "{{.Name}}\t{{.URI}}\t{{.Identity}}\t{{.Default}}\n" } format = report.EnforceRange(format) diff --git a/cmd/podman/system/version.go b/cmd/podman/system/version.go index 3443978d6..87b806503 100644 --- a/cmd/podman/system/version.go +++ b/cmd/podman/system/version.go @@ -59,7 +59,7 @@ func version(cmd *cobra.Command, args []string) error { } defer w.Flush() - if cmd.Flag("format").Changed { + if cmd.Flags().Changed("format") { row := report.NormalizeFormat(versionFormat) tmpl, err := report.NewTemplate("version 2.0.0").Parse(row) if err != nil { diff --git a/cmd/podman/volumes/list.go b/cmd/podman/volumes/list.go index 0243054af..c372527de 100644 --- a/cmd/podman/volumes/list.go +++ b/cmd/podman/volumes/list.go @@ -97,9 +97,14 @@ func outputTemplate(cmd *cobra.Command, responses []*entities.VolumeListReport) "Name": "VOLUME NAME", }) - row := report.NormalizeFormat(cliOpts.Format) - if cliOpts.Quiet { + var row string + switch { + case cliOpts.Quiet: row = "{{.Name}}\n" + case cmd.Flags().Changed("format"): + row = report.NormalizeFormat(cliOpts.Format) + default: + row = cmd.Flag("format").Value.String() } format := report.EnforceRange(row) diff --git a/test/system/010-images.bats b/test/system/010-images.bats index 1e9d5f181..9de31f96c 100644 --- a/test/system/010-images.bats +++ b/test/system/010-images.bats @@ -221,9 +221,7 @@ Labels.created_at | 20[0-9-]\\\+T[0-9:]\\\+Z iid=${output:0:12} # Run the test: this will output three column-aligned rows. Test them. - # Tab character (\t) should have the same effect as the 'table' directive _run_format_test 'table' 'table {{.Repository}} {{.Tag}} {{.ID}}' - _run_format_test 'tabs' '{{.Repository}}\t{{.Tag}}\t{{.ID}}' # Clean up. run_podman rmi ${aaa_name}:${aaa_tag} ${zzz_name}:${zzz_tag} diff --git a/test/system/110-history.bats b/test/system/110-history.bats index 75c15b088..0f6d75cb3 100644 --- a/test/system/110-history.bats +++ b/test/system/110-history.bats @@ -21,6 +21,14 @@ load helpers done } +@test "podman history - custom format" { + run_podman history --format "{{.ID}}\t{{.ID}}" $IMAGE + od -c <<<$output + while IFS= read -r row; do + is "$row" ".* .*$" + done <<<$output +} + @test "podman history - json" { # Sigh. Timestamp in .created can be '...Z' or '...-06:00' tests=" -- cgit v1.2.3-54-g00ecf