This commit adds a set of commands to monitor for enabling breakpoint debugger. This is implemented based on the walker utility for step-by-step LLB inspection. For each vertex and breakpoint, monitor calls Solve API so the user can enter to the debugger container on each vertex for inspection. User can enter to the breakpoint debugger mode by --invoke=debug-step flag. Signed-off-by: Kohei Tokunaga <ktokunaga.mail@gmail.com>
104 lines
3.0 KiB
Go
104 lines
3.0 KiB
Go
package commands
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
|
|
controllererrors "github.com/docker/buildx/controller/errdefs"
|
|
controllerapi "github.com/docker/buildx/controller/pb"
|
|
"github.com/docker/buildx/monitor/types"
|
|
"github.com/docker/buildx/monitor/utils"
|
|
"github.com/docker/buildx/util/progress"
|
|
solverpb "github.com/moby/buildkit/solver/pb"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
type AttachCmd struct {
|
|
m types.Monitor
|
|
|
|
stdout io.WriteCloser
|
|
progress *progress.Printer
|
|
invokeConfig controllerapi.InvokeConfig
|
|
}
|
|
|
|
func NewAttachCmd(m types.Monitor, stdout io.WriteCloser, progress *progress.Printer, invokeConfig controllerapi.InvokeConfig) types.Command {
|
|
return &AttachCmd{m, stdout, progress, invokeConfig}
|
|
}
|
|
|
|
func (cm *AttachCmd) Info() types.CommandInfo {
|
|
return types.CommandInfo{
|
|
Name: "attach",
|
|
HelpMessage: "attach to a buildx server or a process in the container",
|
|
HelpMessageLong: `
|
|
Usage:
|
|
attach ID
|
|
|
|
ID is for a session (visible via list command) or a process (visible via ps command).
|
|
If you attached to a process, use Ctrl-c-a for switching the monitor to that process's STDIO.
|
|
`,
|
|
}
|
|
}
|
|
|
|
func (cm *AttachCmd) Exec(ctx context.Context, args []string) error {
|
|
if len(args) < 2 {
|
|
return errors.Errorf("ID of session or process must be passed")
|
|
}
|
|
ref := args[1]
|
|
var id string
|
|
|
|
isProcess, err := utils.IsProcessID(ctx, cm.m, cm.m.AttachedSessionID(), ref)
|
|
if err == nil && isProcess {
|
|
cm.m.Attach(ctx, ref)
|
|
id = ref
|
|
}
|
|
if id == "" {
|
|
refs, err := cm.m.List(ctx)
|
|
if err != nil {
|
|
return errors.Errorf("failed to get the list of sessions: %v", err)
|
|
}
|
|
found := false
|
|
for _, s := range refs {
|
|
if s == ref {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
return errors.Errorf("unknown ID: %q", ref)
|
|
}
|
|
cm.m.Detach() // Finish existing attach
|
|
cm.m.AttachSession(ref)
|
|
}
|
|
if !isProcess && id != "" {
|
|
var walkerDef *solverpb.Definition
|
|
if res, err := cm.m.Inspect(ctx, id); err == nil {
|
|
walkerDef = res.Definition
|
|
if !utils.IsSameDefinition(res.Definition, res.CurrentDefinition) && res.Options != nil {
|
|
// Reload the current build if breakpoint debugger was ongoing on this session
|
|
ref, _, err := cm.m.Build(ctx, *res.Options, nil, cm.progress)
|
|
if err != nil {
|
|
var be *controllererrors.BuildError
|
|
if errors.As(err, &be) {
|
|
ref = be.Ref
|
|
} else {
|
|
return errors.Errorf("failed to reload after attach: %v", err)
|
|
}
|
|
}
|
|
st, err := cm.m.Inspect(ctx, ref)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
walkerDef = st.Definition
|
|
cm.m.AttachSession(ref)
|
|
// rollback the running container with the new result
|
|
id := cm.m.Rollback(ctx, cm.invokeConfig)
|
|
fmt.Fprintf(cm.stdout, "Interactive container was restarted with process %q. Press Ctrl-a-c to switch to the new container", id)
|
|
}
|
|
}
|
|
cm.m.RegisterWalkerController(utils.NewWalkerController(cm.m, cm.stdout, cm.invokeConfig, cm.progress, walkerDef))
|
|
}
|
|
fmt.Fprintf(cm.stdout, "Attached to process %q. Press Ctrl-a-c to switch to the new container\n", id)
|
|
return nil
|
|
}
|