From cb0e9d997ec386804623459b1d7ed8eb894c21e8 Mon Sep 17 00:00:00 2001 From: CrazyMax <1951866+crazy-max@users.noreply.github.com> Date: Sun, 10 Mar 2024 19:01:29 +0100 Subject: [PATCH] bake: fix output handling for push and load Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com> --- bake/bake.go | 52 +++++++++++++---- bake/bake_test.go | 106 ++++++++++++++++++++++++++++++++++ commands/bake.go | 4 +- docs/reference/buildx_bake.md | 1 + 4 files changed, 150 insertions(+), 13 deletions(-) diff --git a/bake/bake.go b/bake/bake.go index 8facfb79c193..b285c33e8272 100644 --- a/bake/bake.go +++ b/bake/bake.go @@ -898,14 +898,36 @@ func (t *Target) AddOverrides(overrides map[string]Override) error { if err != nil { return errors.Errorf("invalid value %s for boolean key push", value) } - if len(t.Outputs) == 0 { - t.Outputs = append(t.Outputs, "type=image,push=true") - } else { - for i, output := range t.Outputs { - if typ := parseOutputType(output); typ == "image" || typ == "registry" { - t.Outputs[i] = t.Outputs[i] + ",push=" + value + var pushUsed bool + for i, output := range t.Outputs { + if typ := parseOutputType(output); typ == "image" || typ == "registry" { + t.Outputs[i] = t.Outputs[i] + ",push=" + value + pushUsed = true + } + } + if !pushUsed { + t.Outputs = append(t.Outputs, "type=image,push="+value) + } + case "load": + load, err := strconv.ParseBool(value) + if err != nil { + return errors.Errorf("invalid value %s for boolean key load", value) + } + if load { + var loadUsed bool + for _, output := range t.Outputs { + if typ := parseOutputType(output); typ == "docker" { + if v := parseOutput(output); v != nil { + if _, ok := v["dest"]; !ok { + loadUsed = true + break + } + } } } + if !loadUsed { + t.Outputs = append(t.Outputs, "type=docker") + } } default: return errors.Errorf("unknown key: %s", keys[0]) @@ -1394,18 +1416,26 @@ func removeAttestDupes(s []string) []string { return res } -func parseOutputType(str string) string { +func parseOutput(str string) map[string]string { csvReader := csv.NewReader(strings.NewReader(str)) fields, err := csvReader.Read() if err != nil { - return "" + return nil } + res := map[string]string{} for _, field := range fields { parts := strings.SplitN(field, "=", 2) if len(parts) == 2 { - if parts[0] == "type" { - return parts[1] - } + res[parts[0]] = parts[1] + } + } + return res +} + +func parseOutputType(str string) string { + if out := parseOutput(str); out != nil { + if v, ok := out["type"]; ok { + return v } } return "" diff --git a/bake/bake_test.go b/bake/bake_test.go index 39f9ad057774..ac6e369234b8 100644 --- a/bake/bake_test.go +++ b/bake/bake_test.go @@ -259,6 +259,112 @@ func TestPushOverride(t *testing.T) { require.Equal(t, 1, len(m["app"].Outputs)) require.Equal(t, "type=image,push=true", m["app"].Outputs[0]) + + fp = File{ + Name: "docker-bake.hcl", + Data: []byte( + `target "foo" { + output = [ "type=local,dest=out" ] + } + target "bar" { + }`), + } + ctx = context.TODO() + m, _, err = ReadTargets(ctx, []File{fp}, []string{"foo", "bar"}, []string{"*.push=true"}, nil) + require.NoError(t, err) + + require.Equal(t, 2, len(m)) + require.Equal(t, 2, len(m["foo"].Outputs)) + require.Equal(t, []string{"type=local,dest=out", "type=image,push=true"}, m["foo"].Outputs) + require.Equal(t, 1, len(m["bar"].Outputs)) + require.Equal(t, []string{"type=image,push=true"}, m["bar"].Outputs) +} + +func TestLoadOverride(t *testing.T) { + t.Parallel() + + fp := File{ + Name: "docker-bake.hcl", + Data: []byte( + `target "app" { + }`), + } + ctx := context.TODO() + m, _, err := ReadTargets(ctx, []File{fp}, []string{"app"}, []string{"*.load=true"}, nil) + require.NoError(t, err) + + require.Equal(t, 1, len(m["app"].Outputs)) + require.Equal(t, "type=docker", m["app"].Outputs[0]) + + fp = File{ + Name: "docker-bake.hcl", + Data: []byte( + `target "app" { + output = ["type=image"] + }`), + } + ctx = context.TODO() + m, _, err = ReadTargets(ctx, []File{fp}, []string{"app"}, []string{"*.load=true"}, nil) + require.NoError(t, err) + + require.Equal(t, 2, len(m["app"].Outputs)) + require.Equal(t, []string{"type=image", "type=docker"}, m["app"].Outputs) + + fp = File{ + Name: "docker-bake.hcl", + Data: []byte( + `target "app" { + output = ["type=docker,dest=out"] + }`), + } + ctx = context.TODO() + m, _, err = ReadTargets(ctx, []File{fp}, []string{"app"}, []string{"*.load=true"}, nil) + require.NoError(t, err) + + require.Equal(t, 2, len(m["app"].Outputs)) + require.Equal(t, []string{"type=docker,dest=out", "type=docker"}, m["app"].Outputs) + + fp = File{ + Name: "docker-bake.hcl", + Data: []byte( + `target "foo" { + output = [ "type=local,dest=out" ] + } + target "bar" { + }`), + } + ctx = context.TODO() + m, _, err = ReadTargets(ctx, []File{fp}, []string{"foo", "bar"}, []string{"*.load=true"}, nil) + require.NoError(t, err) + + require.Equal(t, 2, len(m)) + require.Equal(t, 2, len(m["foo"].Outputs)) + require.Equal(t, []string{"type=local,dest=out", "type=docker"}, m["foo"].Outputs) + require.Equal(t, 1, len(m["bar"].Outputs)) + require.Equal(t, []string{"type=docker"}, m["bar"].Outputs) +} + +func TestLoadAndPushOverride(t *testing.T) { + t.Parallel() + + fp := File{ + Name: "docker-bake.hcl", + Data: []byte( + `target "foo" { + output = [ "type=local,dest=out" ] + } + target "bar" { + }`), + } + ctx := context.TODO() + m, _, err := ReadTargets(ctx, []File{fp}, []string{"foo", "bar"}, []string{"*.load=true", "*.push=true"}, nil) + require.NoError(t, err) + + require.Equal(t, 2, len(m)) + require.Equal(t, 3, len(m["foo"].Outputs)) + require.Equal(t, []string{"type=local,dest=out", "type=docker", "type=image,push=true"}, m["foo"].Outputs) + require.Equal(t, 2, len(m["bar"].Outputs)) + require.Equal(t, []string{"type=docker", "type=image,push=true"}, m["bar"].Outputs) } func TestReadTargetsCompose(t *testing.T) { diff --git a/commands/bake.go b/commands/bake.go index 9b3ee5c9daeb..3747add79505 100644 --- a/commands/bake.go +++ b/commands/bake.go @@ -72,10 +72,10 @@ func runBake(ctx context.Context, dockerCli command.Cli, targets []string, in ba overrides := in.overrides if in.exportPush { - overrides = append(overrides, "*.output=type=registry") + overrides = append(overrides, "*.push=true") } if in.exportLoad { - overrides = append(overrides, "*.output=type=docker") + overrides = append(overrides, "*.load=true") } if cFlags.noCache != nil { overrides = append(overrides, fmt.Sprintf("*.no-cache=%t", *cFlags.noCache)) diff --git a/docs/reference/buildx_bake.md b/docs/reference/buildx_bake.md index 30d7ccfded15..21c7571dbeb7 100644 --- a/docs/reference/buildx_bake.md +++ b/docs/reference/buildx_bake.md @@ -162,6 +162,7 @@ You can override the following fields: * `context` * `dockerfile` * `labels` +* `load` * `no-cache` * `no-cache-filter` * `output`