From 405565c005a508675f3725fd2eef467dbbfbf02c Mon Sep 17 00:00:00 2001 From: Akihiro Suda Date: Sun, 31 Jul 2022 15:53:11 +0900 Subject: [PATCH 1/4] buildctl: expose parseTemplate() Signed-off-by: Akihiro Suda --- cmd/buildctl/common/common.go | 26 ++++++++++++++++++++++++++ cmd/buildctl/debug/workers.go | 27 +-------------------------- 2 files changed, 27 insertions(+), 26 deletions(-) diff --git a/cmd/buildctl/common/common.go b/cmd/buildctl/common/common.go index e1013160a41e..3a691dc0bac7 100644 --- a/cmd/buildctl/common/common.go +++ b/cmd/buildctl/common/common.go @@ -1,10 +1,14 @@ package common import ( + "bytes" "context" + "encoding/json" "net/url" "os" "path/filepath" + "strings" + "text/template" "time" "github.com/moby/buildkit/client" @@ -88,3 +92,25 @@ func ResolveClient(c *cli.Context) (*client.Client, error) { return client.New(ctx, c.GlobalString("addr"), opts...) } + +func ParseTemplate(format string) (*template.Template, error) { + // aliases is from https://github.com/containerd/nerdctl/blob/v0.17.1/cmd/nerdctl/fmtutil.go#L116-L126 (Apache License 2.0) + aliases := map[string]string{ + "json": "{{json .}}", + } + if alias, ok := aliases[format]; ok { + format = alias + } + // funcs is from https://github.com/docker/cli/blob/v20.10.12/templates/templates.go#L12-L20 (Apache License 2.0) + funcs := template.FuncMap{ + "json": func(v interface{}) string { + buf := &bytes.Buffer{} + enc := json.NewEncoder(buf) + enc.SetEscapeHTML(false) + enc.Encode(v) + // Remove the trailing new line added by the encoder + return strings.TrimSpace(buf.String()) + }, + } + return template.New("").Funcs(funcs).Parse(format) +} diff --git a/cmd/buildctl/debug/workers.go b/cmd/buildctl/debug/workers.go index dbe4499f8e78..9a68d34d532b 100644 --- a/cmd/buildctl/debug/workers.go +++ b/cmd/buildctl/debug/workers.go @@ -1,15 +1,12 @@ package debug import ( - "bytes" "context" - "encoding/json" "fmt" "os" "sort" "strings" "text/tabwriter" - "text/template" "github.com/containerd/containerd/platforms" "github.com/moby/buildkit/client" @@ -54,7 +51,7 @@ func listWorkers(clicontext *cli.Context) error { if clicontext.Bool("verbose") { logrus.Debug("Ignoring --verbose") } - tmpl, err := parseTemplate(format) + tmpl, err := bccommon.ParseTemplate(format) if err != nil { return err } @@ -137,25 +134,3 @@ func joinPlatforms(p []ocispecs.Platform) string { } return strings.Join(str, ",") } - -func parseTemplate(format string) (*template.Template, error) { - // aliases is from https://github.com/containerd/nerdctl/blob/v0.17.1/cmd/nerdctl/fmtutil.go#L116-L126 (Apache License 2.0) - aliases := map[string]string{ - "json": "{{json .}}", - } - if alias, ok := aliases[format]; ok { - format = alias - } - // funcs is from https://github.com/docker/cli/blob/v20.10.12/templates/templates.go#L12-L20 (Apache License 2.0) - funcs := template.FuncMap{ - "json": func(v interface{}) string { - buf := &bytes.Buffer{} - enc := json.NewEncoder(buf) - enc.SetEscapeHTML(false) - enc.Encode(v) - // Remove the trailing new line added by the encoder - return strings.TrimSpace(buf.String()) - }, - } - return template.New("").Funcs(funcs).Parse(format) -} From ddd960c43ed1c1d52d5042d1c6d46e0c4d9f024c Mon Sep 17 00:00:00 2001 From: Akihiro Suda Date: Sun, 31 Jul 2022 15:40:31 +0900 Subject: [PATCH 2/4] buildctl du: support `--format {{json .}}` Signed-off-by: Akihiro Suda --- client/diskusage.go | 22 +++++++++++----------- cmd/buildctl/diskusage.go | 20 ++++++++++++++++++++ 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/client/diskusage.go b/client/diskusage.go index 2a2373f9d36a..3580a0cdf492 100644 --- a/client/diskusage.go +++ b/client/diskusage.go @@ -10,18 +10,18 @@ import ( ) type UsageInfo struct { - ID string - Mutable bool - InUse bool - Size int64 + ID string `json:"id"` + Mutable bool `json:"mutable"` + InUse bool `json:"inUse"` + Size int64 `json:"size"` - CreatedAt time.Time - LastUsedAt *time.Time - UsageCount int - Parents []string - Description string - RecordType UsageRecordType - Shared bool + CreatedAt time.Time `json:"createdAt"` + LastUsedAt *time.Time `json:"lastUsedAt"` + UsageCount int `json:"usageCount"` + Parents []string `json:"parents"` + Description string `json:"description"` + RecordType UsageRecordType `json:"recordType"` + Shared bool `json:"shared"` } func (c *Client) DiskUsage(ctx context.Context, opts ...DiskUsageOption) ([]*UsageInfo, error) { diff --git a/cmd/buildctl/diskusage.go b/cmd/buildctl/diskusage.go index 48f64cfa4cf4..8c00fcfe4e77 100644 --- a/cmd/buildctl/diskusage.go +++ b/cmd/buildctl/diskusage.go @@ -9,6 +9,7 @@ import ( "github.com/moby/buildkit/client" bccommon "github.com/moby/buildkit/cmd/buildctl/common" + "github.com/sirupsen/logrus" "github.com/tonistiigi/units" "github.com/urfave/cli" ) @@ -26,6 +27,10 @@ var diskUsageCommand = cli.Command{ Name: "verbose, v", Usage: "Verbose output", }, + cli.StringFlag{ + Name: "format", + Usage: "Format the output using the given Go template, e.g, '{{json .}}'", + }, }, } @@ -40,6 +45,21 @@ func diskUsage(clicontext *cli.Context) error { return err } + if format := clicontext.String("format"); format != "" { + if clicontext.Bool("verbose") { + logrus.Debug("Ignoring --verbose") + } + tmpl, err := bccommon.ParseTemplate(format) + if err != nil { + return err + } + if err := tmpl.Execute(clicontext.App.Writer, du); err != nil { + return err + } + _, err = fmt.Fprintf(clicontext.App.Writer, "\n") + return err + } + tw := tabwriter.NewWriter(os.Stdout, 1, 8, 1, '\t', 0) if clicontext.Bool("verbose") { From 0ec936ad4f386c14cc268388ffbef4f0e8406ef2 Mon Sep 17 00:00:00 2001 From: Akihiro Suda Date: Sun, 31 Jul 2022 15:51:10 +0900 Subject: [PATCH 3/4] buildctl prune: support `--format {{json .}}` Signed-off-by: Akihiro Suda --- cmd/buildctl/prune.go | 82 +++++++++++++++++++++++++++++-------------- 1 file changed, 56 insertions(+), 26 deletions(-) diff --git a/cmd/buildctl/prune.go b/cmd/buildctl/prune.go index fc3adeaf0e4a..31c917262514 100644 --- a/cmd/buildctl/prune.go +++ b/cmd/buildctl/prune.go @@ -7,6 +7,7 @@ import ( "github.com/moby/buildkit/client" bccommon "github.com/moby/buildkit/cmd/buildctl/common" + "github.com/sirupsen/logrus" "github.com/tonistiigi/units" "github.com/urfave/cli" ) @@ -36,6 +37,10 @@ var pruneCommand = cli.Command{ Name: "verbose, v", Usage: "Verbose output", }, + cli.StringFlag{ + Name: "format", + Usage: "Format the output using the given Go template, e.g, '{{json .}}'", + }, }, } @@ -47,27 +52,7 @@ func prune(clicontext *cli.Context) error { ch := make(chan client.UsageInfo) printed := make(chan struct{}) - - tw := tabwriter.NewWriter(os.Stdout, 1, 8, 1, '\t', 0) - first := true - total := int64(0) - - go func() { - defer close(printed) - for du := range ch { - total += du.Size - if clicontext.Bool("verbose") { - printVerbose(tw, []*client.UsageInfo{&du}) - } else { - if first { - printTableHeader(tw) - first = false - } - printTableRow(tw, &du) - tw.Flush() - } - } - }() + var summarizer func() opts := []client.PruneOption{ client.WithFilter(clicontext.StringSlice("filter")), @@ -78,16 +63,61 @@ func prune(clicontext *cli.Context) error { opts = append(opts, client.PruneAll) } + if format := clicontext.String("format"); format != "" { + if clicontext.Bool("verbose") { + logrus.Debug("Ignoring --verbose") + } + tmpl, err := bccommon.ParseTemplate(format) + if err != nil { + return err + } + go func() { + defer close(printed) + for du := range ch { + // Unlike `buildctl du`, the template is applied to a UsageInfo, not to a slice of UsageInfo + if err := tmpl.Execute(clicontext.App.Writer, du); err != nil { + panic(err) + } + if _, err = fmt.Fprintf(clicontext.App.Writer, "\n"); err != nil { + panic(err) + } + } + }() + } else { + tw := tabwriter.NewWriter(os.Stdout, 1, 8, 1, '\t', 0) + first := true + total := int64(0) + go func() { + defer close(printed) + for du := range ch { + total += du.Size + if clicontext.Bool("verbose") { + printVerbose(tw, []*client.UsageInfo{&du}) + } else { + if first { + printTableHeader(tw) + first = false + } + printTableRow(tw, &du) + tw.Flush() + } + } + }() + summarizer = func() { + tw = tabwriter.NewWriter(os.Stdout, 1, 8, 1, '\t', 0) + fmt.Fprintf(tw, "Total:\t%.2f\n", units.Bytes(total)) + tw.Flush() + } + } + err = c.Prune(bccommon.CommandContext(clicontext), ch, opts...) close(ch) <-printed if err != nil { return err } - - tw = tabwriter.NewWriter(os.Stdout, 1, 8, 1, '\t', 0) - fmt.Fprintf(tw, "Total:\t%.2f\n", units.Bytes(total)) - tw.Flush() - + if summarizer != nil { + summarizer() + } return nil } From bacaef28dd6f139cf4d8931c3714eebeb179a9b1 Mon Sep 17 00:00:00 2001 From: Akihiro Suda Date: Sun, 31 Jul 2022 15:55:25 +0900 Subject: [PATCH 4/4] buildctl debug info: support `--format {{json .}}` Signed-off-by: Akihiro Suda --- client/info.go | 8 ++++---- cmd/buildctl/debug/info.go | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/client/info.go b/client/info.go index 68f883d56ff8..d957195fc27e 100644 --- a/client/info.go +++ b/client/info.go @@ -9,13 +9,13 @@ import ( ) type Info struct { - BuildkitVersion BuildkitVersion + BuildkitVersion BuildkitVersion `json:"buildkitVersion"` } type BuildkitVersion struct { - Package string - Version string - Revision string + Package string `json:"package"` + Version string `json:"version"` + Revision string `json:"revision"` } func (c *Client) Info(ctx context.Context) (*Info, error) { diff --git a/cmd/buildctl/debug/info.go b/cmd/buildctl/debug/info.go index 6e9af149c844..3f702ff3aa0b 100644 --- a/cmd/buildctl/debug/info.go +++ b/cmd/buildctl/debug/info.go @@ -13,6 +13,12 @@ var InfoCommand = cli.Command{ Name: "info", Usage: "display internal information", Action: info, + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "format", + Usage: "Format the output using the given Go template, e.g, '{{json .}}'", + }, + }, } func info(clicontext *cli.Context) error { @@ -24,6 +30,18 @@ func info(clicontext *cli.Context) error { if err != nil { return err } + if format := clicontext.String("format"); format != "" { + tmpl, err := bccommon.ParseTemplate(format) + if err != nil { + return err + } + if err := tmpl.Execute(clicontext.App.Writer, res); err != nil { + return err + } + _, err = fmt.Fprintf(clicontext.App.Writer, "\n") + return err + } + w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', 0) _, _ = fmt.Fprintf(w, "BuildKit:\t%s %s %s\n", res.BuildkitVersion.Package, res.BuildkitVersion.Version, res.BuildkitVersion.Revision) return w.Flush()