Merge pull request #12455 from jwhonce/issues/10974

Refactor podman image command output
This commit is contained in:
OpenShift Merge Robot 2021-12-02 19:28:48 +01:00 committed by GitHub
commit 4ff0ba4c87
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 106 additions and 122 deletions

View File

@ -1,7 +1,6 @@
package images package images
import ( import (
"context"
"fmt" "fmt"
"os" "os"
"strings" "strings"
@ -79,7 +78,7 @@ func historyFlags(cmd *cobra.Command) {
} }
func history(cmd *cobra.Command, args []string) error { 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 { if err != nil {
return err return err
} }
@ -111,37 +110,32 @@ func history(cmd *cobra.Command, args []string) error {
hr = append(hr, historyReporter{l}) hr = append(hr, historyReporter{l})
} }
hdrs := report.Headers(historyReporter{}, map[string]string{ rpt := report.New(os.Stdout, cmd.Name())
"CreatedBy": "CREATED BY", defer rpt.Flush()
})
// Defaults
row := "{{.ID}}\t{{.Created}}\t{{.CreatedBy}}\t{{.Size}}\t{{.Comment}}\n"
switch { switch {
case cmd.Flags().Changed("format"):
row = report.NormalizeFormat(opts.format)
case opts.quiet: 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 { if err != nil {
return err return err
} }
w, err := report.NewWriterDefault(os.Stdout) if rpt.RenderHeaders {
if err != nil { hdrs := report.Headers(historyReporter{}, map[string]string{
return err "CreatedBy": "CREATED BY",
} })
defer w.Flush()
if !opts.quiet && !cmd.Flags().Changed("format") { if err := rpt.Execute(hdrs); err != nil {
if err := tmpl.Execute(w, hdrs); err != nil {
return errors.Wrapf(err, "failed to write report column headers") return errors.Wrapf(err, "failed to write report column headers")
} }
} }
return tmpl.Execute(w, hr) return rpt.Execute(hr)
} }
type historyReporter struct { type historyReporter struct {

View File

@ -117,7 +117,7 @@ func images(cmd *cobra.Command, args []string) error {
listOptions.Filter = append(listOptions.Filter, "reference="+args[0]) 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", return fmt.Errorf("\"%s\" is not a valid field for sorting. Choose from: %s",
listFlag.sort, sortFields.String()) 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) { if cmd.Flags().Changed("format") && !report.HasTable(listFlag.format) {
listFlag.noHeading = true listFlag.noHeading = true
} }
return writeTemplate(imgs) return writeTemplate(cmd, imgs)
} }
} }
@ -186,38 +186,31 @@ func writeJSON(images []imageReporter) error {
return nil return nil
} }
func writeTemplate(imgs []imageReporter) error { func writeTemplate(cmd *cobra.Command, imgs []imageReporter) error {
hdrs := report.Headers(imageReporter{}, map[string]string{ hdrs := report.Headers(imageReporter{}, map[string]string{
"ID": "IMAGE ID", "ID": "IMAGE ID",
"ReadOnly": "R/O", "ReadOnly": "R/O",
}) })
var format string rpt := report.New(os.Stdout, cmd.Name())
if listFlag.format == "" { defer rpt.Flush()
format = lsFormatFromFlags(listFlag)
var err error
if cmd.Flags().Changed("format") {
rpt, err = rpt.Parse(report.OriginUser, cmd.Flag("format").Value.String())
} else { } else {
format = report.NormalizeFormat(listFlag.format) rpt, err = rpt.Parse(report.OriginPodman, lsFormatFromFlags(listFlag))
format = report.EnforceRange(format)
} }
tmpl, err := report.NewTemplate("list").Parse(format)
if err != nil { if err != nil {
return err return err
} }
w, err := report.NewWriterDefault(os.Stdout) if rpt.RenderHeaders && !listFlag.noHeading {
if err != nil { if err := rpt.Execute(hdrs); err != nil {
return err
}
defer w.Flush()
if !listFlag.noHeading {
if err := tmpl.Execute(w, hdrs); err != nil {
return err return err
} }
} }
return rpt.Execute(imgs)
return tmpl.Execute(w, imgs)
} }
func sortImages(imageS []*entities.ImageSummary) ([]imageReporter, error) { func sortImages(imageS []*entities.ImageSummary) ([]imageReporter, error) {

View File

@ -87,7 +87,7 @@ func mount(cmd *cobra.Command, args []string) error {
case report.IsJSON(mountOpts.Format): case report.IsJSON(mountOpts.Format):
return printJSON(reports) return printJSON(reports)
case mountOpts.Format == "": case mountOpts.Format == "":
break // default format break // see default format below
default: default:
return errors.Errorf("unknown --format argument: %q", mountOpts.Format) 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}) mrs = append(mrs, mountReporter{r})
} }
row := "{{range . }}{{.ID}}\t{{.Path}}\n{{end -}}" rpt, err := report.New(os.Stdout, cmd.Name()).Parse(report.OriginPodman, "{{range . }}{{.ID}}\t{{.Path}}\n{{end -}}")
tmpl, err := report.NewTemplate("mounts").Parse(row)
if err != nil { if err != nil {
return err return err
} }
defer rpt.Flush()
w, err := report.NewWriterDefault(os.Stdout) return rpt.Execute(mrs)
if err != nil {
return err
}
defer w.Flush()
return tmpl.Execute(w, mrs)
} }
func printJSON(reports []*entities.ImageMountReport) error { func printJSON(reports []*entities.ImageMountReport) error {

View File

@ -149,9 +149,9 @@ func imageSearch(cmd *cobra.Command, args []string) error {
searchReport[i].Description = d searchReport[i].Description = d
} }
hdrs := report.Headers(entities.ImageSearchReport{}, nil) rpt := report.New(os.Stdout, cmd.Name())
renderHeaders := true defer rpt.Flush()
var row string
switch { switch {
case searchOptions.ListTags: case searchOptions.ListTags:
if len(searchOptions.Filters) != 0 { if len(searchOptions.Filters) != 0 {
@ -161,39 +161,30 @@ func imageSearch(cmd *cobra.Command, args []string) error {
listTagsEntries := buildListTagsJSON(searchReport) listTagsEntries := buildListTagsJSON(searchReport)
return printArbitraryJSON(listTagsEntries) return printArbitraryJSON(listTagsEntries)
} }
row = "{{.Name}}\t{{.Tag}}\n" rpt, err = rpt.Parse(report.OriginPodman, "{{range .}}{{.Name}}\t{{.Tag}}\n{{end -}}")
case isJSON: case isJSON:
return printArbitraryJSON(searchReport) return printArbitraryJSON(searchReport)
case cmd.Flags().Changed("format"): case cmd.Flags().Changed("format"):
renderHeaders = report.HasTable(searchOptions.Format) rpt, err = rpt.Parse(report.OriginUser, searchOptions.Format)
row = report.NormalizeFormat(searchOptions.Format)
default: default:
row = "{{.Name}}\t{{.Description}}" row := "{{.Name}}\t{{.Description}}"
if searchOptions.Compatible { if searchOptions.Compatible {
row += "\t{{.Stars}}\t{{.Official}}\t{{.Automated}}" 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 { if err != nil {
return err return err
} }
w, err := report.NewWriterDefault(os.Stdout) if rpt.RenderHeaders {
if err != nil { hdrs := report.Headers(entities.ImageSearchReport{}, nil)
return err if err := rpt.Execute(hdrs); err != nil {
} return errors.Wrapf(err, "failed to write report column headers")
defer w.Flush()
if renderHeaders {
if err := tmpl.Execute(w, hdrs); err != nil {
return errors.Wrapf(err, "failed to write search column headers")
} }
} }
return rpt.Execute(searchReport)
return tmpl.Execute(w, searchReport)
} }
func printArbitraryJSON(v interface{}) error { func printArbitraryJSON(v interface{}) error {

View File

@ -48,11 +48,12 @@ func showTrust(cmd *cobra.Command, args []string) error {
if err != nil { if err != nil {
return err return err
} }
if showTrustOptions.Raw {
switch {
case showTrustOptions.Raw:
fmt.Println(string(trust.Raw)) fmt.Println(string(trust.Raw))
return nil return nil
} case showTrustOptions.JSON:
if showTrustOptions.JSON {
b, err := json.MarshalIndent(trust.Policies, "", " ") b, err := json.MarshalIndent(trust.Policies, "", " ")
if err != nil { if err != nil {
return err return err
@ -60,23 +61,13 @@ func showTrust(cmd *cobra.Command, args []string) error {
fmt.Println(string(b)) fmt.Println(string(b))
return nil return nil
} }
rpt := report.New(os.Stdout, cmd.Name())
defer rpt.Flush()
format := "{{range . }}{{.RepoName}}\t{{.Type}}\t{{.GPGId}}\t{{.SignatureStore}}\n{{end -}}" rpt, err = rpt.Parse(report.OriginPodman,
tmpl, err := report.NewTemplate("list").Parse(format) "{{range . }}{{.RepoName}}\t{{.Type}}\t{{.GPGId}}\t{{.SignatureStore}}\n{{end -}}")
if err != nil { if err != nil {
return err return err
} }
return rpt.Execute(trust.Policies)
w, err := report.NewWriterDefault(os.Stdout)
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
} }

View File

@ -215,8 +215,10 @@ func (i *inspector) inspect(namesOrIDs []string) error {
case report.IsJSON(i.options.Format) || i.options.Format == "": case report.IsJSON(i.options.Format) || i.options.Format == "":
err = printJSON(data) err = printJSON(data)
default: default:
// Landing here implies user has given a custom --format
row := inspectNormalize(i.options.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) err = printTmpl(tmpType, row, data)
} }
if err != nil { if err != nil {

View File

@ -116,7 +116,15 @@ func outputTemplate(cmd *cobra.Command, responses []*machineReporter) error {
"DiskSize": "DISK SIZE", "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) format := report.EnforceRange(row)
tmpl, err := report.NewTemplate("list").Parse(format) tmpl, err := report.NewTemplate("list").Parse(format)
@ -130,10 +138,6 @@ func outputTemplate(cmd *cobra.Command, responses []*machineReporter) error {
} }
defer w.Flush() defer w.Flush()
if cmd.Flags().Changed("format") && !report.HasTable(listFlag.format) {
listFlag.noHeading = true
}
if !listFlag.noHeading { if !listFlag.noHeading {
if err := tmpl.Execute(w, headers); err != nil { if err := tmpl.Execute(w, headers); err != nil {
return errors.Wrapf(err, "failed to write report column headers") return errors.Wrapf(err, "failed to write report column headers")

View File

@ -84,7 +84,7 @@ func networkList(cmd *cobra.Command, args []string) error {
// table or other format output // table or other format output
default: default:
err = templateOut(responses, cmd) err = templateOut(cmd, responses)
} }
return err return err
@ -105,7 +105,7 @@ func jsonOut(responses []types.Network) error {
return nil 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)) nlprs := make([]ListPrintReports, 0, len(responses))
for _, r := range responses { for _, r := range responses {
nlprs = append(nlprs, ListPrintReports{r}) nlprs = append(nlprs, ListPrintReports{r})
@ -120,14 +120,16 @@ func templateOut(responses []types.Network, cmd *cobra.Command) error {
}) })
renderHeaders := report.HasTable(networkListOptions.Format) renderHeaders := report.HasTable(networkListOptions.Format)
var row, format string var row string
if cmd.Flags().Changed("format") { switch {
case cmd.Flags().Changed("format"):
row = report.NormalizeFormat(networkListOptions.Format) row = report.NormalizeFormat(networkListOptions.Format)
} else { // 'podman network ls' equivalent to 'podman network ls --format="table {{.ID}} {{.Name}} {{.Version}} {{.Plugins}}" ' default:
renderHeaders = true // 'podman network ls' equivalent to 'podman network ls --format="table {{.ID}} {{.Name}} {{.Version}} {{.Plugins}}" '
row = "{{.ID}}\t{{.Name}}\t{{.Driver}}\n" row = "{{.ID}}\t{{.Name}}\t{{.Driver}}\n"
renderHeaders = true
} }
format = report.EnforceRange(row) format := report.EnforceRange(row)
tmpl, err := report.NewTemplate("list").Parse(format) tmpl, err := report.NewTemplate("list").Parse(format)
if err != nil { if err != nil {

View File

@ -64,11 +64,13 @@ func inspect(cmd *cobra.Command, args []string) error {
} }
if report.IsJSON(inspectOptions.Format) { if report.IsJSON(inspectOptions.Format) {
json.MarshalIndent(responses, "", " ")
enc := json.NewEncoder(os.Stdout) enc := json.NewEncoder(os.Stdout)
enc.SetIndent("", " ") enc.SetIndent("", " ")
return enc.Encode(responses) return enc.Encode(responses)
} }
// cmd.Flags().Changed("format") must be true to reach this code
row := report.NormalizeFormat(inspectOptions.Format) row := report.NormalizeFormat(inspectOptions.Format)
t, err := report.NewTemplate("inspect").Parse(row) t, err := report.NewTemplate("inspect").Parse(row)

View File

@ -136,11 +136,12 @@ func pods(cmd *cobra.Command, _ []string) error {
renderHeaders = report.HasTable(psInput.Format) renderHeaders = report.HasTable(psInput.Format)
row = report.NormalizeFormat(psInput.Format) row = report.NormalizeFormat(psInput.Format)
} }
format := report.EnforceRange(row)
noHeading, _ := cmd.Flags().GetBool("noheading") noHeading, _ := cmd.Flags().GetBool("noheading")
if noHeading { if noHeading {
renderHeaders = false renderHeaders = false
} }
format := report.EnforceRange(row)
tmpl, err := report.NewTemplate("list").Parse(format) tmpl, err := report.NewTemplate("list").Parse(format)
if err != nil { if err != nil {

View File

@ -67,9 +67,7 @@ func stats(cmd *cobra.Command, args []string) error {
return err return err
} }
row := report.NormalizeFormat(statsOptions.Format) doJSON := report.IsJSON(cmd.Flag("format").Value.String())
doJSON := report.IsJSON(row)
headers := report.Headers(entities.PodStatsReport{}, map[string]string{ headers := report.Headers(entities.PodStatsReport{}, map[string]string{
"CPU": "CPU %", "CPU": "CPU %",
"MemUsage": "MEM USAGE/ LIMIT", "MemUsage": "MEM USAGE/ LIMIT",
@ -96,6 +94,8 @@ func stats(cmd *cobra.Command, args []string) error {
goterm.Flush() goterm.Flush()
} }
if cmd.Flags().Changed("format") { if cmd.Flags().Changed("format") {
row := report.NormalizeFormat(statsOptions.Format)
row = report.EnforceRange(row)
if err := printFormattedPodStatsLines(headers, row, reports); err != nil { if err := printFormattedPodStatsLines(headers, row, reports); err != nil {
return err return err
} }
@ -143,8 +143,6 @@ func printFormattedPodStatsLines(headerNames []map[string]string, row string, st
return nil return nil
} }
row = report.EnforceRange(row)
tmpl, err := report.NewTemplate("stats").Parse(row) tmpl, err := report.NewTemplate("stats").Parse(row)
if err != nil { if err != nil {
return err return err

View File

@ -71,7 +71,10 @@ func outputTemplate(cmd *cobra.Command, responses []*entities.SecretListReport)
"UpdatedAt": "UPDATED", "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) format := report.EnforceRange(row)
tmpl, err := report.NewTemplate("list").Parse(format) tmpl, err := report.NewTemplate("list").Parse(format)

View File

@ -82,7 +82,7 @@ func list(cmd *cobra.Command, _ []string) error {
return rows[i].Name < rows[j].Name return rows[i].Name < rows[j].Name
}) })
format := "{{.Name}}\t{{.URI}}\t{{.Identity}}\t{{.Default}}\n" var format string
switch { switch {
case report.IsJSON(cmd.Flag("format").Value.String()): case report.IsJSON(cmd.Flag("format").Value.String()):
buf, err := registry.JSONLibrary().MarshalIndent(rows, "", " ") buf, err := registry.JSONLibrary().MarshalIndent(rows, "", " ")
@ -90,11 +90,10 @@ func list(cmd *cobra.Command, _ []string) error {
fmt.Println(string(buf)) fmt.Println(string(buf))
} }
return err return err
case cmd.Flags().Changed("format"):
format = report.NormalizeFormat(cmd.Flag("format").Value.String())
default: default:
if cmd.Flag("format").Changed { format = "{{.Name}}\t{{.URI}}\t{{.Identity}}\t{{.Default}}\n"
format = cmd.Flag("format").Value.String()
format = report.NormalizeFormat(format)
}
} }
format = report.EnforceRange(format) format = report.EnforceRange(format)

View File

@ -59,7 +59,7 @@ func version(cmd *cobra.Command, args []string) error {
} }
defer w.Flush() defer w.Flush()
if cmd.Flag("format").Changed { if cmd.Flags().Changed("format") {
row := report.NormalizeFormat(versionFormat) row := report.NormalizeFormat(versionFormat)
tmpl, err := report.NewTemplate("version 2.0.0").Parse(row) tmpl, err := report.NewTemplate("version 2.0.0").Parse(row)
if err != nil { if err != nil {

View File

@ -97,9 +97,14 @@ func outputTemplate(cmd *cobra.Command, responses []*entities.VolumeListReport)
"Name": "VOLUME NAME", "Name": "VOLUME NAME",
}) })
row := report.NormalizeFormat(cliOpts.Format) var row string
if cliOpts.Quiet { switch {
case cliOpts.Quiet:
row = "{{.Name}}\n" row = "{{.Name}}\n"
case cmd.Flags().Changed("format"):
row = report.NormalizeFormat(cliOpts.Format)
default:
row = cmd.Flag("format").Value.String()
} }
format := report.EnforceRange(row) format := report.EnforceRange(row)

View File

@ -221,9 +221,7 @@ Labels.created_at | 20[0-9-]\\\+T[0-9:]\\\+Z
iid=${output:0:12} iid=${output:0:12}
# Run the test: this will output three column-aligned rows. Test them. # 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 'table' 'table {{.Repository}} {{.Tag}} {{.ID}}'
_run_format_test 'tabs' '{{.Repository}}\t{{.Tag}}\t{{.ID}}'
# Clean up. # Clean up.
run_podman rmi ${aaa_name}:${aaa_tag} ${zzz_name}:${zzz_tag} run_podman rmi ${aaa_name}:${aaa_tag} ${zzz_name}:${zzz_tag}

View File

@ -21,6 +21,14 @@ load helpers
done 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" { @test "podman history - json" {
# Sigh. Timestamp in .created can be '...Z' or '...-06:00' # Sigh. Timestamp in .created can be '...Z' or '...-06:00'
tests=" tests="