diff --git a/frontend/dockerui/config.go b/frontend/dockerui/config.go index e95041ff0f01..99ddb80b1475 100644 --- a/frontend/dockerui/config.go +++ b/frontend/dockerui/config.go @@ -19,6 +19,7 @@ import ( "github.com/moby/buildkit/frontend/gateway/client" "github.com/moby/buildkit/solver/pb" "github.com/moby/buildkit/util/flightcontrol" + digest "github.com/opencontainers/go-digest" ocispecs "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" ) @@ -96,6 +97,7 @@ type ContextOpt struct { LocalOpts []llb.LocalOption Platform *ocispecs.Platform ResolveMode string + CaptureDigest *digest.Digest } func validateMinCaps(c client.Client) error { diff --git a/frontend/dockerui/namedcontext.go b/frontend/dockerui/namedcontext.go index 28db8488b8a6..64fc6cacccc2 100644 --- a/frontend/dockerui/namedcontext.go +++ b/frontend/dockerui/namedcontext.go @@ -58,7 +58,7 @@ func (bc *Client) namedContext(ctx context.Context, name string, nameWithPlatfor named = reference.TagNameOnly(named) - _, data, err := bc.client.ResolveImageConfig(ctx, named.String(), llb.ResolveImageConfigOpt{ + dgst, data, err := bc.client.ResolveImageConfig(ctx, named.String(), llb.ResolveImageConfigOpt{ Platform: opt.Platform, ResolveMode: opt.ResolveMode, LogName: fmt.Sprintf("[context %s] load metadata for %s", nameWithPlatform, ref), @@ -67,7 +67,6 @@ func (bc *Client) namedContext(ctx context.Context, name string, nameWithPlatfor if err != nil { return nil, nil, err } - var img image.Image if err := json.Unmarshal(data, &img); err != nil { return nil, nil, err @@ -79,6 +78,9 @@ func (bc *Client) namedContext(ctx context.Context, name string, nameWithPlatfor if err != nil { return nil, nil, err } + if opt.CaptureDigest != nil { + *opt.CaptureDigest = dgst + } return &st, &img, nil case "git": st, ok := DetectGitContext(v, true) @@ -119,7 +121,7 @@ func (bc *Client) namedContext(ctx context.Context, name string, nameWithPlatfor return nil, nil, errors.Wrapf(err, "could not wrap %q with digest", name) } - _, data, err := bc.client.ResolveImageConfig(ctx, dummyRef.String(), llb.ResolveImageConfigOpt{ + dgst, data, err := bc.client.ResolveImageConfig(ctx, dummyRef.String(), llb.ResolveImageConfigOpt{ Platform: opt.Platform, ResolveMode: opt.ResolveMode, LogName: fmt.Sprintf("[context %s] load metadata for %s", nameWithPlatform, dummyRef.String()), @@ -153,6 +155,9 @@ func (bc *Client) namedContext(ctx context.Context, name string, nameWithPlatfor if err != nil { return nil, nil, err } + if opt.CaptureDigest != nil { + *opt.CaptureDigest = dgst + } return &st, &img, nil case "local": st := llb.Local(vv[1], diff --git a/frontend/gateway/container.go b/frontend/gateway/container/container.go similarity index 98% rename from frontend/gateway/container.go rename to frontend/gateway/container/container.go index 058aa2bb3e5b..6555fd6de613 100644 --- a/frontend/gateway/container.go +++ b/frontend/gateway/container/container.go @@ -1,4 +1,4 @@ -package gateway +package container import ( "context" @@ -261,9 +261,9 @@ func PrepareMounts(ctx context.Context, mm *mounts.MountManager, cm cache.Manage }) root = active } - p.Root = mountWithSession(root, g) + p.Root = MountWithSession(root, g) } else { - mws := mountWithSession(mountable, g) + mws := MountWithSession(mountable, g) dest := m.Dest if !filepath.IsAbs(filepath.Clean(dest)) { dest = filepath.Join("/", cwd, dest) @@ -501,7 +501,7 @@ func addDefaultEnvvar(env []string, k, v string) []string { return append(env, k+"="+v) } -func mountWithSession(m cache.Mountable, g session.Group) executor.Mount { +func MountWithSession(m cache.Mountable, g session.Group) executor.Mount { _, readonly := m.(cache.ImmutableRef) return executor.Mount{ Src: &mountable{m: m, g: g}, diff --git a/frontend/gateway/util.go b/frontend/gateway/container/util.go similarity index 96% rename from frontend/gateway/util.go rename to frontend/gateway/container/util.go index 0de8353402fa..1a1fb25138e3 100644 --- a/frontend/gateway/util.go +++ b/frontend/gateway/container/util.go @@ -1,4 +1,4 @@ -package gateway +package container import ( "net" diff --git a/frontend/gateway/forwarder/forward.go b/frontend/gateway/forwarder/forward.go index 491f14be09c8..0f4da47cbde6 100644 --- a/frontend/gateway/forwarder/forward.go +++ b/frontend/gateway/forwarder/forward.go @@ -7,8 +7,8 @@ import ( cacheutil "github.com/moby/buildkit/cache/util" "github.com/moby/buildkit/client/llb" "github.com/moby/buildkit/frontend" - "github.com/moby/buildkit/frontend/gateway" "github.com/moby/buildkit/frontend/gateway/client" + "github.com/moby/buildkit/frontend/gateway/container" gwpb "github.com/moby/buildkit/frontend/gateway/pb" "github.com/moby/buildkit/identity" "github.com/moby/buildkit/session" @@ -26,8 +26,8 @@ import ( "golang.org/x/sync/errgroup" ) -func llbBridgeToGatewayClient(ctx context.Context, llbBridge frontend.FrontendLLBBridge, opts map[string]string, inputs map[string]*opspb.Definition, w worker.Infos, sid string, sm *session.Manager) (*bridgeClient, error) { - bc := &bridgeClient{ +func LLBBridgeToGatewayClient(ctx context.Context, llbBridge frontend.FrontendLLBBridge, opts map[string]string, inputs map[string]*opspb.Definition, w worker.Infos, sid string, sm *session.Manager) (*BridgeClient, error) { + bc := &BridgeClient{ opts: opts, inputs: inputs, FrontendLLBBridge: llbBridge, @@ -40,7 +40,7 @@ func llbBridgeToGatewayClient(ctx context.Context, llbBridge frontend.FrontendLL return bc, nil } -type bridgeClient struct { +type BridgeClient struct { frontend.FrontendLLBBridge mu sync.Mutex opts map[string]string @@ -54,7 +54,7 @@ type bridgeClient struct { ctrs []client.Container } -func (c *bridgeClient) Solve(ctx context.Context, req client.SolveRequest) (*client.Result, error) { +func (c *BridgeClient) Solve(ctx context.Context, req client.SolveRequest) (*client.Result, error) { res, err := c.FrontendLLBBridge.Solve(ctx, frontend.SolveRequest{ Evaluate: req.Evaluate, Definition: req.Definition, @@ -91,7 +91,7 @@ func (c *bridgeClient) Solve(ctx context.Context, req client.SolveRequest) (*cli return cRes, nil } -func (c *bridgeClient) loadBuildOpts() client.BuildOpts { +func (c *BridgeClient) loadBuildOpts() client.BuildOpts { wis := c.workers.WorkerInfos() workers := make([]client.WorkerInfo, len(wis)) for i, w := range wis { @@ -112,11 +112,11 @@ func (c *bridgeClient) loadBuildOpts() client.BuildOpts { } } -func (c *bridgeClient) BuildOpts() client.BuildOpts { +func (c *BridgeClient) BuildOpts() client.BuildOpts { return c.buildOpts } -func (c *bridgeClient) Inputs(ctx context.Context) (map[string]llb.State, error) { +func (c *BridgeClient) Inputs(ctx context.Context) (map[string]llb.State, error) { inputs := make(map[string]llb.State) for key, def := range c.inputs { defop, err := llb.NewDefinitionOp(def) @@ -128,7 +128,7 @@ func (c *bridgeClient) Inputs(ctx context.Context) (map[string]llb.State, error) return inputs, nil } -func (c *bridgeClient) wrapSolveError(solveErr error) error { +func (c *BridgeClient) wrapSolveError(solveErr error) error { var ( ee *llberrdefs.ExecError fae *llberrdefs.FileActionError @@ -162,7 +162,7 @@ func (c *bridgeClient) wrapSolveError(solveErr error) error { return errdefs.WithSolveError(solveErr, subject, inputIDs, mountIDs) } -func (c *bridgeClient) registerResultIDs(results ...solver.Result) (ids []string, err error) { +func (c *BridgeClient) registerResultIDs(results ...solver.Result) (ids []string, err error) { c.mu.Lock() defer c.mu.Unlock() @@ -181,7 +181,7 @@ func (c *bridgeClient) registerResultIDs(results ...solver.Result) (ids []string return ids, nil } -func (c *bridgeClient) toFrontendResult(r *client.Result) (*frontend.Result, error) { +func (c *BridgeClient) toFrontendResult(r *client.Result) (*frontend.Result, error) { if r == nil { return nil, nil } @@ -206,7 +206,7 @@ func (c *bridgeClient) toFrontendResult(r *client.Result) (*frontend.Result, err return res, nil } -func (c *bridgeClient) discard(err error) { +func (c *BridgeClient) discard(err error) { for _, ctr := range c.ctrs { ctr.Release(context.TODO()) } @@ -227,16 +227,16 @@ func (c *bridgeClient) discard(err error) { } } -func (c *bridgeClient) Warn(ctx context.Context, dgst digest.Digest, msg string, opts client.WarnOpts) error { +func (c *BridgeClient) Warn(ctx context.Context, dgst digest.Digest, msg string, opts client.WarnOpts) error { return c.FrontendLLBBridge.Warn(ctx, dgst, msg, opts) } -func (c *bridgeClient) NewContainer(ctx context.Context, req client.NewContainerRequest) (client.Container, error) { - ctrReq := gateway.NewContainerRequest{ +func (c *BridgeClient) NewContainer(ctx context.Context, req client.NewContainerRequest) (client.Container, error) { + ctrReq := container.NewContainerRequest{ ContainerID: identity.NewID(), NetMode: req.NetMode, Hostname: req.Hostname, - Mounts: make([]gateway.Mount, len(req.Mounts)), + Mounts: make([]container.Mount, len(req.Mounts)), } eg, ctx := errgroup.WithContext(ctx) @@ -267,7 +267,7 @@ func (c *bridgeClient) NewContainer(ctx context.Context, req client.NewContainer return errors.Errorf("failed to find ref %s for %q mount", m.ResultID, m.Dest) } } - ctrReq.Mounts[i] = gateway.Mount{ + ctrReq.Mounts[i] = container.Mount{ WorkerRef: workerRef, Mount: &opspb.Mount{ Dest: m.Dest, @@ -288,7 +288,7 @@ func (c *bridgeClient) NewContainer(ctx context.Context, req client.NewContainer return nil, err } - ctrReq.ExtraHosts, err = gateway.ParseExtraHosts(req.ExtraHosts) + ctrReq.ExtraHosts, err = container.ParseExtraHosts(req.ExtraHosts) if err != nil { return nil, err } @@ -299,7 +299,7 @@ func (c *bridgeClient) NewContainer(ctx context.Context, req client.NewContainer } group := session.NewGroup(c.sid) - ctr, err := gateway.NewContainer(ctx, w, c.sm, group, ctrReq) + ctr, err := container.NewContainer(ctx, w, c.sm, group, ctrReq) if err != nil { return nil, err } @@ -307,7 +307,7 @@ func (c *bridgeClient) NewContainer(ctx context.Context, req client.NewContainer return ctr, nil } -func (c *bridgeClient) newRef(r solver.ResultProxy, s session.Group) (*ref, error) { +func (c *BridgeClient) newRef(r solver.ResultProxy, s session.Group) (*ref, error) { return &ref{resultProxy: r, session: s, c: c}, nil } @@ -316,7 +316,7 @@ type ref struct { resultProxyClones []solver.ResultProxy session session.Group - c *bridgeClient + c *BridgeClient } func (r *ref) acquireResultProxy() solver.ResultProxy { diff --git a/frontend/gateway/forwarder/frontend.go b/frontend/gateway/forwarder/frontend.go index 7cd25a0e8ea0..ae144162c939 100644 --- a/frontend/gateway/forwarder/frontend.go +++ b/frontend/gateway/forwarder/frontend.go @@ -23,7 +23,7 @@ type GatewayForwarder struct { } func (gf *GatewayForwarder) Solve(ctx context.Context, llbBridge frontend.FrontendLLBBridge, opts map[string]string, inputs map[string]*pb.Definition, sid string, sm *session.Manager) (retRes *frontend.Result, retErr error) { - c, err := llbBridgeToGatewayClient(ctx, llbBridge, opts, inputs, gf.workers, sid, sm) + c, err := LLBBridgeToGatewayClient(ctx, llbBridge, opts, inputs, gf.workers, sid, sm) if err != nil { return nil, err } diff --git a/frontend/gateway/gateway.go b/frontend/gateway/gateway.go index 09f789baca16..db4b1db82fb5 100644 --- a/frontend/gateway/gateway.go +++ b/frontend/gateway/gateway.go @@ -27,8 +27,12 @@ import ( "github.com/moby/buildkit/client/llb" "github.com/moby/buildkit/executor" "github.com/moby/buildkit/exporter/containerimage/exptypes" + "github.com/moby/buildkit/exporter/containerimage/image" "github.com/moby/buildkit/frontend" + "github.com/moby/buildkit/frontend/dockerui" gwclient "github.com/moby/buildkit/frontend/gateway/client" + "github.com/moby/buildkit/frontend/gateway/container" + "github.com/moby/buildkit/frontend/gateway/forwarder" pb "github.com/moby/buildkit/frontend/gateway/pb" "github.com/moby/buildkit/identity" "github.com/moby/buildkit/session" @@ -89,7 +93,7 @@ func (gf *gatewayFrontend) Solve(ctx context.Context, llbBridge frontend.Fronten } _, isDevel := opts[keyDevel] - var img ocispecs.Image + var img image.Image var mfstDigest digest.Digest var rootFS cache.MutableRef var readonly bool // TODO: try to switch to read-only by default. @@ -137,31 +141,51 @@ func (gf *gatewayFrontend) Solve(ctx context.Context, llbBridge frontend.Fronten } } } else { - sourceRef, err := reference.ParseNormalizedNamed(source) + c, err := forwarder.LLBBridgeToGatewayClient(ctx, llbBridge, opts, inputs, gf.workers, sid, sm) if err != nil { return nil, err } - - dgst, config, err := llbBridge.ResolveImageConfig(ctx, reference.TagNameOnly(sourceRef).String(), llb.ResolveImageConfigOpt{}) + dc, err := dockerui.NewClient(c) if err != nil { return nil, err } - mfstDigest = dgst - - if err := json.Unmarshal(config, &img); err != nil { + st, dockerImage, err := dc.NamedContext(ctx, source, dockerui.ContextOpt{ + CaptureDigest: &mfstDigest, + }) + if err != nil { return nil, err } + if dockerImage != nil { + img = *dockerImage + } + if st == nil { + sourceRef, err := reference.ParseNormalizedNamed(source) + if err != nil { + return nil, err + } - if dgst != "" { - sourceRef, err = reference.WithDigest(sourceRef, dgst) + dgst, config, err := llbBridge.ResolveImageConfig(ctx, reference.TagNameOnly(sourceRef).String(), llb.ResolveImageConfigOpt{}) if err != nil { return nil, err } - } + mfstDigest = dgst + + if err := json.Unmarshal(config, &img); err != nil { + return nil, err + } + + if dgst != "" { + sourceRef, err = reference.WithDigest(sourceRef, dgst) + if err != nil { + return nil, err + } + } - src := llb.Image(sourceRef.String(), &markTypeFrontend{}) + src := llb.Image(sourceRef.String(), &markTypeFrontend{}) + st = &src + } - def, err := src.Marshal(ctx) + def, err := st.Marshal(ctx) if err != nil { return nil, err } @@ -275,8 +299,7 @@ func (gf *gatewayFrontend) Solve(ctx context.Context, llbBridge frontend.Fronten mnts = append(mnts, *mdmnt) } - _, err = w.Executor().Run(ctx, "", mountWithSession(rootFS, session.NewGroup(sid)), mnts, executor.ProcessInfo{Meta: meta, Stdin: lbf.Stdin, Stdout: lbf.Stdout, Stderr: os.Stderr}, nil) - + _, err = w.Executor().Run(ctx, "", container.MountWithSession(rootFS, session.NewGroup(sid)), mnts, executor.ProcessInfo{Meta: meta, Stdin: lbf.Stdin, Stdout: lbf.Stdout, Stderr: os.Stderr}, nil) if err != nil { if errdefs.IsCanceled(ctx, err) && lbf.isErrServerClosed { err = errors.Errorf("frontend grpc server closed unexpectedly") @@ -954,7 +977,7 @@ func (lbf *llbBridgeForwarder) Inputs(ctx context.Context, in *pb.InputsRequest) func (lbf *llbBridgeForwarder) NewContainer(ctx context.Context, in *pb.NewContainerRequest) (_ *pb.NewContainerResponse, err error) { bklog.G(ctx).Debugf("|<--- NewContainer %s", in.ContainerID) - ctrReq := NewContainerRequest{ + ctrReq := container.NewContainerRequest{ ContainerID: in.ContainerID, NetMode: in.Network, Hostname: in.Hostname, @@ -984,7 +1007,7 @@ func (lbf *llbBridgeForwarder) NewContainer(ctx context.Context, in *pb.NewConta } } } - ctrReq.Mounts = append(ctrReq.Mounts, Mount{ + ctrReq.Mounts = append(ctrReq.Mounts, container.Mount{ WorkerRef: workerRef, Mount: &opspb.Mount{ Dest: m.Dest, @@ -1007,12 +1030,12 @@ func (lbf *llbBridgeForwarder) NewContainer(ctx context.Context, in *pb.NewConta return nil, stack.Enable(err) } - ctrReq.ExtraHosts, err = ParseExtraHosts(in.ExtraHosts) + ctrReq.ExtraHosts, err = container.ParseExtraHosts(in.ExtraHosts) if err != nil { return nil, stack.Enable(err) } - ctr, err := NewContainer(context.Background(), w, lbf.sm, group, ctrReq) + ctr, err := container.NewContainer(context.Background(), w, lbf.sm, group, ctrReq) if err != nil { return nil, stack.Enable(err) } diff --git a/solver/llbsolver/ops/exec.go b/solver/llbsolver/ops/exec.go index 73481534a7e5..48556b60ab68 100644 --- a/solver/llbsolver/ops/exec.go +++ b/solver/llbsolver/ops/exec.go @@ -14,7 +14,7 @@ import ( "github.com/moby/buildkit/cache" "github.com/moby/buildkit/executor" resourcestypes "github.com/moby/buildkit/executor/resources/types" - "github.com/moby/buildkit/frontend/gateway" + "github.com/moby/buildkit/frontend/gateway/container" "github.com/moby/buildkit/session" "github.com/moby/buildkit/session/secrets" "github.com/moby/buildkit/solver" @@ -260,7 +260,7 @@ func (e *ExecOp) Exec(ctx context.Context, g session.Group, inputs []solver.Resu } } - p, err := gateway.PrepareMounts(ctx, e.mm, e.cm, g, e.op.Meta.Cwd, e.op.Mounts, refs, func(m *pb.Mount, ref cache.ImmutableRef) (cache.MutableRef, error) { + p, err := container.PrepareMounts(ctx, e.mm, e.cm, g, e.op.Meta.Cwd, e.op.Mounts, refs, func(m *pb.Mount, ref cache.ImmutableRef) (cache.MutableRef, error) { desc := fmt.Sprintf("mount %s from exec %s", m.Dest, strings.Join(e.op.Meta.Args, " ")) return e.cm.New(ctx, ref, g, cache.WithDescription(desc)) }) @@ -307,7 +307,7 @@ func (e *ExecOp) Exec(ctx context.Context, g session.Group, inputs []solver.Resu return nil, err } - extraHosts, err := gateway.ParseExtraHosts(e.op.Meta.ExtraHosts) + extraHosts, err := container.ParseExtraHosts(e.op.Meta.ExtraHosts) if err != nil { return nil, err }