mirror of https://github.com/docker/buildx.git
metrics: add metrics for bake command
This adds metrics for the bake command using a different method of calculating the build identifier but with the same attributes otherwise. Signed-off-by: Jonathan A. Sternberg <jonathan.sternberg@docker.com>
This commit is contained in:
parent
9a0c320588
commit
58571ff6d6
118
commands/bake.go
118
commands/bake.go
|
@ -4,12 +4,16 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"cmp"
|
"cmp"
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"slices"
|
"slices"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"text/tabwriter"
|
"text/tabwriter"
|
||||||
|
|
||||||
"github.com/containerd/console"
|
"github.com/containerd/console"
|
||||||
|
@ -26,6 +30,7 @@ import (
|
||||||
"github.com/docker/buildx/util/confutil"
|
"github.com/docker/buildx/util/confutil"
|
||||||
"github.com/docker/buildx/util/desktop"
|
"github.com/docker/buildx/util/desktop"
|
||||||
"github.com/docker/buildx/util/dockerutil"
|
"github.com/docker/buildx/util/dockerutil"
|
||||||
|
"github.com/docker/buildx/util/osutil"
|
||||||
"github.com/docker/buildx/util/progress"
|
"github.com/docker/buildx/util/progress"
|
||||||
"github.com/docker/buildx/util/tracing"
|
"github.com/docker/buildx/util/tracing"
|
||||||
"github.com/docker/cli/cli/command"
|
"github.com/docker/cli/cli/command"
|
||||||
|
@ -33,6 +38,7 @@ import (
|
||||||
"github.com/moby/buildkit/util/progress/progressui"
|
"github.com/moby/buildkit/util/progress/progressui"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
)
|
)
|
||||||
|
|
||||||
type bakeOptions struct {
|
type bakeOptions struct {
|
||||||
|
@ -52,6 +58,8 @@ type bakeOptions struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func runBake(ctx context.Context, dockerCli command.Cli, targets []string, in bakeOptions, cFlags commonFlags) (err error) {
|
func runBake(ctx context.Context, dockerCli command.Cli, targets []string, in bakeOptions, cFlags commonFlags) (err error) {
|
||||||
|
mp := dockerCli.MeterProvider()
|
||||||
|
|
||||||
ctx, end, err := tracing.TraceCurrentCommand(ctx, "bake")
|
ctx, end, err := tracing.TraceCurrentCommand(ctx, "bake")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -60,22 +68,7 @@ func runBake(ctx context.Context, dockerCli command.Cli, targets []string, in ba
|
||||||
end(err)
|
end(err)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
var url string
|
url, cmdContext, targets := bakeArgs(targets)
|
||||||
cmdContext := "cwd://"
|
|
||||||
|
|
||||||
if len(targets) > 0 {
|
|
||||||
if build.IsRemoteURL(targets[0]) {
|
|
||||||
url = targets[0]
|
|
||||||
targets = targets[1:]
|
|
||||||
if len(targets) > 0 {
|
|
||||||
if build.IsRemoteURL(targets[0]) {
|
|
||||||
cmdContext = targets[0]
|
|
||||||
targets = targets[1:]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(targets) == 0 {
|
if len(targets) == 0 {
|
||||||
targets = []string{"default"}
|
targets = []string{"default"}
|
||||||
}
|
}
|
||||||
|
@ -116,6 +109,7 @@ func runBake(ctx context.Context, dockerCli command.Cli, targets []string, in ba
|
||||||
var progressConsoleDesc, progressTextDesc string
|
var progressConsoleDesc, progressTextDesc string
|
||||||
|
|
||||||
// instance only needed for reading remote bake files or building
|
// instance only needed for reading remote bake files or building
|
||||||
|
var driverType string
|
||||||
if url != "" || !in.printOnly {
|
if url != "" || !in.printOnly {
|
||||||
b, err := builder.New(dockerCli,
|
b, err := builder.New(dockerCli,
|
||||||
builder.WithName(in.builder),
|
builder.WithName(in.builder),
|
||||||
|
@ -133,17 +127,20 @@ func runBake(ctx context.Context, dockerCli command.Cli, targets []string, in ba
|
||||||
}
|
}
|
||||||
progressConsoleDesc = fmt.Sprintf("%s:%s", b.Driver, b.Name)
|
progressConsoleDesc = fmt.Sprintf("%s:%s", b.Driver, b.Name)
|
||||||
progressTextDesc = fmt.Sprintf("building with %q instance using %s driver", b.Name, b.Driver)
|
progressTextDesc = fmt.Sprintf("building with %q instance using %s driver", b.Name, b.Driver)
|
||||||
|
driverType = b.Driver
|
||||||
}
|
}
|
||||||
|
|
||||||
var term bool
|
var term bool
|
||||||
if _, err := console.ConsoleFromFile(os.Stderr); err == nil {
|
if _, err := console.ConsoleFromFile(os.Stderr); err == nil {
|
||||||
term = true
|
term = true
|
||||||
}
|
}
|
||||||
|
attributes := bakeMetricAttributes(dockerCli, driverType, url, cmdContext, targets, &in)
|
||||||
|
|
||||||
progressMode := progressui.DisplayMode(cFlags.progress)
|
progressMode := progressui.DisplayMode(cFlags.progress)
|
||||||
var printer *progress.Printer
|
var printer *progress.Printer
|
||||||
printer, err = progress.NewPrinter(ctx2, os.Stderr, progressMode,
|
printer, err = progress.NewPrinter(ctx2, os.Stderr, progressMode,
|
||||||
progress.WithDesc(progressTextDesc, progressConsoleDesc),
|
progress.WithDesc(progressTextDesc, progressConsoleDesc),
|
||||||
|
progress.WithMetrics(mp, attributes),
|
||||||
progress.WithOnClose(func() {
|
progress.WithOnClose(func() {
|
||||||
printWarnings(os.Stderr, printer.Warnings(), progressMode)
|
printWarnings(os.Stderr, printer.Warnings(), progressMode)
|
||||||
}),
|
}),
|
||||||
|
@ -241,12 +238,18 @@ func runBake(ctx context.Context, dockerCli command.Cli, targets []string, in ba
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
done := timeBuildCommand(mp, attributes)
|
||||||
resp, retErr := build.Build(ctx, nodes, bo, dockerutil.NewClient(dockerCli), confutil.ConfigDir(dockerCli), printer)
|
resp, retErr := build.Build(ctx, nodes, bo, dockerutil.NewClient(dockerCli), confutil.ConfigDir(dockerCli), printer)
|
||||||
if err := printer.Wait(); retErr == nil {
|
if err := printer.Wait(); retErr == nil {
|
||||||
retErr = err
|
retErr = err
|
||||||
}
|
}
|
||||||
if retErr != nil {
|
if retErr != nil {
|
||||||
return wrapBuildError(retErr, true)
|
err = wrapBuildError(retErr, true)
|
||||||
|
}
|
||||||
|
done(err)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if progressMode != progressui.QuietMode && progressMode != progressui.RawJSONMode {
|
if progressMode != progressui.QuietMode && progressMode != progressui.RawJSONMode {
|
||||||
|
@ -266,7 +269,7 @@ func runBake(ctx context.Context, dockerCli command.Cli, targets []string, in ba
|
||||||
}
|
}
|
||||||
|
|
||||||
var callFormatJSON bool
|
var callFormatJSON bool
|
||||||
var jsonResults = map[string]map[string]any{}
|
jsonResults := map[string]map[string]any{}
|
||||||
if callFunc != nil {
|
if callFunc != nil {
|
||||||
callFormatJSON = callFunc.Format == "json"
|
callFormatJSON = callFunc.Format == "json"
|
||||||
}
|
}
|
||||||
|
@ -454,6 +457,21 @@ func saveLocalStateGroup(dockerCli command.Cli, in bakeOptions, targets []string
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// bakeArgs will retrieve the remote url, command context, and targets
|
||||||
|
// from the command line arguments.
|
||||||
|
func bakeArgs(args []string) (url, cmdContext string, targets []string) {
|
||||||
|
cmdContext, targets = "cwd://", args
|
||||||
|
if len(targets) == 0 || !build.IsRemoteURL(targets[0]) {
|
||||||
|
return url, cmdContext, targets
|
||||||
|
}
|
||||||
|
url, targets = targets[0], targets[1:]
|
||||||
|
if len(targets) == 0 || !build.IsRemoteURL(targets[0]) {
|
||||||
|
return url, cmdContext, targets
|
||||||
|
}
|
||||||
|
cmdContext, targets = targets[0], targets[1:]
|
||||||
|
return url, cmdContext, targets
|
||||||
|
}
|
||||||
|
|
||||||
func readBakeFiles(ctx context.Context, nodes []builder.Node, url string, names []string, stdin io.Reader, pw progress.Writer) (files []bake.File, inp *bake.Input, err error) {
|
func readBakeFiles(ctx context.Context, nodes []builder.Node, url string, names []string, stdin io.Reader, pw progress.Writer) (files []bake.File, inp *bake.Input, err error) {
|
||||||
var lnames []string // local
|
var lnames []string // local
|
||||||
var rnames []string // remote
|
var rnames []string // remote
|
||||||
|
@ -568,3 +586,67 @@ func printTargetList(w io.Writer, cfg *bake.Config) error {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func bakeMetricAttributes(dockerCli command.Cli, driverType, url, cmdContext string, targets []string, options *bakeOptions) attribute.Set {
|
||||||
|
return attribute.NewSet(
|
||||||
|
commandNameAttribute.String("bake"),
|
||||||
|
attribute.Stringer(string(commandOptionsHash), &bakeOptionsHash{
|
||||||
|
bakeOptions: options,
|
||||||
|
configDir: confutil.ConfigDir(dockerCli),
|
||||||
|
url: url,
|
||||||
|
cmdContext: cmdContext,
|
||||||
|
targets: targets,
|
||||||
|
}),
|
||||||
|
driverNameAttribute.String(options.builder),
|
||||||
|
driverTypeAttribute.String(driverType),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
type bakeOptionsHash struct {
|
||||||
|
*bakeOptions
|
||||||
|
configDir string
|
||||||
|
url string
|
||||||
|
cmdContext string
|
||||||
|
targets []string
|
||||||
|
result string
|
||||||
|
resultOnce sync.Once
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *bakeOptionsHash) String() string {
|
||||||
|
o.resultOnce.Do(func() {
|
||||||
|
url := o.url
|
||||||
|
cmdContext := o.cmdContext
|
||||||
|
if cmdContext == "cwd://" {
|
||||||
|
// Resolve the directory if the cmdContext is the current working directory.
|
||||||
|
cmdContext = osutil.GetWd()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort the inputs for files and targets since the ordering
|
||||||
|
// doesn't matter, but avoid modifying the original slice.
|
||||||
|
files := immutableSort(o.files)
|
||||||
|
targets := immutableSort(o.targets)
|
||||||
|
|
||||||
|
joinedFiles := strings.Join(files, ",")
|
||||||
|
joinedTargets := strings.Join(targets, ",")
|
||||||
|
salt := confutil.TryNodeIdentifier(o.configDir)
|
||||||
|
|
||||||
|
h := sha256.New()
|
||||||
|
for _, s := range []string{url, cmdContext, joinedFiles, joinedTargets, salt} {
|
||||||
|
_, _ = io.WriteString(h, s)
|
||||||
|
h.Write([]byte{0})
|
||||||
|
}
|
||||||
|
o.result = hex.EncodeToString(h.Sum(nil))
|
||||||
|
})
|
||||||
|
return o.result
|
||||||
|
}
|
||||||
|
|
||||||
|
// immutableSort will sort the entries in s without modifying the original slice.
|
||||||
|
func immutableSort(s []string) []string {
|
||||||
|
if !sort.StringsAreSorted(s) {
|
||||||
|
cpy := make([]string, len(s))
|
||||||
|
copy(cpy, s)
|
||||||
|
sort.Strings(cpy)
|
||||||
|
return cpy
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
|
@ -224,15 +224,22 @@ func (o *buildOptions) toDisplayMode() (progressui.DisplayMode, error) {
|
||||||
return progress, nil
|
return progress, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildMetricAttributes(dockerCli command.Cli, b *builder.Builder, options *buildOptions) attribute.Set {
|
const (
|
||||||
|
commandNameAttribute = attribute.Key("command.name")
|
||||||
|
commandOptionsHash = attribute.Key("command.options.hash")
|
||||||
|
driverNameAttribute = attribute.Key("driver.name")
|
||||||
|
driverTypeAttribute = attribute.Key("driver.type")
|
||||||
|
)
|
||||||
|
|
||||||
|
func buildMetricAttributes(dockerCli command.Cli, driverType string, options *buildOptions) attribute.Set {
|
||||||
return attribute.NewSet(
|
return attribute.NewSet(
|
||||||
attribute.String("command.name", "build"),
|
commandNameAttribute.String("build"),
|
||||||
attribute.Stringer("command.options.hash", &buildOptionsHash{
|
attribute.Stringer(string(commandOptionsHash), &buildOptionsHash{
|
||||||
buildOptions: options,
|
buildOptions: options,
|
||||||
configDir: confutil.ConfigDir(dockerCli),
|
configDir: confutil.ConfigDir(dockerCli),
|
||||||
}),
|
}),
|
||||||
attribute.String("driver.name", options.builder),
|
driverNameAttribute.String(options.builder),
|
||||||
attribute.String("driver.type", b.Driver),
|
driverTypeAttribute.String(driverType),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -308,12 +315,13 @@ func runBuild(ctx context.Context, dockerCli command.Cli, options buildOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
driverType := b.Driver
|
||||||
|
|
||||||
var term bool
|
var term bool
|
||||||
if _, err := console.ConsoleFromFile(os.Stderr); err == nil {
|
if _, err := console.ConsoleFromFile(os.Stderr); err == nil {
|
||||||
term = true
|
term = true
|
||||||
}
|
}
|
||||||
attributes := buildMetricAttributes(dockerCli, b, &options)
|
attributes := buildMetricAttributes(dockerCli, driverType, &options)
|
||||||
|
|
||||||
ctx2, cancel := context.WithCancel(context.TODO())
|
ctx2, cancel := context.WithCancel(context.TODO())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
Loading…
Reference in New Issue