Skip to content

Commit 60b1eda

Browse files
authored
Merge pull request #3220 from jsternberg/monitor-driven-build
monitor: move remaining controller functionality into monitor
2 parents bb5b5e3 + 8f2604b commit 60b1eda

File tree

7 files changed

+189
-241
lines changed

7 files changed

+189
-241
lines changed

build/build.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -311,11 +311,15 @@ func toRepoOnly(in string) (string, error) {
311311
return strings.Join(out, ","), nil
312312
}
313313

314+
type Handler struct {
315+
OnResult func(driverIdx int, rCtx *ResultHandle)
316+
}
317+
314318
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) {
315319
return BuildWithResultHandler(ctx, nodes, opts, docker, cfg, w, nil)
316320
}
317321

318-
func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opts map[string]Options, docker *dockerutil.Client, cfg *confutil.Config, w progress.Writer, resultHandleFunc func(driverIdx int, rCtx *ResultHandle)) (resp map[string]*client.SolveResponse, err error) {
322+
func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opts map[string]Options, docker *dockerutil.Client, cfg *confutil.Config, w progress.Writer, bh *Handler) (resp map[string]*client.SolveResponse, err error) {
319323
if len(nodes) == 0 {
320324
return nil, errors.Errorf("driver required for build")
321325
}
@@ -509,10 +513,10 @@ func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opts map[
509513
buildRef := fmt.Sprintf("%s/%s/%s", node.Builder, node.Name, so.Ref)
510514

511515
var rr *client.SolveResponse
512-
if resultHandleFunc != nil {
516+
if bh != nil && bh.OnResult != nil {
513517
var resultHandle *ResultHandle
514518
resultHandle, rr, err = NewResultHandle(ctx, cc, *so, "buildx", buildFunc, ch)
515-
resultHandleFunc(dp.driverIndex, resultHandle)
519+
bh.OnResult(dp.driverIndex, resultHandle)
516520
} else {
517521
span, ctx := tracing.StartSpan(ctx, "build")
518522
rr, err = c.Build(ctx, *so, "buildx", buildFunc, ch)

commands/build.go

Lines changed: 17 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import (
2323
"github.com/docker/buildx/commands/debug"
2424
cbuild "github.com/docker/buildx/controller/build"
2525
controllererrors "github.com/docker/buildx/controller/errdefs"
26-
"github.com/docker/buildx/controller/local"
2726
controllerapi "github.com/docker/buildx/controller/pb"
2827
"github.com/docker/buildx/monitor"
2928
"github.com/docker/buildx/store"
@@ -32,7 +31,6 @@ import (
3231
"github.com/docker/buildx/util/cobrautil"
3332
"github.com/docker/buildx/util/confutil"
3433
"github.com/docker/buildx/util/desktop"
35-
"github.com/docker/buildx/util/ioset"
3634
"github.com/docker/buildx/util/metricutil"
3735
"github.com/docker/buildx/util/osutil"
3836
"github.com/docker/buildx/util/progress"
@@ -418,11 +416,7 @@ func getImageID(resp map[string]string) string {
418416
}
419417

420418
func runBasicBuild(ctx context.Context, dockerCli command.Cli, opts *cbuild.Options, printer *progress.Printer) (*client.SolveResponse, *build.Inputs, error) {
421-
resp, res, dfmap, err := cbuild.RunBuild(ctx, dockerCli, opts, dockerCli.In(), printer, false)
422-
if res != nil {
423-
res.Done()
424-
}
425-
return resp, dfmap, err
419+
return cbuild.RunBuild(ctx, dockerCli, opts, dockerCli.In(), printer, nil)
426420
}
427421

428422
func runControllerBuild(ctx context.Context, dockerCli command.Cli, opts *cbuild.Options, options buildOptions, printer *progress.Printer) (_ *client.SolveResponse, _ *build.Inputs, retErr error) {
@@ -433,33 +427,32 @@ func runControllerBuild(ctx context.Context, dockerCli command.Cli, opts *cbuild
433427

434428
var (
435429
in io.ReadCloser
436-
f *ioset.SingleForwarder
430+
m *monitor.Monitor
431+
bh build.Handler
437432
)
438433
if options.invokeConfig == nil {
439434
in = dockerCli.In()
440435
} else {
441-
f = ioset.NewSingleForwarder()
442-
f.SetReader(dockerCli.In())
436+
m = monitor.New(&options.invokeConfig.InvokeConfig, dockerCli.In(), os.Stdout, os.Stderr, printer)
437+
defer m.Close()
438+
439+
bh = m.Handler()
443440
}
444441

445442
for {
446-
c := local.NewController(ctx, dockerCli)
447-
448-
resp, inputs, err := c.Build(ctx, opts, in, printer)
443+
resp, inputs, err := cbuild.RunBuild(ctx, dockerCli, opts, in, printer, &bh)
449444
if err != nil {
450445
var be *controllererrors.BuildError
451446
if errors.As(err, &be) {
452447
retErr = err
453448
// We can proceed to monitor
454449
} else {
455-
c.Close()
456450
return nil, nil, errors.Wrapf(err, "failed to build")
457451
}
458452
}
459453

460-
if options.invokeConfig != nil {
461-
if err := runMonitorIfNeeded(ctx, options.invokeConfig, retErr, c, f, os.Stdout, os.Stderr, printer); err != nil {
462-
c.Close()
454+
if m != nil {
455+
if err := m.Run(ctx, err); err != nil {
463456
if errors.Is(err, monitor.ErrReload) {
464457
retErr = nil
465458
continue
@@ -468,55 +461,10 @@ func runControllerBuild(ctx context.Context, dockerCli command.Cli, opts *cbuild
468461
}
469462
}
470463

471-
c.Close()
472464
return resp, inputs, err
473465
}
474466
}
475467

476-
func runMonitorIfNeeded(ctx context.Context, cfg *invokeConfig, retErr error, c *local.Controller, stdin *ioset.SingleForwarder, stdout io.WriteCloser, stderr console.File, printer *progress.Printer) error {
477-
if !cfg.needsDebug(retErr) {
478-
return nil
479-
}
480-
481-
// Print errors before launching monitor
482-
if err := printError(retErr, printer); err != nil {
483-
logrus.Warnf("failed to print error information: %v", err)
484-
}
485-
486-
pr, pw := io.Pipe()
487-
stdin.SetWriter(pw, func() io.WriteCloser {
488-
pw.Close() // propagate EOF
489-
return nil
490-
})
491-
492-
con := console.Current()
493-
if err := con.SetRaw(); err != nil {
494-
return errors.Errorf("failed to configure terminal: %v", err)
495-
}
496-
defer con.Reset()
497-
498-
monitorErr := monitor.RunMonitor(ctx, &cfg.InvokeConfig, c, pr, stdout, stderr, printer)
499-
if err := pw.Close(); err != nil {
500-
logrus.Debug("failed to close monitor stdin pipe reader")
501-
}
502-
return monitorErr
503-
}
504-
505-
func printError(err error, printer *progress.Printer) error {
506-
if err == nil {
507-
return nil
508-
}
509-
if err := printer.Pause(); err != nil {
510-
return err
511-
}
512-
defer printer.Unpause()
513-
for _, s := range errdefs.Sources(err) {
514-
s.Print(os.Stderr)
515-
}
516-
fmt.Fprintf(os.Stderr, "ERROR: %v\n", err)
517-
return nil
518-
}
519-
520468
func newDebuggableBuild(dockerCli command.Cli, rootOpts *rootOptions) debug.DebuggableCmd {
521469
return &debuggableBuild{dockerCli: dockerCli, rootOpts: rootOpts}
522470
}
@@ -973,23 +921,21 @@ func printValue(w io.Writer, printer callFunc, version string, format string, re
973921

974922
type invokeConfig struct {
975923
controllerapi.InvokeConfig
976-
onFlag string
977924
invokeFlag string
978925
}
979926

980-
func (cfg *invokeConfig) needsDebug(retErr error) bool {
981-
switch cfg.onFlag {
927+
func (cfg *invokeConfig) parseInvokeConfig(invoke, on string) error {
928+
switch on {
982929
case "always":
983-
return true
930+
cfg.SuspendOn = controllerapi.SuspendAlways
984931
case "error":
985-
return retErr != nil
932+
cfg.SuspendOn = controllerapi.SuspendError
986933
default:
987-
return cfg.invokeFlag != ""
934+
if invoke != "" {
935+
cfg.SuspendOn = controllerapi.SuspendAlways
936+
}
988937
}
989-
}
990938

991-
func (cfg *invokeConfig) parseInvokeConfig(invoke, on string) error {
992-
cfg.onFlag = on
993939
cfg.invokeFlag = invoke
994940
cfg.Tty = true
995941
cfg.NoCmd = true

commands/debug/root.go

Lines changed: 0 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,8 @@
11
package debug
22

33
import (
4-
"context"
5-
"os"
6-
7-
"github.com/containerd/console"
8-
"github.com/docker/buildx/controller/local"
9-
controllerapi "github.com/docker/buildx/controller/pb"
10-
"github.com/docker/buildx/monitor"
114
"github.com/docker/buildx/util/cobrautil"
12-
"github.com/docker/buildx/util/progress"
135
"github.com/docker/cli/cli/command"
14-
"github.com/moby/buildkit/util/progress/progressui"
15-
"github.com/pkg/errors"
166
"github.com/spf13/cobra"
177
)
188

@@ -38,27 +28,6 @@ func RootCmd(dockerCli command.Cli, children ...DebuggableCmd) *cobra.Command {
3828
cmd := &cobra.Command{
3929
Use: "debug",
4030
Short: "Start debugger",
41-
Args: cobra.NoArgs,
42-
RunE: func(cmd *cobra.Command, args []string) error {
43-
printer, err := progress.NewPrinter(context.TODO(), os.Stderr, progressui.DisplayMode(progressMode))
44-
if err != nil {
45-
return err
46-
}
47-
48-
ctx := context.TODO()
49-
c := local.NewController(ctx, dockerCli)
50-
51-
con := console.Current()
52-
if err := con.SetRaw(); err != nil {
53-
return errors.Errorf("failed to configure terminal: %v", err)
54-
}
55-
56-
err = monitor.RunMonitor(ctx, &controllerapi.InvokeConfig{
57-
Tty: true,
58-
}, c, dockerCli.In(), os.Stdout, os.Stderr, printer)
59-
con.Reset()
60-
return err
61-
},
6231
}
6332
cobrautil.MarkCommandExperimental(cmd)
6433

controller/build/build.go

Lines changed: 19 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ import (
55
"io"
66
"path/filepath"
77
"strings"
8-
"sync"
98

109
"github.com/docker/buildx/build"
1110
"github.com/docker/buildx/builder"
11+
"github.com/docker/buildx/controller/errdefs"
1212
controllerapi "github.com/docker/buildx/controller/pb"
1313
"github.com/docker/buildx/store"
1414
"github.com/docker/buildx/store/storeutil"
@@ -34,9 +34,9 @@ const defaultTargetName = "default"
3434
// NOTE: When an error happens during the build and this function acquires the debuggable *build.ResultHandle,
3535
// this function returns it in addition to the error (i.e. it does "return nil, res, err"). The caller can
3636
// inspect the result and debug the cause of that error.
37-
func RunBuild(ctx context.Context, dockerCli command.Cli, in *Options, inStream io.Reader, progress progress.Writer, generateResult bool) (*client.SolveResponse, *build.ResultHandle, *build.Inputs, error) {
37+
func RunBuild(ctx context.Context, dockerCli command.Cli, in *Options, inStream io.Reader, progress progress.Writer, bh *build.Handler) (*client.SolveResponse, *build.Inputs, error) {
3838
if in.NoCache && len(in.NoCacheFilter) > 0 {
39-
return nil, nil, nil, errors.Errorf("--no-cache and --no-cache-filter cannot currently be used together")
39+
return nil, nil, errors.Errorf("--no-cache and --no-cache-filter cannot currently be used together")
4040
}
4141

4242
contexts := map[string]build.NamedContext{}
@@ -70,7 +70,7 @@ func RunBuild(ctx context.Context, dockerCli command.Cli, in *Options, inStream
7070

7171
platforms, err := platformutil.Parse(in.Platforms)
7272
if err != nil {
73-
return nil, nil, nil, err
73+
return nil, nil, err
7474
}
7575
opts.Platforms = platforms
7676

@@ -81,7 +81,7 @@ func RunBuild(ctx context.Context, dockerCli command.Cli, in *Options, inStream
8181

8282
secrets, err := controllerapi.CreateSecrets(in.Secrets)
8383
if err != nil {
84-
return nil, nil, nil, err
84+
return nil, nil, err
8585
}
8686
opts.Session = append(opts.Session, secrets)
8787

@@ -91,13 +91,13 @@ func RunBuild(ctx context.Context, dockerCli command.Cli, in *Options, inStream
9191
}
9292
ssh, err := controllerapi.CreateSSH(sshSpecs)
9393
if err != nil {
94-
return nil, nil, nil, err
94+
return nil, nil, err
9595
}
9696
opts.Session = append(opts.Session, ssh)
9797

9898
outputs, _, err := controllerapi.CreateExports(in.Exports)
9999
if err != nil {
100-
return nil, nil, nil, err
100+
return nil, nil, err
101101
}
102102
if in.ExportPush {
103103
var pushUsed bool
@@ -136,7 +136,7 @@ func RunBuild(ctx context.Context, dockerCli command.Cli, in *Options, inStream
136136

137137
annotations, err := buildflags.ParseAnnotations(in.Annotations)
138138
if err != nil {
139-
return nil, nil, nil, errors.Wrap(err, "parse annotations")
139+
return nil, nil, errors.Wrap(err, "parse annotations")
140140
}
141141

142142
for _, o := range outputs {
@@ -156,7 +156,7 @@ func RunBuild(ctx context.Context, dockerCli command.Cli, in *Options, inStream
156156

157157
allow, err := buildflags.ParseEntitlements(in.Allow)
158158
if err != nil {
159-
return nil, nil, nil, err
159+
return nil, nil, err
160160
}
161161
opts.Allow = allow
162162

@@ -180,56 +180,40 @@ func RunBuild(ctx context.Context, dockerCli command.Cli, in *Options, inStream
180180
builder.WithContextPathHash(contextPathHash),
181181
)
182182
if err != nil {
183-
return nil, nil, nil, err
183+
return nil, nil, err
184184
}
185185
if err = updateLastActivity(dockerCli, b.NodeGroup); err != nil {
186-
return nil, nil, nil, errors.Wrapf(err, "failed to update builder last activity time")
186+
return nil, nil, errors.Wrapf(err, "failed to update builder last activity time")
187187
}
188188
nodes, err := b.LoadNodes(ctx)
189189
if err != nil {
190-
return nil, nil, nil, err
190+
return nil, nil, err
191191
}
192192

193193
var inputs *build.Inputs
194194
buildOptions := map[string]build.Options{defaultTargetName: opts}
195-
resp, res, err := buildTargets(ctx, dockerCli, nodes, buildOptions, progress, generateResult)
195+
resp, err := buildTargets(ctx, dockerCli, nodes, buildOptions, progress, bh)
196196
err = wrapBuildError(err, false)
197197
if err != nil {
198-
// NOTE: buildTargets can return *build.ResultHandle even on error.
199-
return nil, res, nil, err
198+
return nil, nil, errdefs.WrapBuild(err)
200199
}
201200
if i, ok := buildOptions[defaultTargetName]; ok {
202201
inputs = &i.Inputs
203202
}
204-
return resp, res, inputs, nil
203+
return resp, inputs, nil
205204
}
206205

207206
// buildTargets runs the specified build and returns the result.
208207
//
209208
// NOTE: When an error happens during the build and this function acquires the debuggable *build.ResultHandle,
210209
// this function returns it in addition to the error (i.e. it does "return nil, res, err"). The caller can
211210
// inspect the result and debug the cause of that error.
212-
func buildTargets(ctx context.Context, dockerCli command.Cli, nodes []builder.Node, opts map[string]build.Options, progress progress.Writer, generateResult bool) (*client.SolveResponse, *build.ResultHandle, error) {
213-
var res *build.ResultHandle
214-
var resp map[string]*client.SolveResponse
215-
var err error
216-
if generateResult {
217-
var mu sync.Mutex
218-
var idx int
219-
resp, err = build.BuildWithResultHandler(ctx, nodes, opts, dockerutil.NewClient(dockerCli), confutil.NewConfig(dockerCli), progress, func(driverIndex int, gotRes *build.ResultHandle) {
220-
mu.Lock()
221-
defer mu.Unlock()
222-
if res == nil || driverIndex < idx {
223-
idx, res = driverIndex, gotRes
224-
}
225-
})
226-
} else {
227-
resp, err = build.Build(ctx, nodes, opts, dockerutil.NewClient(dockerCli), confutil.NewConfig(dockerCli), progress)
228-
}
211+
func buildTargets(ctx context.Context, dockerCli command.Cli, nodes []builder.Node, opts map[string]build.Options, progress progress.Writer, bh *build.Handler) (*client.SolveResponse, error) {
212+
resp, err := build.BuildWithResultHandler(ctx, nodes, opts, dockerutil.NewClient(dockerCli), confutil.NewConfig(dockerCli), progress, bh)
229213
if err != nil {
230-
return nil, res, err
214+
return nil, err
231215
}
232-
return resp[defaultTargetName], res, err
216+
return resp[defaultTargetName], err
233217
}
234218

235219
func wrapBuildError(err error, bake bool) error {

0 commit comments

Comments
 (0)