@@ -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,31 @@ 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 we had a solve error and the exec filesystem capability, we can
367+ // get the filesystem mounts used in the actual build rather than only the input
368+ // mounts.
369+ if gwcaps .Supports (gwpb .CapGatewayExecFilesystem ) == nil && errors .As (err , & solveErr ) {
370+ if exec , ok := solveErr .Op .Op .(* pb.Op_Exec ); ok {
371+ rCtx := t .rCtx
372+
373+ getContainer := sync .OnceValues (func () (* build.Container , error ) {
374+ return build .NewContainer (c , rCtx , & build.InvokeConfig {})
375+ })
376+
377+ for i , m := range exec .Exec .Mounts {
378+ refs [m .Dest ] = & mountReference {
379+ getContainer : getContainer ,
380+ index : i ,
381+ }
382+ }
383+ }
384+ }
385+ }
386+
356387 // Start the attach. Use the context we created and perform it in
357388 // a goroutine. We aren't necessarily assuming this will actually work.
358389 wg .Go (func () {
@@ -678,3 +709,52 @@ func (t *thread) rewind(ctx Context, inErr error) (k string, result *step, mount
678709 }
679710 return k , result , mounts , inErr
680711}
712+
713+ type mountReference struct {
714+ getContainer func () (* build.Container , error )
715+ index int
716+ }
717+
718+ func (r * mountReference ) ToState () (llb.State , error ) {
719+ return llb.State {}, errors .New ("unimplemented, cannot use ToState with mount reference" )
720+ }
721+
722+ func (r * mountReference ) Evaluate (ctx context.Context ) error {
723+ return nil
724+ }
725+
726+ func (r * mountReference ) ReadFile (ctx context.Context , req gateway.ReadRequest ) ([]byte , error ) {
727+ ctr , err := r .getContainer ()
728+ if err != nil {
729+ return nil , err
730+ }
731+
732+ return ctr .ReadFile (ctx , gateway.ReadContainerRequest {
733+ ReadRequest : req ,
734+ MountIndex : r .index ,
735+ })
736+ }
737+
738+ func (r * mountReference ) StatFile (ctx context.Context , req gateway.StatRequest ) (* types.Stat , error ) {
739+ ctr , err := r .getContainer ()
740+ if err != nil {
741+ return nil , err
742+ }
743+
744+ return ctr .StatFile (ctx , gateway.StatContainerRequest {
745+ StatRequest : req ,
746+ MountIndex : r .index ,
747+ })
748+ }
749+
750+ func (r * mountReference ) ReadDir (ctx context.Context , req gateway.ReadDirRequest ) ([]* types.Stat , error ) {
751+ ctr , err := r .getContainer ()
752+ if err != nil {
753+ return nil , err
754+ }
755+
756+ return ctr .ReadDir (ctx , gateway.ReadDirContainerRequest {
757+ ReadDirRequest : req ,
758+ MountIndex : r .index ,
759+ })
760+ }
0 commit comments