From 938764067650f67514c391362896d9239ab88dae Mon Sep 17 00:00:00 2001 From: Justin Chadwell Date: Thu, 1 Jun 2023 10:36:04 +0200 Subject: [PATCH] bake: use controller build options as an intermediate stage With the previous changes to bring controllerapi.BuildOptions up to date with build.Options, we can have bake generate controllerapi.BuildOptions, and then convert those to build.Option using the controller/build package. This is an intermediate patch, designed to allow us to clean up some shared logic between both build and bake. The next step will be to modify bake to use the controller api, and completely skip the build.Options generation step. Signed-off-by: CrazyMax --- bake/bake.go | 124 +++++++++++++++++++++++---------------------------- 1 file changed, 55 insertions(+), 69 deletions(-) diff --git a/bake/bake.go b/bake/bake.go index 9a507cd6..22d31f96 100644 --- a/bake/bake.go +++ b/bake/bake.go @@ -15,14 +15,11 @@ import ( composecli "github.com/compose-spec/compose-go/cli" "github.com/docker/buildx/bake/hclparser" "github.com/docker/buildx/build" + cbuild "github.com/docker/buildx/controller/build" controllerapi "github.com/docker/buildx/controller/pb" "github.com/docker/buildx/util/buildflags" - "github.com/docker/buildx/util/platformutil" - - "github.com/docker/cli/cli/config" hcl "github.com/hashicorp/hcl/v2" "github.com/moby/buildkit/client/llb" - "github.com/moby/buildkit/session/auth/authprovider" "github.com/pkg/errors" "github.com/zclconf/go-cty/cty" "github.com/zclconf/go-cty/cty/convert" @@ -914,7 +911,11 @@ func (t *Target) GetName(ectx *hcl.EvalContext, block *hcl.Block, loadDeps func( func TargetsToBuildOpt(m map[string]*Target, inp *Input) (map[string]build.Options, error) { m2 := make(map[string]build.Options, len(m)) for k, v := range m { - bo, err := toBuildOpt(v, inp) + opts, err := toControllerOpt(v, inp) + if err != nil { + return nil, err + } + bo, err := cbuild.ToBuildOpts(*opts, nil) if err != nil { return nil, err } @@ -923,14 +924,14 @@ func TargetsToBuildOpt(m map[string]*Target, inp *Input) (map[string]build.Optio return m2, nil } -func updateContext(t *build.Inputs, inp *Input) { +func updateContext(t *controllerapi.Inputs, inp *Input) error { if inp == nil || inp.State == nil { - return + return nil } for k, v := range t.NamedContexts { if v.Path == "." { - t.NamedContexts[k] = build.NamedContext{Path: inp.URL} + t.NamedContexts[k] = &controllerapi.NamedContext{Path: inp.URL} } if strings.HasPrefix(v.Path, "cwd://") || strings.HasPrefix(v.Path, "target:") || strings.HasPrefix(v.Path, "docker-image:") { continue @@ -939,18 +940,22 @@ func updateContext(t *build.Inputs, inp *Input) { continue } st := llb.Scratch().File(llb.Copy(*inp.State, v.Path, "/"), llb.WithCustomNamef("set context %s to %s", k, v.Path)) - t.NamedContexts[k] = build.NamedContext{State: &st} + def, err := st.Marshal(context.TODO()) + if err != nil { + return err + } + t.NamedContexts[k] = &controllerapi.NamedContext{Definition: def.ToPB()} } if t.ContextPath == "." { t.ContextPath = inp.URL - return + return nil } if strings.HasPrefix(t.ContextPath, "cwd://") { - return + return nil } if build.IsRemoteURL(t.ContextPath) { - return + return nil } st := llb.Scratch().File( llb.Copy(*inp.State, t.ContextPath, "/", &llb.CopyInfo{ @@ -958,13 +963,18 @@ func updateContext(t *build.Inputs, inp *Input) { }), llb.WithCustomNamef("set context to %s", t.ContextPath), ) - t.ContextState = &st + def, err := st.Marshal(context.TODO()) + if err != nil { + return err + } + t.ContextDefinition = def.ToPB() + return nil } // validateContextsEntitlements is a basic check to ensure contexts do not // escape local directories when loaded from remote sources. This is to be // replaced with proper entitlements support in the future. -func validateContextsEntitlements(t build.Inputs, inp *Input) error { +func validateContextsEntitlements(t controllerapi.Inputs, inp *Input) error { if inp == nil || inp.State == nil { return nil } @@ -973,13 +983,13 @@ func validateContextsEntitlements(t build.Inputs, inp *Input) error { return nil } } - if t.ContextState == nil { + if t.ContextDefinition == nil { if err := checkPath(t.ContextPath); err != nil { return err } } for _, v := range t.NamedContexts { - if v.State != nil { + if v.Definition != nil { continue } if err := checkPath(v.Path); err != nil { @@ -1019,7 +1029,9 @@ func checkPath(p string) error { return nil } -func toBuildOpt(t *Target, inp *Input) (*build.Options, error) { +func toControllerOpt(t *Target, inp *Input) (*controllerapi.BuildOptions, error) { + var err error + if v := t.Context; v != nil && *v == "-" { return nil, errors.Errorf("context from stdin not allowed in bake") } @@ -1039,24 +1051,24 @@ func toBuildOpt(t *Target, inp *Input) (*build.Options, error) { dockerfilePath = *t.Dockerfile } - bi := build.Inputs{ + bi := controllerapi.Inputs{ ContextPath: contextPath, - DockerfilePath: dockerfilePath, + DockerfileName: dockerfilePath, NamedContexts: toNamedContexts(t.Contexts), } if t.DockerfileInline != nil { bi.DockerfileInline = *t.DockerfileInline } updateContext(&bi, inp) - if !build.IsRemoteURL(bi.ContextPath) && bi.ContextState == nil && !path.IsAbs(bi.DockerfilePath) { - bi.DockerfilePath = path.Join(bi.ContextPath, bi.DockerfilePath) + if !build.IsRemoteURL(bi.ContextPath) && bi.ContextDefinition == nil && !path.IsAbs(bi.DockerfileName) { + bi.DockerfileName = path.Join(bi.ContextPath, bi.DockerfileName) } if strings.HasPrefix(bi.ContextPath, "cwd://") { bi.ContextPath = path.Clean(strings.TrimPrefix(bi.ContextPath, "cwd://")) } for k, v := range bi.NamedContexts { if strings.HasPrefix(v.Path, "cwd://") { - bi.NamedContexts[k] = build.NamedContext{Path: path.Clean(strings.TrimPrefix(v.Path, "cwd://"))} + bi.NamedContexts[k] = &controllerapi.NamedContext{Path: path.Clean(strings.TrimPrefix(v.Path, "cwd://"))} } } @@ -1095,87 +1107,61 @@ func toBuildOpt(t *Target, inp *Input) (*build.Options, error) { networkMode = *t.NetworkMode } - bo := &build.Options{ - Inputs: bi, + opts := &controllerapi.BuildOptions{ + Inputs: &bi, + Opts: &controllerapi.CommonOptions{ + NoCache: noCache, + Pull: pull, + Linked: t.linked, + }, Tags: t.Tags, BuildArgs: args, Labels: labels, - NoCache: noCache, NoCacheFilter: t.NoCacheFilter, - Pull: pull, NetworkMode: networkMode, - Linked: t.linked, + Platforms: t.Platforms, } - platforms, err := platformutil.Parse(t.Platforms) - if err != nil { - return nil, err + if t.Target != nil { + opts.Target = *t.Target } - bo.Platforms = platforms - dockerConfig := config.LoadDefaultConfigFile(os.Stderr) - bo.Session = append(bo.Session, authprovider.NewDockerAuthProvider(dockerConfig)) - - secrets, err := buildflags.ParseSecretSpecs(t.Secrets) - if err != nil { - return nil, err - } - secretAttachment, err := controllerapi.CreateSecrets(secrets) + opts.Secrets, err = buildflags.ParseSecretSpecs(t.Secrets) if err != nil { return nil, err } - bo.Session = append(bo.Session, secretAttachment) - sshSpecs, err := buildflags.ParseSSHSpecs(t.SSH) - if err != nil { - return nil, err - } - if len(sshSpecs) == 0 && (buildflags.IsGitSSH(bi.ContextPath) || (inp != nil && buildflags.IsGitSSH(inp.URL))) { - sshSpecs = append(sshSpecs, &controllerapi.SSH{ID: "default"}) - } - sshAttachment, err := controllerapi.CreateSSH(sshSpecs) + opts.SSH, err = buildflags.ParseSSHSpecs(t.SSH) if err != nil { return nil, err } - bo.Session = append(bo.Session, sshAttachment) - - if t.Target != nil { - bo.Target = *t.Target - } - cacheImports, err := buildflags.ParseCacheEntry(t.CacheFrom) + opts.CacheFrom, err = buildflags.ParseCacheEntry(t.CacheFrom) if err != nil { return nil, err } - bo.CacheFrom = controllerapi.CreateCaches(cacheImports) - cacheExports, err := buildflags.ParseCacheEntry(t.CacheTo) + opts.CacheTo, err = buildflags.ParseCacheEntry(t.CacheTo) if err != nil { return nil, err } - bo.CacheTo = controllerapi.CreateCaches(cacheExports) - outputs, err := buildflags.ParseExports(t.Outputs) - if err != nil { - return nil, err - } - bo.Exports, err = controllerapi.CreateExports(outputs) + opts.Exports, err = buildflags.ParseExports(t.Outputs) if err != nil { return nil, err } - attests, err := buildflags.ParseAttests(t.Attest) + opts.Attests, err = buildflags.ParseAttests(t.Attest) if err != nil { return nil, err } - bo.Attests = controllerapi.CreateAttestations(attests) - bo.SourcePolicy, err = build.ReadSourcePolicy() + opts.SourcePolicy, err = build.ReadSourcePolicy() if err != nil { return nil, err } - return bo, nil + return opts, nil } func defaultTarget() *Target { @@ -1264,10 +1250,10 @@ func sliceEqual(s1, s2 []string) bool { return true } -func toNamedContexts(m map[string]string) map[string]build.NamedContext { - m2 := make(map[string]build.NamedContext, len(m)) +func toNamedContexts(m map[string]string) map[string]*controllerapi.NamedContext { + m2 := make(map[string]*controllerapi.NamedContext, len(m)) for k, v := range m { - m2[k] = build.NamedContext{Path: v} + m2[k] = &controllerapi.NamedContext{Path: v} } return m2 }