Merge pull request #3450 from jsternberg/dap-container-fs-requests

dap: utilize the container filesystem for the file explorer
This commit is contained in:
Tõnis Tiigi
2026-02-22 21:50:28 -08:00
committed by GitHub
2 changed files with 100 additions and 7 deletions
+13
View File
@@ -11,6 +11,7 @@ import (
gateway "github.com/moby/buildkit/frontend/gateway/client" gateway "github.com/moby/buildkit/frontend/gateway/client"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/tonistiigi/fsutil/types"
) )
type InvokeConfig struct { type InvokeConfig struct {
@@ -145,6 +146,18 @@ func (c *Container) Exec(ctx context.Context, cfg *InvokeConfig, stdin io.ReadCl
return err return err
} }
func (c *Container) ReadFile(ctx context.Context, req gateway.ReadContainerRequest) ([]byte, error) {
return c.container.ReadFile(ctx, req)
}
func (c *Container) ReadDir(ctx context.Context, req gateway.ReadDirContainerRequest) ([]*types.Stat, error) {
return c.container.ReadDir(ctx, req)
}
func (c *Container) StatFile(ctx context.Context, req gateway.StatContainerRequest) (*types.Stat, error) {
return c.container.StatFile(ctx, req)
}
func exec(ctx context.Context, resultCtx *ResultHandle, cfg *InvokeConfig, ctr gateway.Container, stdin io.ReadCloser, stdout io.WriteCloser, stderr io.WriteCloser) error { func exec(ctx context.Context, resultCtx *ResultHandle, cfg *InvokeConfig, ctr gateway.Container, stdin io.ReadCloser, stdout io.WriteCloser, stderr io.WriteCloser) error {
processCfg, err := resultCtx.getProcessConfig(cfg, stdin, stdout, stderr) processCfg, err := resultCtx.getProcessConfig(cfg, stdin, stdout, stderr)
if err != nil { if err != nil {
+87 -7
View File
@@ -12,10 +12,12 @@ import (
"github.com/google/go-dap" "github.com/google/go-dap"
"github.com/moby/buildkit/client/llb" "github.com/moby/buildkit/client/llb"
gateway "github.com/moby/buildkit/frontend/gateway/client" gateway "github.com/moby/buildkit/frontend/gateway/client"
gwpb "github.com/moby/buildkit/frontend/gateway/pb"
"github.com/moby/buildkit/solver/errdefs" "github.com/moby/buildkit/solver/errdefs"
"github.com/moby/buildkit/solver/pb" "github.com/moby/buildkit/solver/pb"
"github.com/opencontainers/go-digest" "github.com/opencontainers/go-digest"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/tonistiigi/fsutil/types"
"golang.org/x/sync/errgroup" "golang.org/x/sync/errgroup"
) )
@@ -323,16 +325,12 @@ func (t *thread) pause(c Context, k string, refs map[string]gateway.Reference, e
} }
t.paused = make(chan stepType, 1) t.paused = make(chan stepType, 1)
t.prepareResultHandle(c, k, refs, err)
ctx, cancel := context.WithCancelCause(c) ctx, cancel := context.WithCancelCause(c)
t.collectStackTrace(ctx, pos, refs) t.collectStackTrace(ctx, pos, refs)
t.cancel = cancel t.cancel = cancel
// Used for exec. Only works if there was an error or if the step returns
// a root mount.
if ref, ok := refs[k]; ok || err != nil {
t.prepareResultHandle(c, ref, err)
}
event.ThreadId = t.id event.ThreadId = t.id
c.C() <- &dap.StoppedEvent{ c.C() <- &dap.StoppedEvent{
Event: dap.Event{Event: "stopped"}, Event: dap.Event{Event: "stopped"},
@@ -341,7 +339,15 @@ func (t *thread) pause(c Context, k string, refs map[string]gateway.Reference, e
return t.paused return t.paused
} }
func (t *thread) prepareResultHandle(c Context, ref gateway.Reference, err error) { func (t *thread) prepareResultHandle(c Context, k string, refs map[string]gateway.Reference, err error) {
var ref gateway.Reference
if err == nil {
var ok bool
if ref, ok = refs[k]; !ok {
return
}
}
// Create a context for cancellations and make the cancel function // Create a context for cancellations and make the cancel function
// block on the wait group. // block on the wait group.
var wg sync.WaitGroup var wg sync.WaitGroup
@@ -353,6 +359,31 @@ func (t *thread) prepareResultHandle(c Context, ref gateway.Reference, err error
t.rCtx = build.NewResultHandle(ctx, t.c, ref, t.meta, err) t.rCtx = build.NewResultHandle(ctx, t.c, ref, t.meta, err)
if err != nil {
gwcaps := t.c.BuildOpts().Caps
var solveErr *errdefs.SolveError
// If we had a solve error and the exec filesystem capability, we can
// get the filesystem mounts used in the actual build rather than only the input
// mounts.
if gwcaps.Supports(gwpb.CapGatewayExecFilesystem) == nil && errors.As(err, &solveErr) {
if exec, ok := solveErr.Op.Op.(*pb.Op_Exec); ok {
rCtx := t.rCtx
getContainer := sync.OnceValues(func() (*build.Container, error) {
return build.NewContainer(c, rCtx, &build.InvokeConfig{})
})
for i, m := range exec.Exec.Mounts {
refs[m.Dest] = &mountReference{
getContainer: getContainer,
index: i,
}
}
}
}
}
// Start the attach. Use the context we created and perform it in // Start the attach. Use the context we created and perform it in
// a goroutine. We aren't necessarily assuming this will actually work. // a goroutine. We aren't necessarily assuming this will actually work.
wg.Go(func() { wg.Go(func() {
@@ -689,3 +720,52 @@ func (t *thread) rewind(ctx Context, inErr error) (k string, result *step, mount
} }
return k, result, mounts, inErr return k, result, mounts, inErr
} }
type mountReference struct {
getContainer func() (*build.Container, error)
index int
}
func (r *mountReference) ToState() (llb.State, error) {
return llb.State{}, errors.New("unimplemented, cannot use ToState with mount reference")
}
func (r *mountReference) Evaluate(ctx context.Context) error {
return nil
}
func (r *mountReference) ReadFile(ctx context.Context, req gateway.ReadRequest) ([]byte, error) {
ctr, err := r.getContainer()
if err != nil {
return nil, err
}
return ctr.ReadFile(ctx, gateway.ReadContainerRequest{
ReadRequest: req,
MountIndex: r.index,
})
}
func (r *mountReference) StatFile(ctx context.Context, req gateway.StatRequest) (*types.Stat, error) {
ctr, err := r.getContainer()
if err != nil {
return nil, err
}
return ctr.StatFile(ctx, gateway.StatContainerRequest{
StatRequest: req,
MountIndex: r.index,
})
}
func (r *mountReference) ReadDir(ctx context.Context, req gateway.ReadDirRequest) ([]*types.Stat, error) {
ctr, err := r.getContainer()
if err != nil {
return nil, err
}
return ctr.ReadDir(ctx, gateway.ReadDirContainerRequest{
ReadDirRequest: req,
MountIndex: r.index,
})
}