Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions monitor/commands/attach.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package commands

import (
"context"
"fmt"
"io"

"github.com/docker/buildx/monitor/types"
"github.com/pkg/errors"
)

type AttachCmd struct {
m types.Monitor

stdout io.WriteCloser
}

func NewAttachCmd(m types.Monitor, stdout io.WriteCloser) types.Command {
return &AttachCmd{m, stdout}
}

func (cm *AttachCmd) Info() types.CommandInfo {
return types.CommandInfo{HelpMessage: "attach to a buildx server or a process in the container"}
}

func (cm *AttachCmd) Exec(ctx context.Context, args []string) error {
if len(args) < 2 {
return errors.Errorf("server name must be passed")
}
ref := args[1]
var id string

isProcess, err := isProcessID(ctx, cm.m, 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)
}
fmt.Fprintf(cm.stdout, "Attached to process %q. Press Ctrl-a-c to switch to the new container\n", id)
return nil
}

func isProcessID(ctx context.Context, c types.Monitor, ref string) (bool, error) {
infos, err := c.ListProcesses(ctx, c.AttachedSessionID())
if err != nil {
return false, err
}
for _, p := range infos {
if p.ProcessID == ref {
return true, nil
}
}
return false, nil
}
38 changes: 38 additions & 0 deletions monitor/commands/disconnect.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package commands

import (
"context"

"github.com/docker/buildx/monitor/types"
"github.com/pkg/errors"
)

type DisconnectCmd struct {
m types.Monitor
}

func NewDisconnectCmd(m types.Monitor) types.Command {
return &DisconnectCmd{m}
}

func (cm *DisconnectCmd) Info() types.CommandInfo {
return types.CommandInfo{HelpMessage: "disconnect a client from a buildx server. Specific session ID can be specified an arg"}
}

func (cm *DisconnectCmd) Exec(ctx context.Context, args []string) error {
target := cm.m.AttachedSessionID()
if len(args) >= 2 {
target = args[1]
}
isProcess, err := isProcessID(ctx, cm.m, target)
if err == nil && isProcess {
if err := cm.m.DisconnectProcess(ctx, cm.m.AttachedSessionID(), target); err != nil {
return errors.Errorf("disconnecting from process failed %v", target)
}
return nil
}
if err := cm.m.DisconnectSession(ctx, target); err != nil {
return errors.Errorf("disconnecting from session failed: %v", err)
}
return nil
}
44 changes: 44 additions & 0 deletions monitor/commands/exec.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package commands

import (
"context"
"fmt"
"io"

controllerapi "github.com/docker/buildx/controller/pb"
"github.com/docker/buildx/monitor/types"
"github.com/pkg/errors"
)

type ExecCmd struct {
m types.Monitor

invokeConfig controllerapi.InvokeConfig
stdout io.WriteCloser
}

func NewExecCmd(m types.Monitor, invokeConfig controllerapi.InvokeConfig, stdout io.WriteCloser) types.Command {
return &ExecCmd{m, invokeConfig, stdout}
}

func (cm *ExecCmd) Info() types.CommandInfo {
return types.CommandInfo{HelpMessage: "execute a process in the interactive container"}
}

func (cm *ExecCmd) Exec(ctx context.Context, args []string) error {
if len(args) < 2 {
return errors.Errorf("command must be passed")
}
cfg := controllerapi.InvokeConfig{
Entrypoint: []string{args[1]},
Cmd: args[2:],
// TODO: support other options as well via flags
Env: cm.invokeConfig.Env,
User: cm.invokeConfig.User,
Cwd: cm.invokeConfig.Cwd,
Tty: true,
}
pid := cm.m.Exec(ctx, cfg)
fmt.Fprintf(cm.stdout, "Process %q started. Press Ctrl-a-c to switch to that process.\n", pid)
return nil
}
27 changes: 27 additions & 0 deletions monitor/commands/kill.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package commands

import (
"context"

"github.com/docker/buildx/monitor/types"
"github.com/pkg/errors"
)

type KillCmd struct {
m types.Monitor
}

func NewKillCmd(m types.Monitor) types.Command {
return &KillCmd{m}
}

func (cm *KillCmd) Info() types.CommandInfo {
return types.CommandInfo{HelpMessage: "kill buildx server"}
}

func (cm *KillCmd) Exec(ctx context.Context, args []string) error {
if err := cm.m.Kill(ctx); err != nil {
return errors.Errorf("failed to kill: %v", err)
}
return nil
}
40 changes: 40 additions & 0 deletions monitor/commands/list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package commands

import (
"context"
"fmt"
"io"
"sort"
"text/tabwriter"

"github.com/docker/buildx/monitor/types"
)

type ListCmd struct {
m types.Monitor

stdout io.WriteCloser
}

func NewListCmd(m types.Monitor, stdout io.WriteCloser) types.Command {
return &ListCmd{m, stdout}
}

func (cm *ListCmd) Info() types.CommandInfo {
return types.CommandInfo{HelpMessage: "list buildx sessions"}
}

