From bab3cda0e81fa3ac3315601f5ec17724f3ad8ed5 Mon Sep 17 00:00:00 2001 From: Jhon Honce Date: Tue, 20 Oct 2020 10:29:49 -0700 Subject: Refactor podman to use c/common/pkg/report All formatting for containers stack moved into one package The does not correct issue with headers when using custom tables Signed-off-by: Jhon Honce --- cmd/podman/common/diffChanges.go | 43 ++++++++ cmd/podman/containers/diff.go | 10 +- cmd/podman/containers/mount.go | 4 +- cmd/podman/containers/ps.go | 7 +- cmd/podman/containers/stats.go | 9 +- cmd/podman/generate/systemd.go | 14 +-- cmd/podman/images/diff.go | 10 +- cmd/podman/images/history.go | 5 +- cmd/podman/images/list.go | 5 +- cmd/podman/images/mount.go | 4 +- cmd/podman/images/search.go | 2 +- cmd/podman/inspect/inspect.go | 5 +- cmd/podman/networks/create.go | 4 +- cmd/podman/networks/inspect.go | 5 +- cmd/podman/networks/list.go | 2 +- cmd/podman/pods/inspect.go | 5 +- cmd/podman/pods/ps.go | 5 +- cmd/podman/pods/stats.go | 5 +- cmd/podman/report/diff.go | 43 -------- cmd/podman/report/format.go | 68 ------------ cmd/podman/report/format_test.go | 35 ------- cmd/podman/report/report.go | 6 -- cmd/podman/system/df.go | 2 +- cmd/podman/system/events.go | 4 +- cmd/podman/system/info.go | 4 +- cmd/podman/system/version.go | 5 +- cmd/podman/volumes/inspect.go | 5 +- cmd/podman/volumes/list.go | 5 +- .../common/pkg/report/camelcase/LICENSE.md | 20 ++++ .../common/pkg/report/camelcase/README.md | 58 +++++++++++ .../common/pkg/report/camelcase/camelcase.go | 91 ++++++++++++++++ .../github.com/containers/common/pkg/report/doc.go | 46 +++++++++ .../containers/common/pkg/report/template.go | 114 +++++++++++++++++++++ .../containers/common/pkg/report/validate.go | 13 +++ .../containers/common/pkg/report/writer.go | 27 +++++ vendor/modules.txt | 2 + 36 files changed, 471 insertions(+), 221 deletions(-) create mode 100644 cmd/podman/common/diffChanges.go delete mode 100644 cmd/podman/report/diff.go delete mode 100644 cmd/podman/report/format.go delete mode 100644 cmd/podman/report/format_test.go delete mode 100644 cmd/podman/report/report.go create mode 100644 vendor/github.com/containers/common/pkg/report/camelcase/LICENSE.md create mode 100644 vendor/github.com/containers/common/pkg/report/camelcase/README.md create mode 100644 vendor/github.com/containers/common/pkg/report/camelcase/camelcase.go create mode 100644 vendor/github.com/containers/common/pkg/report/doc.go create mode 100644 vendor/github.com/containers/common/pkg/report/template.go create mode 100644 vendor/github.com/containers/common/pkg/report/validate.go create mode 100644 vendor/github.com/containers/common/pkg/report/writer.go diff --git a/cmd/podman/common/diffChanges.go b/cmd/podman/common/diffChanges.go new file mode 100644 index 000000000..4aa485acc --- /dev/null +++ b/cmd/podman/common/diffChanges.go @@ -0,0 +1,43 @@ +package common + +import ( + "fmt" + "os" + + "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/containers/storage/pkg/archive" + "github.com/pkg/errors" +) + +type ChangesReportJSON struct { + Changed []string `json:"changed,omitempty"` + Added []string `json:"added,omitempty"` + Deleted []string `json:"deleted,omitempty"` +} + +func ChangesToJSON(diffs *entities.DiffReport) error { + body := ChangesReportJSON{} + for _, row := range diffs.Changes { + switch row.Kind { + case archive.ChangeAdd: + body.Added = append(body.Added, row.Path) + case archive.ChangeDelete: + body.Deleted = append(body.Deleted, row.Path) + case archive.ChangeModify: + body.Changed = append(body.Changed, row.Path) + default: + return errors.Errorf("output kind %q not recognized", row.Kind) + } + } + + // Pull in configured json library + enc := json.NewEncoder(os.Stdout) + return enc.Encode(body) +} + +func ChangesToTable(diffs *entities.DiffReport) error { + for _, row := range diffs.Changes { + fmt.Fprintln(os.Stdout, row.String()) + } + return nil +} diff --git a/cmd/podman/containers/diff.go b/cmd/podman/containers/diff.go index a3ca6edf9..14d09b3b6 100644 --- a/cmd/podman/containers/diff.go +++ b/cmd/podman/containers/diff.go @@ -1,9 +1,9 @@ package containers import ( - "github.com/containers/podman/v2/cmd/podman/parse" + "github.com/containers/common/pkg/report" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" - "github.com/containers/podman/v2/cmd/podman/report" "github.com/containers/podman/v2/cmd/podman/validate" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/pkg/errors" @@ -54,10 +54,10 @@ func diff(cmd *cobra.Command, args []string) error { } switch { - case parse.MatchesJSONFormat(diffOpts.Format): - return report.ChangesToJSON(results) + case report.IsJSON(diffOpts.Format): + return common.ChangesToJSON(results) case diffOpts.Format == "": - return report.ChangesToTable(results) + return common.ChangesToTable(results) default: return errors.New("only supported value for '--format' is 'json'") } diff --git a/cmd/podman/containers/mount.go b/cmd/podman/containers/mount.go index c4dfb513f..7c592697b 100644 --- a/cmd/podman/containers/mount.go +++ b/cmd/podman/containers/mount.go @@ -6,7 +6,7 @@ import ( "text/tabwriter" "text/template" - "github.com/containers/podman/v2/cmd/podman/parse" + "github.com/containers/common/pkg/report" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/utils" "github.com/containers/podman/v2/cmd/podman/validate" @@ -97,7 +97,7 @@ func mount(_ *cobra.Command, args []string) error { } switch { - case parse.MatchesJSONFormat(mountOpts.Format): + case report.IsJSON(mountOpts.Format): return printJSON(reports) case mountOpts.Format == "": break // print defaults diff --git a/cmd/podman/containers/ps.go b/cmd/podman/containers/ps.go index 446b46471..89034b252 100644 --- a/cmd/podman/containers/ps.go +++ b/cmd/podman/containers/ps.go @@ -11,9 +11,8 @@ import ( "time" tm "github.com/buger/goterm" - "github.com/containers/podman/v2/cmd/podman/parse" + "github.com/containers/common/pkg/report" "github.com/containers/podman/v2/cmd/podman/registry" - "github.com/containers/podman/v2/cmd/podman/report" "github.com/containers/podman/v2/cmd/podman/utils" "github.com/containers/podman/v2/cmd/podman/validate" "github.com/containers/podman/v2/pkg/domain/entities" @@ -92,7 +91,7 @@ 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 && !parse.MatchesJSONFormat(listOpts.Format) { + if c.Flag("format").Changed && !report.IsJSON(listOpts.Format) { // Quiet is overridden by Go template output. listOpts.Quiet = false } @@ -179,7 +178,7 @@ func ps(cmd *cobra.Command, args []string) error { } switch { - case parse.MatchesJSONFormat(listOpts.Format): + case report.IsJSON(listOpts.Format): return jsonOut(listContainers) case listOpts.Quiet: return quietOut(listContainers) diff --git a/cmd/podman/containers/stats.go b/cmd/podman/containers/stats.go index c30ea52ec..07631681b 100644 --- a/cmd/podman/containers/stats.go +++ b/cmd/podman/containers/stats.go @@ -7,9 +7,8 @@ import ( "text/template" tm "github.com/buger/goterm" - "github.com/containers/podman/v2/cmd/podman/parse" + "github.com/containers/common/pkg/report" "github.com/containers/podman/v2/cmd/podman/registry" - "github.com/containers/podman/v2/cmd/podman/report" "github.com/containers/podman/v2/cmd/podman/validate" "github.com/containers/podman/v2/libpod/define" "github.com/containers/podman/v2/pkg/cgroups" @@ -157,7 +156,7 @@ func outputStats(reports []define.ContainerStats) error { for _, r := range reports { stats = append(stats, containerStats{r}) } - if parse.MatchesJSONFormat(statsOptions.Format) { + if report.IsJSON(statsOptions.Format) { return outputJSON(stats) } format := defaultStatsRow @@ -240,9 +239,9 @@ func combineHumanValues(a, b uint64) string { func outputJSON(stats []containerStats) error { type jstat struct { - Id string `json:"id"` //nolint + Id string `json:"id"` // nolint Name string `json:"name"` - CpuPercent string `json:"cpu_percent"` //nolint + CpuPercent string `json:"cpu_percent"` // nolint MemUsage string `json:"mem_usage"` MemPerc string `json:"mem_percent"` NetIO string `json:"net_io"` diff --git a/cmd/podman/generate/systemd.go b/cmd/podman/generate/systemd.go index 02e826549..882c769b8 100644 --- a/cmd/podman/generate/systemd.go +++ b/cmd/podman/generate/systemd.go @@ -6,7 +6,7 @@ import ( "os" "path/filepath" - "github.com/containers/podman/v2/cmd/podman/parse" + "github.com/containers/common/pkg/report" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/utils" "github.com/containers/podman/v2/pkg/domain/entities" @@ -63,7 +63,7 @@ func systemd(cmd *cobra.Command, args []string) error { logrus.Warnln("The generated units should be placed on your remote system") } - report, err := registry.ContainerEngine().GenerateSystemd(registry.GetContext(), args[0], systemdOptions) + reports, err := registry.ContainerEngine().GenerateSystemd(registry.GetContext(), args[0], systemdOptions) if err != nil { return err } @@ -73,7 +73,7 @@ func systemd(cmd *cobra.Command, args []string) error { if err != nil { return errors.Wrap(err, "error getting current working directory") } - for name, content := range report.Units { + for name, content := range reports.Units { path := filepath.Join(cwd, fmt.Sprintf("%s.service", name)) f, err := os.Create(path) if err != nil { @@ -94,15 +94,15 @@ func systemd(cmd *cobra.Command, args []string) error { } // modify in place so we can print the // paths when --files is set - report.Units[name] = path + reports.Units[name] = path } } switch { - case parse.MatchesJSONFormat(format): - return printJSON(report.Units) + case report.IsJSON(format): + return printJSON(reports.Units) case format == "": - return printDefault(report.Units) + return printDefault(reports.Units) default: return errors.Errorf("unknown --format argument: %s", format) } diff --git a/cmd/podman/images/diff.go b/cmd/podman/images/diff.go index 05a05fa04..3b00848f7 100644 --- a/cmd/podman/images/diff.go +++ b/cmd/podman/images/diff.go @@ -1,9 +1,9 @@ package images import ( - "github.com/containers/podman/v2/cmd/podman/parse" + "github.com/containers/common/pkg/report" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" - "github.com/containers/podman/v2/cmd/podman/report" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -51,10 +51,10 @@ func diff(cmd *cobra.Command, args []string) error { } switch { - case parse.MatchesJSONFormat(diffOpts.Format): - return report.ChangesToJSON(results) + case report.IsJSON(diffOpts.Format): + return common.ChangesToJSON(results) case diffOpts.Format == "": - return report.ChangesToTable(results) + return common.ChangesToTable(results) default: return errors.New("only supported value for '--format' is 'json'") } diff --git a/cmd/podman/images/history.go b/cmd/podman/images/history.go index fa4b368c6..184e50965 100644 --- a/cmd/podman/images/history.go +++ b/cmd/podman/images/history.go @@ -10,9 +10,8 @@ import ( "time" "unicode" - "github.com/containers/podman/v2/cmd/podman/parse" + "github.com/containers/common/pkg/report" "github.com/containers/podman/v2/cmd/podman/registry" - "github.com/containers/podman/v2/cmd/podman/report" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/docker/go-units" "github.com/pkg/errors" @@ -81,7 +80,7 @@ func history(cmd *cobra.Command, args []string) error { return err } - if parse.MatchesJSONFormat(opts.format) { + if report.IsJSON(opts.format) { var err error if len(results.Layers) == 0 { _, err = fmt.Fprintf(os.Stdout, "[]\n") diff --git a/cmd/podman/images/list.go b/cmd/podman/images/list.go index 239da9d28..eaf81748f 100644 --- a/cmd/podman/images/list.go +++ b/cmd/podman/images/list.go @@ -10,10 +10,9 @@ import ( "time" "unicode" + "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/cmd/podman/report" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/docker/go-units" "github.com/pkg/errors" @@ -108,7 +107,7 @@ func images(cmd *cobra.Command, args []string) error { switch { case listFlag.quiet: return writeID(imgs) - case parse.MatchesJSONFormat(listFlag.format): + case report.IsJSON(listFlag.format): return writeJSON(imgs) default: if cmd.Flag("format").Changed { diff --git a/cmd/podman/images/mount.go b/cmd/podman/images/mount.go index 0a972ea81..ff54e1d54 100644 --- a/cmd/podman/images/mount.go +++ b/cmd/podman/images/mount.go @@ -6,7 +6,7 @@ import ( "text/tabwriter" "text/template" - "github.com/containers/podman/v2/cmd/podman/parse" + "github.com/containers/common/pkg/report" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/utils" "github.com/containers/podman/v2/pkg/domain/entities" @@ -80,7 +80,7 @@ func mount(cmd *cobra.Command, args []string) error { } switch { - case parse.MatchesJSONFormat(mountOpts.Format): + case report.IsJSON(mountOpts.Format): return printJSON(reports) case mountOpts.Format == "": break // default format diff --git a/cmd/podman/images/search.go b/cmd/podman/images/search.go index 8edd776ce..a451c25ff 100644 --- a/cmd/podman/images/search.go +++ b/cmd/podman/images/search.go @@ -6,9 +6,9 @@ import ( "text/template" "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/registry" - "github.com/containers/podman/v2/cmd/podman/report" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/pkg/errors" "github.com/spf13/cobra" diff --git a/cmd/podman/inspect/inspect.go b/cmd/podman/inspect/inspect.go index 3d1ef72aa..a62a68959 100644 --- a/cmd/podman/inspect/inspect.go +++ b/cmd/podman/inspect/inspect.go @@ -9,9 +9,8 @@ import ( "text/tabwriter" "text/template" - "github.com/containers/podman/v2/cmd/podman/parse" + "github.com/containers/common/pkg/report" "github.com/containers/podman/v2/cmd/podman/registry" - "github.com/containers/podman/v2/cmd/podman/report" "github.com/containers/podman/v2/cmd/podman/validate" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/pkg/errors" @@ -143,7 +142,7 @@ func (i *inspector) inspect(namesOrIDs []string) error { var err error switch { - case parse.MatchesJSONFormat(i.options.Format) || i.options.Format == "": + case report.IsJSON(i.options.Format) || i.options.Format == "": err = printJSON(data) default: row := inspectNormalize(i.options.Format) diff --git a/cmd/podman/networks/create.go b/cmd/podman/networks/create.go index 17f39bd8b..d32225aa7 100644 --- a/cmd/podman/networks/create.go +++ b/cmd/podman/networks/create.go @@ -34,9 +34,9 @@ func networkCreateFlags(flags *pflag.FlagSet) { flags.IPNetVar(&networkCreateOptions.Range, "ip-range", net.IPNet{}, "allocate container IP from range") flags.StringVar(&networkCreateOptions.MacVLAN, "macvlan", "", "create a Macvlan connection based on this device") // TODO not supported yet - //flags.StringVar(&networkCreateOptions.IPamDriver, "ipam-driver", "", "IP Address Management Driver") + // flags.StringVar(&networkCreateOptions.IPamDriver, "ipam-driver", "", "IP Address Management Driver") // TODO enable when IPv6 is working - //flags.BoolVar(&networkCreateOptions.IPV6, "IPv6", false, "enable IPv6 networking") + // flags.BoolVar(&networkCreateOptions.IPV6, "IPv6", false, "enable IPv6 networking") flags.IPNetVar(&networkCreateOptions.Subnet, "subnet", net.IPNet{}, "subnet in CIDR format") flags.BoolVar(&networkCreateOptions.DisableDNS, "disable-dns", false, "disable dns plugin") } diff --git a/cmd/podman/networks/inspect.go b/cmd/podman/networks/inspect.go index c36125948..29131cd28 100644 --- a/cmd/podman/networks/inspect.go +++ b/cmd/podman/networks/inspect.go @@ -7,9 +7,8 @@ import ( "text/tabwriter" "text/template" - "github.com/containers/podman/v2/cmd/podman/parse" + "github.com/containers/common/pkg/report" "github.com/containers/podman/v2/cmd/podman/registry" - "github.com/containers/podman/v2/cmd/podman/report" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/spf13/cobra" ) @@ -47,7 +46,7 @@ func networkInspect(_ *cobra.Command, args []string) error { } switch { - case parse.MatchesJSONFormat(networkInspectOptions.Format) || networkInspectOptions.Format == "": + case report.IsJSON(networkInspectOptions.Format) || networkInspectOptions.Format == "": b, err := json.MarshalIndent(responses, "", " ") if err != nil { return err diff --git a/cmd/podman/networks/list.go b/cmd/podman/networks/list.go index c53f50c9f..cb70d5218 100644 --- a/cmd/podman/networks/list.go +++ b/cmd/podman/networks/list.go @@ -36,7 +36,7 @@ var ( func networkListFlags(flags *pflag.FlagSet) { // TODO enable filters based on something - //flags.StringSliceVarP(&networklistCommand.Filter, "filter", "f", []string{}, "Pause all running containers") + // flags.StringSliceVarP(&networklistCommand.Filter, "filter", "f", []string{}, "Pause all running containers") flags.StringVarP(&networkListOptions.Format, "format", "f", "", "Pretty-print networks to JSON or using a Go template") flags.BoolVarP(&networkListOptions.Quiet, "quiet", "q", false, "display only names") flags.StringVarP(&networkListOptions.Filter, "filter", "", "", "Provide filter values (e.g. 'name=podman')") diff --git a/cmd/podman/pods/inspect.go b/cmd/podman/pods/inspect.go index 142c8d270..d5e026d38 100644 --- a/cmd/podman/pods/inspect.go +++ b/cmd/podman/pods/inspect.go @@ -7,9 +7,8 @@ import ( "text/tabwriter" "text/template" - "github.com/containers/podman/v2/cmd/podman/parse" + "github.com/containers/common/pkg/report" "github.com/containers/podman/v2/cmd/podman/registry" - "github.com/containers/podman/v2/cmd/podman/report" "github.com/containers/podman/v2/cmd/podman/validate" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/pkg/errors" @@ -62,7 +61,7 @@ func inspect(cmd *cobra.Command, args []string) error { return err } - if parse.MatchesJSONFormat(inspectOptions.Format) { + if report.IsJSON(inspectOptions.Format) { enc := json.NewEncoder(os.Stdout) enc.SetIndent("", " ") return enc.Encode(responses) diff --git a/cmd/podman/pods/ps.go b/cmd/podman/pods/ps.go index 0013cca02..714d4130f 100644 --- a/cmd/podman/pods/ps.go +++ b/cmd/podman/pods/ps.go @@ -10,9 +10,8 @@ import ( "text/template" "time" - "github.com/containers/podman/v2/cmd/podman/parse" + "github.com/containers/common/pkg/report" "github.com/containers/podman/v2/cmd/podman/registry" - "github.com/containers/podman/v2/cmd/podman/report" "github.com/containers/podman/v2/cmd/podman/validate" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/docker/go-units" @@ -85,7 +84,7 @@ func pods(cmd *cobra.Command, _ []string) error { } switch { - case parse.MatchesJSONFormat(psInput.Format): + case report.IsJSON(psInput.Format): b, err := json.MarshalIndent(responses, "", " ") if err != nil { return err diff --git a/cmd/podman/pods/stats.go b/cmd/podman/pods/stats.go index 2f59e4e47..953494918 100644 --- a/cmd/podman/pods/stats.go +++ b/cmd/podman/pods/stats.go @@ -9,9 +9,8 @@ import ( "time" "github.com/buger/goterm" - "github.com/containers/podman/v2/cmd/podman/parse" + "github.com/containers/common/pkg/report" "github.com/containers/podman/v2/cmd/podman/registry" - "github.com/containers/podman/v2/cmd/podman/report" "github.com/containers/podman/v2/cmd/podman/validate" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/spf13/cobra" @@ -66,7 +65,7 @@ func stats(cmd *cobra.Command, args []string) error { } row := report.NormalizeFormat(statsOptions.Format) - doJSON := parse.MatchesJSONFormat(row) + doJSON := report.IsJSON(row) headers := report.Headers(entities.PodStatsReport{}, map[string]string{ "CPU": "CPU %", diff --git a/cmd/podman/report/diff.go b/cmd/podman/report/diff.go deleted file mode 100644 index edd324bfe..000000000 --- a/cmd/podman/report/diff.go +++ /dev/null @@ -1,43 +0,0 @@ -package report - -import ( - "fmt" - "os" - - "github.com/containers/podman/v2/pkg/domain/entities" - "github.com/containers/storage/pkg/archive" - "github.com/pkg/errors" -) - -type ChangesReportJSON struct { - Changed []string `json:"changed,omitempty"` - Added []string `json:"added,omitempty"` - Deleted []string `json:"deleted,omitempty"` -} - -func ChangesToJSON(diffs *entities.DiffReport) error { - body := ChangesReportJSON{} - for _, row := range diffs.Changes { - switch row.Kind { - case archive.ChangeAdd: - body.Added = append(body.Added, row.Path) - case archive.ChangeDelete: - body.Deleted = append(body.Deleted, row.Path) - case archive.ChangeModify: - body.Changed = append(body.Changed, row.Path) - default: - return errors.Errorf("output kind %q not recognized", row.Kind) - } - } - - // Pull in configured json library - enc := json.NewEncoder(os.Stdout) - return enc.Encode(body) -} - -func ChangesToTable(diffs *entities.DiffReport) error { - for _, row := range diffs.Changes { - fmt.Fprintln(os.Stdout, row.String()) - } - return nil -} diff --git a/cmd/podman/report/format.go b/cmd/podman/report/format.go deleted file mode 100644 index 32d92bec5..000000000 --- a/cmd/podman/report/format.go +++ /dev/null @@ -1,68 +0,0 @@ -package report - -import ( - "reflect" - "strings" -) - -// tableReplacer will remove 'table ' prefix and clean up tabs -var tableReplacer = strings.NewReplacer( - "table ", "", - `\t`, "\t", - `\n`, "\n", - " ", "\t", -) - -// escapedReplacer will clean up escaped characters from CLI -var escapedReplacer = strings.NewReplacer( - `\t`, "\t", - `\n`, "\n", -) - -// NormalizeFormat reads given go template format provided by CLI and munges it into what we need -func NormalizeFormat(format string) string { - f := format - // two replacers used so we only remove the prefix keyword `table` - if strings.HasPrefix(f, "table ") { - f = tableReplacer.Replace(f) - } else { - f = escapedReplacer.Replace(format) - } - - if !strings.HasSuffix(f, "\n") { - f += "\n" - } - - return f -} - -// Headers queries the interface for field names -func Headers(object interface{}, overrides map[string]string) []map[string]string { - value := reflect.ValueOf(object) - if value.Kind() == reflect.Ptr { - value = value.Elem() - } - - // Column header will be field name upper-cased. - headers := make(map[string]string, value.NumField()) - for i := 0; i < value.Type().NumField(); i++ { - field := value.Type().Field(i) - // Recurse to find field names from promoted structs - if field.Type.Kind() == reflect.Struct && field.Anonymous { - h := Headers(reflect.New(field.Type).Interface(), nil) - for k, v := range h[0] { - headers[k] = v - } - continue - } - headers[field.Name] = strings.ToUpper(field.Name) - } - - if len(overrides) > 0 { - // Override column header as provided - for k, v := range overrides { - headers[k] = strings.ToUpper(v) - } - } - return []map[string]string{headers} -} diff --git a/cmd/podman/report/format_test.go b/cmd/podman/report/format_test.go deleted file mode 100644 index 7dd62e899..000000000 --- a/cmd/podman/report/format_test.go +++ /dev/null @@ -1,35 +0,0 @@ -package report - -import ( - "strings" - "testing" -) - -func TestNormalizeFormat(t *testing.T) { - cases := []struct { - format string - expected string - }{ - {"table {{.ID}}", "{{.ID}}\n"}, - {"table {{.ID}} {{.C}}", "{{.ID}}\t{{.C}}\n"}, - {"{{.ID}}", "{{.ID}}\n"}, - {"{{.ID}}\n", "{{.ID}}\n"}, - {"{{.ID}} {{.C}}", "{{.ID}} {{.C}}\n"}, - {"\t{{.ID}}", "\t{{.ID}}\n"}, - {`\t` + "{{.ID}}", "\t{{.ID}}\n"}, - {"table {{.ID}}\t{{.C}}", "{{.ID}}\t{{.C}}\n"}, - {"{{.ID}} table {{.C}}", "{{.ID}} table {{.C}}\n"}, - } - for _, tc := range cases { - tc := tc - - label := strings.ReplaceAll(tc.format, " ", "") - t.Run("NormalizeFormat/"+label, func(t *testing.T) { - t.Parallel() - actual := NormalizeFormat(tc.format) - if actual != tc.expected { - t.Errorf("Expected %q, actual %q", tc.expected, actual) - } - }) - } -} diff --git a/cmd/podman/report/report.go b/cmd/podman/report/report.go deleted file mode 100644 index 2c4f2e1fd..000000000 --- a/cmd/podman/report/report.go +++ /dev/null @@ -1,6 +0,0 @@ -package report - -import "github.com/containers/podman/v2/cmd/podman/registry" - -// Pull in configured json library -var json = registry.JSONLibrary() diff --git a/cmd/podman/system/df.go b/cmd/podman/system/df.go index da7bbed02..574435232 100644 --- a/cmd/podman/system/df.go +++ b/cmd/podman/system/df.go @@ -8,8 +8,8 @@ import ( "text/template" "time" + "github.com/containers/common/pkg/report" "github.com/containers/podman/v2/cmd/podman/registry" - "github.com/containers/podman/v2/cmd/podman/report" "github.com/containers/podman/v2/cmd/podman/validate" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/docker/go-units" diff --git a/cmd/podman/system/events.go b/cmd/podman/system/events.go index aaf572873..83c7d64fb 100644 --- a/cmd/podman/system/events.go +++ b/cmd/podman/system/events.go @@ -6,7 +6,7 @@ import ( "os" "text/template" - "github.com/containers/podman/v2/cmd/podman/parse" + "github.com/containers/common/pkg/report" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/validate" "github.com/containers/podman/v2/libpod/events" @@ -65,7 +65,7 @@ func eventsCmd(cmd *cobra.Command, _ []string) error { ) if cmd.Flags().Changed("format") { - doJSON = parse.MatchesJSONFormat(eventFormat) + doJSON = report.IsJSON(eventFormat) if !doJSON { var err error tmpl, err = template.New("events").Parse(eventFormat) diff --git a/cmd/podman/system/info.go b/cmd/podman/system/info.go index ee720abf8..549238ba9 100644 --- a/cmd/podman/system/info.go +++ b/cmd/podman/system/info.go @@ -5,7 +5,7 @@ import ( "os" "text/template" - "github.com/containers/podman/v2/cmd/podman/parse" + "github.com/containers/common/pkg/report" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/validate" "github.com/containers/podman/v2/pkg/domain/entities" @@ -70,7 +70,7 @@ func info(cmd *cobra.Command, args []string) error { } switch { - case parse.MatchesJSONFormat(inFormat): + case report.IsJSON(inFormat): b, err := json.MarshalIndent(info, "", " ") if err != nil { return err diff --git a/cmd/podman/system/version.go b/cmd/podman/system/version.go index 4f47c5fba..0f53170a2 100644 --- a/cmd/podman/system/version.go +++ b/cmd/podman/system/version.go @@ -8,9 +8,8 @@ import ( "text/tabwriter" "text/template" - "github.com/containers/podman/v2/cmd/podman/parse" + "github.com/containers/common/pkg/report" "github.com/containers/podman/v2/cmd/podman/registry" - "github.com/containers/podman/v2/cmd/podman/report" "github.com/containers/podman/v2/cmd/podman/validate" "github.com/containers/podman/v2/libpod/define" "github.com/containers/podman/v2/pkg/domain/entities" @@ -42,7 +41,7 @@ func version(cmd *cobra.Command, args []string) error { return err } - if parse.MatchesJSONFormat(versionFormat) { + if report.IsJSON(versionFormat) { s, err := json.MarshalToString(versions) if err != nil { return err diff --git a/cmd/podman/volumes/inspect.go b/cmd/podman/volumes/inspect.go index 8d1350228..ece1e31ce 100644 --- a/cmd/podman/volumes/inspect.go +++ b/cmd/podman/volumes/inspect.go @@ -5,9 +5,8 @@ import ( "os" "text/template" - "github.com/containers/podman/v2/cmd/podman/parse" + "github.com/containers/common/pkg/report" "github.com/containers/podman/v2/cmd/podman/registry" - "github.com/containers/podman/v2/cmd/podman/report" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -55,7 +54,7 @@ func inspect(cmd *cobra.Command, args []string) error { } switch { - case parse.MatchesJSONFormat(inspectFormat), inspectFormat == "": + case report.IsJSON(inspectFormat), inspectFormat == "": jsonOut, err := json.MarshalIndent(responses, "", " ") if err != nil { return errors.Wrapf(err, "error marshalling inspect JSON") diff --git a/cmd/podman/volumes/list.go b/cmd/podman/volumes/list.go index 18765a499..0a308ec47 100644 --- a/cmd/podman/volumes/list.go +++ b/cmd/podman/volumes/list.go @@ -8,9 +8,8 @@ import ( "text/tabwriter" "text/template" - "github.com/containers/podman/v2/cmd/podman/parse" + "github.com/containers/common/pkg/report" "github.com/containers/podman/v2/cmd/podman/registry" - "github.com/containers/podman/v2/cmd/podman/report" "github.com/containers/podman/v2/cmd/podman/validate" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/pkg/errors" @@ -75,7 +74,7 @@ func list(cmd *cobra.Command, args []string) error { } switch { - case parse.MatchesJSONFormat(cliOpts.Format): + case report.IsJSON(cliOpts.Format): return outputJSON(responses) case len(responses) < 1: return nil diff --git a/vendor/github.com/containers/common/pkg/report/camelcase/LICENSE.md b/vendor/github.com/containers/common/pkg/report/camelcase/LICENSE.md new file mode 100644 index 000000000..aa4a536ca --- /dev/null +++ b/vendor/github.com/containers/common/pkg/report/camelcase/LICENSE.md @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2015 Fatih Arslan + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/containers/common/pkg/report/camelcase/README.md b/vendor/github.com/containers/common/pkg/report/camelcase/README.md new file mode 100644 index 000000000..105a6ae33 --- /dev/null +++ b/vendor/github.com/containers/common/pkg/report/camelcase/README.md @@ -0,0 +1,58 @@ +# CamelCase [![GoDoc](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square)](http://godoc.org/github.com/fatih/camelcase) [![Build Status](http://img.shields.io/travis/fatih/camelcase.svg?style=flat-square)](https://travis-ci.org/fatih/camelcase) + +CamelCase is a Golang (Go) package to split the words of a camelcase type +string into a slice of words. It can be used to convert a camelcase word (lower +or upper case) into any type of word. + +## Splitting rules: + +1. If string is not valid UTF-8, return it without splitting as + single item array. +2. Assign all unicode characters into one of 4 sets: lower case + letters, upper case letters, numbers, and all other characters. +3. Iterate through characters of string, introducing splits + between adjacent characters that belong to different sets. +4. Iterate through array of split strings, and if a given string + is upper case: + * if subsequent string is lower case: + * move last character of upper case string to beginning of + lower case string + +## Install + +```bash +go get github.com/fatih/camelcase +``` + +## Usage and examples + +```go +splitted := camelcase.Split("GolangPackage") + +fmt.Println(splitted[0], splitted[1]) // prints: "Golang", "Package" +``` + +Both lower camel case and upper camel case are supported. For more info please +check: [http://en.wikipedia.org/wiki/CamelCase](http://en.wikipedia.org/wiki/CamelCase) + +Below are some example cases: + +``` +"" => [] +"lowercase" => ["lowercase"] +"Class" => ["Class"] +"MyClass" => ["My", "Class"] +"MyC" => ["My", "C"] +"HTML" => ["HTML"] +"PDFLoader" => ["PDF", "Loader"] +"AString" => ["A", "String"] +"SimpleXMLParser" => ["Simple", "XML", "Parser"] +"vimRPCPlugin" => ["vim", "RPC", "Plugin"] +"GL11Version" => ["GL", "11", "Version"] +"99Bottles" => ["99", "Bottles"] +"May5" => ["May", "5"] +"BFG9000" => ["BFG", "9000"] +"BöseÜberraschung" => ["Böse", "Überraschung"] +"Two spaces" => ["Two", " ", "spaces"] +"BadUTF8\xe2\xe2\xa1" => ["BadUTF8\xe2\xe2\xa1"] +``` diff --git a/vendor/github.com/containers/common/pkg/report/camelcase/camelcase.go b/vendor/github.com/containers/common/pkg/report/camelcase/camelcase.go new file mode 100644 index 000000000..0a82d1005 --- /dev/null +++ b/vendor/github.com/containers/common/pkg/report/camelcase/camelcase.go @@ -0,0 +1,91 @@ +// Package camelcase is a micro package to split the words of a camelcase type +// string into a slice of words. +package camelcase + +import ( + "unicode" + "unicode/utf8" +) + +// Split splits the camelcase word and returns a list of words. It also +// supports digits. Both lower camel case and upper camel case are supported. +// For more info please check: http://en.wikipedia.org/wiki/CamelCase +// +// Examples +// +// "" => [""] +// "lowercase" => ["lowercase"] +// "Class" => ["Class"] +// "MyClass" => ["My", "Class"] +// "MyC" => ["My", "C"] +// "HTML" => ["HTML"] +// "PDFLoader" => ["PDF", "Loader"] +// "AString" => ["A", "String"] +// "SimpleXMLParser" => ["Simple", "XML", "Parser"] +// "vimRPCPlugin" => ["vim", "RPC", "Plugin"] +// "GL11Version" => ["GL", "11", "Version"] +// "99Bottles" => ["99", "Bottles"] +// "May5" => ["May", "5"] +// "BFG9000" => ["BFG", "9000"] +// "BöseÜberraschung" => ["Böse", "Überraschung"] +// "Two spaces" => ["Two", " ", "spaces"] +// "BadUTF8\xe2\xe2\xa1" => ["BadUTF8\xe2\xe2\xa1"] +// +// Splitting rules +// +// 1) If string is not valid UTF-8, return it without splitting as +// single item array. +// 2) Assign all unicode characters into one of 4 sets: lower case +// letters, upper case letters, numbers, and all other characters. +// 3) Iterate through characters of string, introducing splits +// between adjacent characters that belong to different sets. +// 4) Iterate through array of split strings, and if a given string +// is upper case: +// if subsequent string is lower case: +// move last character of upper case string to beginning of +// lower case string +func Split(src string) (entries []string) { + // don't split invalid utf8 + if !utf8.ValidString(src) { + return []string{src} + } + entries = []string{} + var runes [][]rune + lastClass := 0 + class := 0 + // split into fields based on class of unicode character + for _, r := range src { + switch { + case unicode.IsLower(r): + class = 1 + case unicode.IsUpper(r): + class = 2 + case unicode.IsDigit(r): + class = 3 + default: + class = 4 + } + if class == lastClass { + runes[len(runes)-1] = append(runes[len(runes)-1], r) + } else { + runes = append(runes, []rune{r}) + } + lastClass = class + } + // handle upper case -> lower case sequences, e.g. + // "PDFL", "oader" -> "PDF", "Loader" + for i := 0; i < len(runes)-1; i++ { + if unicode.IsUpper(runes[i][0]) && unicode.IsLower(runes[i+1][0]) { + runes[i+1] = append([]rune{runes[i][len(runes[i])-1]}, runes[i+1]...) + runes[i] = runes[i][:len(runes[i])-1] + } + } + // construct []string from results + for _, s := range runes { + if len(s) > 0 { + entries = append(entries, string(s)) + } + } + + return entries +} diff --git a/vendor/github.com/containers/common/pkg/report/doc.go b/vendor/github.com/containers/common/pkg/report/doc.go new file mode 100644 index 000000000..60d954d7e --- /dev/null +++ b/vendor/github.com/containers/common/pkg/report/doc.go @@ -0,0 +1,46 @@ +/* +Package report provides helper structs/methods/funcs for formatting output + +To format output for an array of structs: + + w := report.NewWriterDefault(os.Stdout) + defer w.Flush() + + headers := report.Headers(struct { + ID string + }{}, nil) + t, _ := report.NewTemplate("command name").Parse("{{range .}}{{.ID}}{{end}}") + t.Execute(t, headers) + t.Execute(t, map[string]string{ + "ID":"fa85da03b40141899f3af3de6d27852b", + }) + // t.IsTable() == false + +or + + w := report.NewWriterDefault(os.Stdout) + defer w.Flush() + + headers := report.Headers(struct { + CID string + }{}, map[string]string{ + "CID":"ID"}) + t, _ := report.NewTemplate("command name").Parse("table {{.CID}}") + t.Execute(t, headers) + t.Execute(t,map[string]string{ + "CID":"fa85da03b40141899f3af3de6d27852b", + }) + // t.IsTable() == true + +Helpers: + + if report.IsJSON(cmd.Flag("format").Value.String()) { + ... process JSON and output + } + +and + + +Note: Your code should not ignore errors +*/ +package report diff --git a/vendor/github.com/containers/common/pkg/report/template.go b/vendor/github.com/containers/common/pkg/report/template.go new file mode 100644 index 000000000..07f9634a6 --- /dev/null +++ b/vendor/github.com/containers/common/pkg/report/template.go @@ -0,0 +1,114 @@ +package report + +import ( + "reflect" + "strings" + "text/template" + + "github.com/containers/common/pkg/report/camelcase" +) + +// Template embeds template.Template to add functionality to methods +type Template struct { + *template.Template + isTable bool +} + +// FuncMap is aliased from template.FuncMap +type FuncMap template.FuncMap + +// tableReplacer will remove 'table ' prefix and clean up tabs +var tableReplacer = strings.NewReplacer( + "table ", "", + `\t`, "\t", + `\n`, "\n", + " ", "\t", +) + +// escapedReplacer will clean up escaped characters from CLI +var escapedReplacer = strings.NewReplacer( + `\t`, "\t", + `\n`, "\n", +) + +// NormalizeFormat reads given go template format provided by CLI and munges it into what we need +func NormalizeFormat(format string) string { + var f string + // two replacers used so we only remove the prefix keyword `table` + if strings.HasPrefix(format, "table ") { + f = tableReplacer.Replace(format) + } else { + f = escapedReplacer.Replace(format) + } + + if !strings.HasSuffix(f, "\n") { + f += "\n" + } + + return f +} + +// Headers queries the interface for field names. +// Array of map is returned to support range templates +// Note: unexported fields can be supported by adding field to overrides +// Note: It is left to the developer to write out said headers +// Podman commands use the general rules of: +// 1) unchanged --format includes headers +// 2) --format '{{.ID}" # no headers +// 3) --format 'table {{.ID}}' # includes headers +func Headers(object interface{}, overrides map[string]string) []map[string]string { + value := reflect.ValueOf(object) + if value.Kind() == reflect.Ptr { + value = value.Elem() + } + + // Column header will be field name upper-cased. + headers := make(map[string]string, value.NumField()) + for i := 0; i < value.Type().NumField(); i++ { + field := value.Type().Field(i) + // Recurse to find field names from promoted structs + if field.Type.Kind() == reflect.Struct && field.Anonymous { + h := Headers(reflect.New(field.Type).Interface(), nil) + for k, v := range h[0] { + headers[k] = v + } + continue + } + name := strings.Join(camelcase.Split(field.Name), " ") + headers[field.Name] = strings.ToUpper(name) + } + + if len(overrides) > 0 { + // Override column header as provided + for k, v := range overrides { + headers[k] = strings.ToUpper(v) + } + } + return []map[string]string{headers} +} + +// NewTemplate creates a new template object +func NewTemplate(name string) *Template { + return &Template{template.New(name), false} +} + +// Parse parses text as a template body for t +func (t *Template) Parse(text string) (*Template, error) { + if strings.HasPrefix(text, "table ") { + t.isTable = true + text = "{{range .}}" + NormalizeFormat(text) + "{{end}}" + } + + tt, err := t.Template.Parse(text) + return &Template{tt, t.isTable}, err +} + +// Funcs adds the elements of the argument map to the template's function map +func (t *Template) Funcs(funcMap FuncMap) *Template { + return &Template{t.Template.Funcs(template.FuncMap(funcMap)), t.isTable} +} + +// IsTable returns true if format string defines a "table" +func (t *Template) IsTable() bool { + return t.isTable +} diff --git a/vendor/github.com/containers/common/pkg/report/validate.go b/vendor/github.com/containers/common/pkg/report/validate.go new file mode 100644 index 000000000..a5eac5328 --- /dev/null +++ b/vendor/github.com/containers/common/pkg/report/validate.go @@ -0,0 +1,13 @@ +package report + +import "regexp" + +var jsonRegex = regexp.MustCompile(`^\s*(json|{{\s*json\s*(\.)?\s*}})\s*$`) + +// JSONFormat test CLI --format string to be a JSON request +// if report.IsJSON(cmd.Flag("format").Value.String()) { +// ... process JSON and output +// } +func IsJSON(s string) bool { + return jsonRegex.MatchString(s) +} diff --git a/vendor/github.com/containers/common/pkg/report/writer.go b/vendor/github.com/containers/common/pkg/report/writer.go new file mode 100644 index 000000000..360ef8265 --- /dev/null +++ b/vendor/github.com/containers/common/pkg/report/writer.go @@ -0,0 +1,27 @@ +package report + +import ( + "io" + "text/tabwriter" +) + +// Writer aliases tabwriter.Writer to provide Podman defaults +type Writer struct { + *tabwriter.Writer +} + +// NewWriter initializes a new report.Writer with given values +func NewWriter(output io.Writer, minwidth, tabwidth, padding int, padchar byte, flags uint) (*Writer, error) { + t := tabwriter.NewWriter(output, minwidth, tabwidth, padding, padchar, flags) + return &Writer{t}, nil +} + +// NewWriterDefault initializes a new report.Writer with Podman defaults +func NewWriterDefault(output io.Writer) (*Writer, error) { + return NewWriter(output, 12, 2, 2, ' ', 0) +} + +// Flush any output left in buffers +func (w *Writer) Flush() error { + return w.Writer.Flush() +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 9840261a9..6d8a7b8a4 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -94,6 +94,8 @@ github.com/containers/common/pkg/capabilities github.com/containers/common/pkg/cgroupv2 github.com/containers/common/pkg/completion github.com/containers/common/pkg/config +github.com/containers/common/pkg/report +github.com/containers/common/pkg/report/camelcase github.com/containers/common/pkg/retry github.com/containers/common/pkg/seccomp github.com/containers/common/pkg/sysinfo -- cgit v1.2.3-54-g00ecf