diff --git a/build/build.go b/build/build.go index f0badc95982d..a2444e5452f1 100644 --- a/build/build.go +++ b/build/build.go @@ -432,6 +432,41 @@ func toRepoOnly(in string) (string, error) { return strings.Join(out, ","), nil } +func prepareMultiDriverExports(so *client.SolveOpt, pushNames *string, insecurePush *bool) error { + var pushPrepared bool + for i := range so.Exports { + e := &so.Exports[i] + switch e.Type { + case "oci", "tar": + return errors.Errorf("%s for multi-node builds currently not supported", e.Type) + case "image": + if pushPrepared { + continue + } + if ok, _ := strconv.ParseBool(e.Attrs["push"]); !ok { + continue + } + if *pushNames == "" { + *pushNames = e.Attrs["name"] + if *pushNames == "" { + return errors.Errorf("tag is needed when pushing to registry") + } + if ok, _ := strconv.ParseBool(e.Attrs["registry.insecure"]); ok { + *insecurePush = true + } + } + names, err := toRepoOnly(e.Attrs["name"]) + if err != nil { + return err + } + e.Attrs["name"] = names + e.Attrs["push-by-digest"] = "true" + pushPrepared = true + } + } + return nil +} + type ( EvaluateFunc func(ctx context.Context, name string, c gateway.Client, res *gateway.Result, opt Options) error Handler struct { @@ -566,30 +601,8 @@ func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opts map[ node := dp.Node() so := reqForNodes[k][i].so if multiDriver { - for i, e := range so.Exports { - switch e.Type { - case "oci", "tar": - return errors.Errorf("%s for multi-node builds currently not supported", e.Type) - case "image": - if pushNames == "" && e.Attrs["push"] != "" { - if ok, _ := strconv.ParseBool(e.Attrs["push"]); ok { - pushNames = e.Attrs["name"] - if pushNames == "" { - return errors.Errorf("tag is needed when pushing to registry") - } - names, err := toRepoOnly(e.Attrs["name"]) - if err != nil { - return err - } - if ok, _ := strconv.ParseBool(e.Attrs["registry.insecure"]); ok { - insecurePush = true - } - e.Attrs["name"] = names - e.Attrs["push-by-digest"] = "true" - so.Exports[i].Attrs = e.Attrs - } - } - } + if err := prepareMultiDriverExports(so, &pushNames, &insecurePush); err != nil { + return err } } diff --git a/build/build_test.go b/build/build_test.go index d7f65fb4e092..30450584782f 100644 --- a/build/build_test.go +++ b/build/build_test.go @@ -35,6 +35,48 @@ func (d warnOutputDriver) IsMobyDriver() bool { return d.moby } +func TestPrepareMultiDriverExportsForEveryNode(t *testing.T) { + const taggedName = "registry.example.com/user/app:latest" + const secondaryName = "registry.example.com/user/secondary:latest" + newSolveOpt := func() *client.SolveOpt { + return &client.SolveOpt{ + Exports: []client.ExportEntry{ + { + Type: "image", + Attrs: map[string]string{ + "name": taggedName, + "push": "true", + "registry.insecure": "true", + }, + }, + { + Type: "image", + Attrs: map[string]string{ + "name": secondaryName, + "push": "true", + }, + }, + }, + } + } + solveOpts := []*client.SolveOpt{newSolveOpt(), newSolveOpt()} + + var pushNames string + var insecurePush bool + for _, so := range solveOpts { + require.NoError(t, prepareMultiDriverExports(so, &pushNames, &insecurePush)) + } + + require.Equal(t, taggedName, pushNames) + require.True(t, insecurePush) + for _, so := range solveOpts { + require.Equal(t, "registry.example.com/user/app", so.Exports[0].Attrs["name"]) + require.Equal(t, "true", so.Exports[0].Attrs["push-by-digest"]) + require.Equal(t, secondaryName, so.Exports[1].Attrs["name"]) + require.NotContains(t, so.Exports[1].Attrs, "push-by-digest") + } +} + func TestWarnOnNoOutput(t *testing.T) { cloudNodes := []builder.Node{{Driver: newWarnOutputDriver("cloud", false)}} mobyNodes := []builder.Node{{Driver: newWarnOutputDriver("docker", true)}}