func (cm *ListCmd) Exec(ctx context.Context, args []string) error {
refs, err := cm.m.List(ctx)
if err != nil {
return err
}
sort.Strings(refs)
tw := tabwriter.NewWriter(cm.stdout, 1, 8, 1, '\t', 0)
fmt.Fprintln(tw, "ID\tCURRENT_SESSION")
for _, k := range refs {
fmt.Fprintf(tw, "%-20s\t%v\n", k, k == cm.m.AttachedSessionID())
}
tw.Flush()
return nil
}
38 changes: 38 additions & 0 deletions monitor/commands/ps.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package commands

import (
"context"
"fmt"
"io"
"text/tabwriter"

"github.com/docker/buildx/monitor/types"
)

type PsCmd struct {
m types.Monitor
stdout io.WriteCloser
}

func NewPsCmd(m types.Monitor, stdout io.WriteCloser) types.Command {
return &PsCmd{m, stdout}
}

func (cm *PsCmd) Info() types.CommandInfo {
return types.CommandInfo{HelpMessage: `list processes invoked by "exec". Use "attach" to attach IO to that process`}
}

func (cm *PsCmd) Exec(ctx context.Context, args []string) error {
ref := cm.m.AttachedSessionID()
plist, err := cm.m.ListProcesses(ctx, ref)
if err != nil {
return err
}
tw := tabwriter.NewWriter(cm.stdout, 1, 8, 1, '\t', 0)
fmt.Fprintln(tw, "PID\tCURRENT_SESSION\tCOMMAND")
for _, p := range plist {
fmt.Fprintf(tw, "%-20s\t%v\t%v\n", p.ProcessID, p.ProcessID == cm.m.AttachedPID(), append(p.InvokeConfig.Entrypoint, p.InvokeConfig.Cmd...))
}
tw.Flush()
return nil
}
76 changes: 76 additions & 0 deletions monitor/commands/reload.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
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/util/progress"
"github.com/pkg/errors"
)

type ReloadCmd struct {
m types.Monitor

stdout io.WriteCloser
progress *progress.Printer

options *controllerapi.BuildOptions
invokeConfig controllerapi.InvokeConfig
}

func NewReloadCmd(m types.Monitor, stdout io.WriteCloser, progress *progress.Printer, options *controllerapi.BuildOptions, invokeConfig controllerapi.InvokeConfig) types.Command {
return &ReloadCmd{m, stdout, progress, options, invokeConfig}
}

func (cm *ReloadCmd) Info() types.CommandInfo {
return types.CommandInfo{HelpMessage: "reloads the context and build it"}
}

func (cm *ReloadCmd) Exec(ctx context.Context, args []string) error {
var bo *controllerapi.BuildOptions
if ref := cm.m.AttachedSessionID(); ref != "" {
// Rebuilding an existing session; Restore the build option used for building this session.
res, err := cm.m.Inspect(ctx, ref)
if err != nil {
fmt.Printf("failed to inspect the current build session: %v\n", err)
} else {
bo = res.Options
}
} else {
bo = cm.options
}
if bo == nil {
return errors.Errorf("no build option is provided")
}
if ref := cm.m.AttachedSessionID(); ref != "" {
if err := cm.m.Disconnect(ctx, ref); err != nil {
fmt.Println("disconnect error", err)
}
}
var resultUpdated bool
cm.progress.Unpause()
ref, _, err := cm.m.Build(ctx, *bo, nil, cm.progress) // TODO: support stdin, hold build ref
cm.progress.Pause()
if err != nil {
var be *controllererrors.BuildError
if errors.As(err, &be) {
ref = be.Ref
resultUpdated = true
} else {
fmt.Printf("failed to reload: %v\n", err)
}
} else {
resultUpdated = true
}
cm.m.AttachSession(ref)
if resultUpdated {
// 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\n", id)
}
return nil
}
43 changes: 43 additions & 0 deletions monitor/commands/rollback.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package commands

import (
"context"
"fmt"
"io"

controllerapi "github.com/docker/buildx/controller/pb"
"github.com/docker/buildx/monitor/types"
)

type RollbackCmd struct {
m types.Monitor

invokeConfig controllerapi.InvokeConfig
stdout io.WriteCloser
}

func NewRollbackCmd(m types.Monitor, invokeConfig controllerapi.InvokeConfig, stdout io.WriteCloser) types.Command {
return &RollbackCmd{m, invokeConfig, stdout}
}

func (cm *RollbackCmd) Info() types.CommandInfo {
return types.CommandInfo{HelpMessage: "re-runs the interactive container with initial rootfs contents"}
}

func (cm *RollbackCmd) Exec(ctx context.Context, args []string) error {
cfg := cm.invokeConfig
if len(args) >= 2 {
cmds := args[1:]
if cmds[0] == "--init" {
cfg.Initial = true
cmds = cmds[1:]
}
if len(cmds) > 0 {
cfg.Entrypoint = []string{cmds[0]}
cfg.Cmd = cmds[1:]
}
}
id := cm.m.Rollback(ctx, cfg)
fmt.Fprintf(cm.stdout, "Interactive container was restarted with process %q. Press Ctrl-a-c to switch to the new container\n", id)
return nil
}
Loading