Files
buildx/monitor/commands/attach.go
Kohei Tokunaga bcf21dee44 monitor: support step-by-step breakpoint debugger
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>
2023-06-30 22:05:06 +09:00

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
}