diff --git a/commands/build.go b/commands/build.go index f8b9f1268a1..98e34120a3b 100644 --- a/commands/build.go +++ b/commands/build.go @@ -401,13 +401,17 @@ func runControllerBuild(ctx context.Context, dockerCli command.Cli, opts *contro pw2.Close() // propagate EOF return nil }) - err = options.invokeConfig.runDebug(ctx, ref, opts, c, pr2, os.Stdout, os.Stderr, printer) + monitorBuildResult, err := options.invokeConfig.runDebug(ctx, ref, opts, c, pr2, os.Stdout, os.Stderr, printer) if err := pw2.Close(); err != nil { logrus.Debug("failed to close monitor stdin pipe reader") } if err != nil { logrus.Warnf("failed to run monitor: %v", err) } + if monitorBuildResult != nil { + // Update return values with the last build result from monitor + resp, retErr = monitorBuildResult.Resp, monitorBuildResult.Err + } } else { if err := c.Disconnect(ctx, ref); err != nil { logrus.Warnf("disconnect error: %v", err) @@ -418,6 +422,9 @@ func runControllerBuild(ctx context.Context, dockerCli command.Cli, opts *contro } func printError(err error, printer *progress.Printer) error { + if err == nil { + return nil + } if err := printer.Pause(); err != nil { return err } @@ -841,14 +848,14 @@ func (cfg *invokeConfig) needsDebug(retErr error) bool { } } -func (cfg *invokeConfig) runDebug(ctx context.Context, ref string, options *controllerapi.BuildOptions, c control.BuildxController, stdin io.ReadCloser, stdout io.WriteCloser, stderr console.File, progress *progress.Printer) error { +func (cfg *invokeConfig) runDebug(ctx context.Context, ref string, options *controllerapi.BuildOptions, c control.BuildxController, stdin io.ReadCloser, stdout io.WriteCloser, stderr console.File, progress *progress.Printer) (*monitor.MonitorBuildResult, error) { con := console.Current() if err := con.SetRaw(); err != nil { // TODO: run disconnect in build command (on error case) if err := c.Disconnect(ctx, ref); err != nil { logrus.Warnf("disconnect error: %v", err) } - return errors.Errorf("failed to configure terminal: %v", err) + return nil, errors.Errorf("failed to configure terminal: %v", err) } defer con.Reset() return monitor.RunMonitor(ctx, ref, options, cfg.InvokeConfig, c, stdin, stdout, stderr, progress) diff --git a/commands/debug/root.go b/commands/debug/root.go index 71b5b41796a..4d803c10a7d 100644 --- a/commands/debug/root.go +++ b/commands/debug/root.go @@ -66,7 +66,7 @@ func RootCmd(dockerCli command.Cli, children ...DebuggableCmd) *cobra.Command { return errors.Errorf("failed to configure terminal: %v", err) } - err = monitor.RunMonitor(ctx, "", nil, controllerapi.InvokeConfig{ + _, err = monitor.RunMonitor(ctx, "", nil, controllerapi.InvokeConfig{ Tty: true, }, c, dockerCli.In(), os.Stdout, os.Stderr, printer) con.Reset() diff --git a/controller/processes/processes.go b/controller/processes/processes.go index c873860152f..3cd9e14f8a0 100644 --- a/controller/processes/processes.go +++ b/controller/processes/processes.go @@ -137,11 +137,7 @@ func (m *Manager) StartProcess(pid string, resultCtx *build.ResultHandle, cfg *p go func() { var err error if err = ctr.Exec(ctx, cfg, in.Stdin, in.Stdout, in.Stderr); err != nil { - if errors.Is(err, context.Canceled) { - logrus.Debugf("process canceled: %v", err) - } else { - logrus.Errorf("failed to exec process: %v", err) - } + logrus.Debugf("process error: %v", err) } logrus.Debugf("finished process %s %v", pid, cfg.Entrypoint) m.processes.Delete(pid) diff --git a/monitor/commands/reload.go b/monitor/commands/reload.go index b5938026e0c..b2395f4bd32 100644 --- a/monitor/commands/reload.go +++ b/monitor/commands/reload.go @@ -9,6 +9,7 @@ import ( controllerapi "github.com/docker/buildx/controller/pb" "github.com/docker/buildx/monitor/types" "github.com/docker/buildx/util/progress" + "github.com/moby/buildkit/solver/errdefs" "github.com/pkg/errors" ) @@ -70,6 +71,11 @@ func (cm *ReloadCmd) Exec(ctx context.Context, args []string) error { } else { fmt.Printf("failed to reload: %v\n", err) } + // report error + for _, s := range errdefs.Sources(err) { + s.Print(cm.stdout) + } + fmt.Fprintf(cm.stdout, "ERROR: %v\n", err) } else { resultUpdated = true } diff --git a/monitor/monitor.go b/monitor/monitor.go index f856682d8ca..1201e79a0e0 100644 --- a/monitor/monitor.go +++ b/monitor/monitor.go @@ -17,14 +17,20 @@ import ( "github.com/docker/buildx/util/ioset" "github.com/docker/buildx/util/progress" "github.com/google/shlex" + "github.com/moby/buildkit/client" "github.com/moby/buildkit/identity" "github.com/pkg/errors" "github.com/sirupsen/logrus" "golang.org/x/term" ) +type MonitorBuildResult struct { + Resp *client.SolveResponse + Err error +} + // RunMonitor provides an interactive session for running and managing containers via specified IO. -func RunMonitor(ctx context.Context, curRef string, options *controllerapi.BuildOptions, invokeConfig controllerapi.InvokeConfig, c control.BuildxController, stdin io.ReadCloser, stdout io.WriteCloser, stderr console.File, progress *progress.Printer) error { +func RunMonitor(ctx context.Context, curRef string, options *controllerapi.BuildOptions, invokeConfig controllerapi.InvokeConfig, c control.BuildxController, stdin io.ReadCloser, stdout io.WriteCloser, stderr console.File, progress *progress.Printer) (*MonitorBuildResult, error) { defer func() { if err := c.Disconnect(ctx, curRef); err != nil { logrus.Warnf("disconnect error: %v", err) @@ -32,7 +38,7 @@ func RunMonitor(ctx context.Context, curRef string, options *controllerapi.Build }() if err := progress.Pause(); err != nil { - return err + return nil, err } defer progress.Unpause() @@ -169,10 +175,10 @@ func RunMonitor(ctx context.Context, curRef string, options *controllerapi.Build select { case <-doneCh: m.close() - return nil + return m.lastBuildResult, nil case err := <-errCh: m.close() - return err + return m.lastBuildResult, err case <-monitorDisableCh: } monitorForwarder.SetOut(nil) @@ -233,6 +239,14 @@ type monitor struct { invokeIO *ioset.Forwarder invokeCancel func() attachedPid atomic.Value + + lastBuildResult *MonitorBuildResult +} + +func (m *monitor) Build(ctx context.Context, options controllerapi.BuildOptions, in io.ReadCloser, progress progress.Writer) (ref string, resp *client.SolveResponse, err error) { + ref, resp, err = m.BuildxController.Build(ctx, options, in, progress) + m.lastBuildResult = &MonitorBuildResult{Resp: resp, Err: err} // Record build result + return } func (m *monitor) DisconnectSession(ctx context.Context, targetID string) error { @@ -288,7 +302,11 @@ func (m *monitor) startInvoke(ctx context.Context, pid string, cfg controllerapi go func() { // Start a new invoke if err := m.invoke(ctx, pid, cfg); err != nil { - logrus.Debugf("invoke error: %v", err) + if errors.Is(err, context.Canceled) { + logrus.Debugf("process canceled: %v", err) + } else { + logrus.Errorf("invoke: %v", err) + } } if pid == m.attachedPid.Load() { m.attachedPid.Store("")