Skip to content

Commit fe002ca

Browse files
committed
dap: use container fs requests to get a more accurate state for the file system
The container filesystem request API that has been added to buildkit allows a container created through the `NewContainer` API to also access the filesystems. This is useful when an error occurs because it allows us to grab the mutable state of the mounts used during the actual build rather than the input version copies which don't contain any files that were added as part of the failed command. This gives us a more accurate view of the filesystem that was previously only accessible through using `exec` and `ls`/`cat` commands that may not always exist. Signed-off-by: Jonathan A. Sternberg <jonathan.sternberg@docker.com>
1 parent 44945d7 commit fe002ca

File tree

1 file changed

+84
-7
lines changed

1 file changed

+84
-7
lines changed

dap/thread.go

Lines changed: 84 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,12 @@ import (
1212
"github.com/google/go-dap"
1313
"github.com/moby/buildkit/client/llb"
1414
gateway "github.com/moby/buildkit/frontend/gateway/client"
15+
gwpb "github.com/moby/buildkit/frontend/gateway/pb"
1516
"github.com/moby/buildkit/solver/errdefs"
1617
"github.com/moby/buildkit/solver/pb"
1718
"github.com/opencontainers/go-digest"
1819
"github.com/pkg/errors"
20+
"github.com/tonistiigi/fsutil/types"
1921
"golang.org/x/sync/errgroup"
2022
)
2123

@@ -323,16 +325,12 @@ func (t *thread) pause(c Context, k string, refs map[string]gateway.Reference, e
323325
}
324326
t.paused = make(chan stepType, 1)
325327

328+
t.prepareResultHandle(c, k, refs, err)
329+
326330
ctx, cancel := context.WithCancelCause(c)
327331
t.collectStackTrace(ctx, pos, refs)
328332
t.cancel = cancel
329333

330-
// Used for exec. Only works if there was an error or if the step returns
331-
// a root mount.
332-
if ref, ok := refs[k]; ok || err != nil {
333-
t.prepareResultHandle(c, ref, err)
334-
}
335-
336334
event.ThreadId = t.id
337335
c.C() <- &dap.StoppedEvent{
338336
Event: dap.Event{Event: "stopped"},
@@ -341,7 +339,15 @@ func (t *thread) pause(c Context, k string, refs map[string]gateway.Reference, e
341339
return t.paused
342340
}
343341

344-
func (t *thread) prepareResultHandle(c Context, ref gateway.Reference, err error) {
342+
func (t *thread) prepareResultHandle(c Context, k string, refs map[string]gateway.Reference, err error) {
343+
var ref gateway.Reference
344+
if err == nil {
345+
var ok bool
346+
if ref, ok = refs[k]; !ok {
347+
return
348+
}
349+
}
350+
345351
// Create a context for cancellations and make the cancel function
346352
// block on the wait group.
347353
var wg sync.WaitGroup
@@ -353,6 +359,28 @@ func (t *thread) prepareResultHandle(c Context, ref gateway.Reference, err error
353359

354360
t.rCtx = build.NewResultHandle(ctx, t.c, ref, t.meta, err)
355361

362+
if err != nil {
363+
gwcaps := t.c.BuildOpts().Caps
364+
365+
var solveErr *errdefs.SolveError
366+
if gwcaps.Supports(gwpb.CapGatewayExecFilesystem) != nil && errors.As(err, &solveErr) {
367+
if exec, ok := solveErr.Op.Op.(*pb.Op_Exec); ok {
368+
rCtx := t.rCtx
369+
370+
getContainer := sync.OnceValues(func() (gateway.Container, error) {
371+
return rCtx.NewContainer(ctx, &build.InvokeConfig{})
372+
})
373+
374+
for i, m := range exec.Exec.Mounts {
375+
refs[m.Dest] = &mountReference{
376+
getContainer: getContainer,
377+
index: i,
378+
}
379+
}
380+
}
381+
}
382+
}
383+
356384
// Start the attach. Use the context we created and perform it in
357385
// a goroutine. We aren't necessarily assuming this will actually work.
358386
wg.Go(func() {
@@ -678,3 +706,52 @@ func (t *thread) rewind(ctx Context, inErr error) (k string, result *step, mount
678706
}
679707
return k, result, mounts, inErr
680708
}
709+
710+
type mountReference struct {
711+
getContainer func() (gateway.Container, error)
712+
index int
713+
}
714+
715+
func (r *mountReference) ToState() (llb.State, error) {
716+
return llb.State{}, errors.New("unimplemented")
717+
}
718+
719+
func (r *mountReference) Evaluate(ctx context.Context) error {
720+
return nil
721+
}
722+
723+
func (r *mountReference) ReadFile(ctx context.Context, req gateway.ReadRequest) ([]byte, error) {
724+
ctr, err := r.getContainer()
725+
if err != nil {
726+
return nil, err
727+
}
728+
729+
return ctr.ReadFile(ctx, gateway.ReadContainerRequest{
730+
ReadRequest: req,
731+
Index: r.index,
732+
})
733+
}
734+
735+
func (r *mountReference) StatFile(ctx context.Context, req gateway.StatRequest) (*types.Stat, error) {
736+
ctr, err := r.getContainer()
737+
if err != nil {
738+
return nil, err
739+
}
740+
741+
return ctr.StatFile(ctx, gateway.StatContainerRequest{
742+
StatRequest: req,
743+
Index: r.index,
744+
})
745+
}
746+
747+
func (r *mountReference) ReadDir(ctx context.Context, req gateway.ReadDirRequest) ([]*types.Stat, error) {
748+
ctr, err := r.getContainer()
749+
if err != nil {
750+
return nil, err
751+
}
752+
753+
return ctr.ReadDir(ctx, gateway.ReadDirContainerRequest{
754+
ReadDirRequest: req,
755+
Index: r.index,
756+
})
757+
}

0 commit comments

Comments
 (0)