Skip to content

Commit 38cf843

Browse files
committed
build: change build handler to evaluate instead of onresult
This changes the build handler to customize the behavior of evaluate rather than onresult and also simplifies the `ResultHandle`. The `ResultHandle` is now only valid within the gateway callback and can be used to start containers from the handler. `Evaluate` now executes inside of the gateway callback rather than having a separate implementation that executes or re-invokes the build. This keeps the gateway callback session open until the debugger has returned. The `ErrReload` for monitor has now been moved into the `build` package and been renamed to `ErrRestart`. This is because it restarts the build so the name makes a bit more sense. The actual use of this functionality is still tied to the monitor reload. Signed-off-by: Jonathan A. Sternberg <[email protected]>
1 parent 34e59ca commit 38cf843

File tree

5 files changed

+94
-333
lines changed

5 files changed

+94
-333
lines changed

build/build.go

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ const (
5959
printLintFallbackImage = "docker/dockerfile:1.8.1@sha256:e87caa74dcb7d46cd820352bfea12591f3dba3ddc4285e19c7dcd13359f7cefd"
6060
)
6161

62+
var ErrRestart = errors.New("build: restart")
63+
6264
type Options struct {
6365
Inputs Inputs
6466

@@ -312,7 +314,7 @@ func toRepoOnly(in string) (string, error) {
312314
}
313315

314316
type Handler struct {
315-
OnResult func(driverIdx int, rCtx *ResultHandle)
317+
Evaluate func(ctx context.Context, c gateway.Client, res *gateway.Result) error
316318
}
317319

318320
func Build(ctx context.Context, nodes []builder.Node, opts map[string]Options, docker *dockerutil.Client, cfg *confutil.Config, w progress.Writer) (resp map[string]*client.SolveResponse, err error) {
@@ -479,9 +481,14 @@ func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opts map[
479481
ch, done := progress.NewChannel(pw)
480482
defer func() { <-done }()
481483

482-
cc := c
483-
var callRes map[string][]byte
484-
buildFunc := func(ctx context.Context, c gateway.Client) (*gateway.Result, error) {
484+
var (
485+
callRes map[string][]byte
486+
frontendErr error
487+
)
488+
buildFunc := func(ctx context.Context, c gateway.Client) (_ *gateway.Result, retErr error) {
489+
// Capture the error from this build function.
490+
defer catchFrontendError(&retErr, &frontendErr)
491+
485492
if opt.CallFunc != nil {
486493
if _, ok := req.FrontendOpt["frontend.caps"]; !ok {
487494
req.FrontendOpt["frontend.caps"] = "moby.buildkit.frontend.subrequests+forward"
@@ -504,24 +511,25 @@ func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opts map[
504511
results.Set(rKey, res)
505512

506513
if children := childTargets[rKey]; len(children) > 0 {
507-
if err := waitForChildren(ctx, res, results, children); err != nil {
514+
if err := waitForChildren(ctx, bh, c, res, results, children); err != nil {
515+
return nil, err
516+
}
517+
} else if bh != nil && bh.Evaluate != nil {
518+
if err := bh.Evaluate(ctx, c, res); err != nil {
508519
return nil, err
509520
}
510521
}
511522
return res, nil
512523
}
524+
513525
buildRef := fmt.Sprintf("%s/%s/%s", node.Builder, node.Name, so.Ref)
514526

515-
var rr *client.SolveResponse
516-
if bh != nil && bh.OnResult != nil {
517-
var resultHandle *ResultHandle
518-
resultHandle, rr, err = NewResultHandle(ctx, cc, *so, "buildx", buildFunc, ch)
519-
bh.OnResult(dp.driverIndex, resultHandle)
520-
} else {
521-
span, ctx := tracing.StartSpan(ctx, "build")
522-
rr, err = c.Build(ctx, *so, "buildx", buildFunc, ch)
523-
tracing.FinishWithError(span, err)
527+
span, ctx := tracing.StartSpan(ctx, "build")
528+
rr, err := c.Build(ctx, *so, "buildx", buildFunc, ch)
529+
if errors.Is(frontendErr, ErrRestart) {
530+
err = ErrRestart
524531
}
532+
tracing.FinishWithError(span, err)
525533

526534
if !so.Internal && desktop.BuildBackendEnabled() && node.Driver.HistoryAPISupported(ctx) {
527535
if err != nil {
@@ -1191,7 +1199,7 @@ func solve(ctx context.Context, c gateway.Client, req gateway.SolveRequest) (*ga
11911199
return res, nil
11921200
}
11931201

1194-
func waitForChildren(ctx context.Context, res *gateway.Result, results *waitmap.Map, children []string) error {
1202+
func waitForChildren(ctx context.Context, bh *Handler, c gateway.Client, res *gateway.Result, results *waitmap.Map, children []string) error {
11951203
// wait for the child targets to register their LLB before evaluating
11961204
_, err := results.Get(ctx, children...)
11971205
if err != nil {
@@ -1200,6 +1208,9 @@ func waitForChildren(ctx context.Context, res *gateway.Result, results *waitmap.
12001208
// we need to wait until the child targets have completed before we can release
12011209
eg, ctx := errgroup.WithContext(ctx)
12021210
eg.Go(func() error {
1211+
if bh != nil && bh.Evaluate != nil {
1212+
return bh.Evaluate(ctx, c, res)
1213+
}
12031214
return res.EachRef(func(ref gateway.Reference) error {
12041215
return ref.Evaluate(ctx)
12051216
})
@@ -1210,3 +1221,12 @@ func waitForChildren(ctx context.Context, res *gateway.Result, results *waitmap.
12101221
})
12111222
return eg.Wait()
12121223
}
1224+
1225+
func catchFrontendError(retErr, frontendErr *error) {
1226+
*frontendErr = *retErr
1227+
if errors.Is(*retErr, ErrRestart) {
1228+
// Overwrite the sentinel error with a more user friendly message.
1229+
// This gets stored only in the return error.
1230+
*retErr = errors.New("build restarted by client")
1231+
}
1232+
}

build/invoke.go

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -56,26 +56,18 @@ type Container struct {
5656
func NewContainer(ctx context.Context, resultCtx *ResultHandle, cfg *InvokeConfig) (*Container, error) {
5757
mainCtx := ctx
5858

59-
ctrCh := make(chan *Container)
60-
errCh := make(chan error)
59+
ctrCh := make(chan *Container, 1)
60+
errCh := make(chan error, 1)
6161
go func() {
62-
err := resultCtx.build(func(ctx context.Context, c gateway.Client) (*gateway.Result, error) {
63-
ctx, cancel := context.WithCancelCause(ctx)
64-
go func() {
65-
<-mainCtx.Done()
66-
cancel(errors.WithStack(context.Canceled))
67-
}()
68-
69-
containerCfg, err := resultCtx.getContainerConfig(cfg)
70-
if err != nil {
71-
return nil, err
72-
}
62+
err := func() error {
7363
containerCtx, containerCancel := context.WithCancelCause(ctx)
7464
defer containerCancel(errors.WithStack(context.Canceled))
75-
bkContainer, err := c.NewContainer(containerCtx, containerCfg)
65+
66+
bkContainer, err := resultCtx.NewContainer(containerCtx, cfg)
7667
if err != nil {
77-
return nil, err
68+
return err
7869
}
70+
7971
releaseCh := make(chan struct{})
8072
container := &Container{
8173
containerCancel: containerCancel,
@@ -92,8 +84,8 @@ func NewContainer(ctx context.Context, resultCtx *ResultHandle, cfg *InvokeConfi
9284
ctrCh <- container
9385
<-container.releaseCh
9486

95-
return nil, bkContainer.Release(ctx)
96-
})
87+
return bkContainer.Release(ctx)
88+
}()
9789
if err != nil {
9890
errCh <- err
9991
}

0 commit comments

Comments
 (0)