From e8f55a3cf76a511b12dae335a1f2d6cb8e666a5a Mon Sep 17 00:00:00 2001 From: Kohei Tokunaga Date: Tue, 14 Feb 2023 21:16:29 +0900 Subject: [PATCH] monitor: Enable to exec into the container Signed-off-by: Kohei Tokunaga --- build/build.go | 145 ------- build/invoke.go | 216 ++++++++++ commands/build.go | 6 +- controller/control/controller.go | 8 +- controller/local/controller.go | 81 +++- controller/pb/controller.pb.go | 635 ++++++++++++++++++++++-------- controller/pb/controller.proto | 32 +- controller/processes/processes.go | 149 +++++++ controller/remote/client.go | 19 +- controller/remote/server.go | 212 +++++----- driver/docker-container/driver.go | 2 + monitor/monitor.go | 176 +++++++-- util/ioset/ioset.go | 28 +- util/ioset/mux.go | 21 + 14 files changed, 1249 insertions(+), 481 deletions(-) create mode 100644 build/invoke.go create mode 100644 controller/processes/processes.go diff --git a/build/build.go b/build/build.go index aec66172..3728711d 100644 --- a/build/build.go +++ b/build/build.go @@ -660,151 +660,6 @@ func toSolveOpt(ctx context.Context, node builder.Node, multiDriver bool, opt Op return &so, releaseF, nil } -// ContainerConfig is configuration for a container to run. -type ContainerConfig struct { - ResultCtx *ResultContext - - Stdin io.ReadCloser - Stdout io.WriteCloser - Stderr io.WriteCloser - Tty bool - - Entrypoint []string - Cmd []string - Env []string - User *string - Cwd *string -} - -// ResultContext is a build result with the client that built it. -type ResultContext struct { - Client *client.Client - Res *gateway.Result -} - -// Invoke invokes a build result as a container. -func Invoke(ctx context.Context, cfg ContainerConfig) error { - if cfg.ResultCtx == nil { - return errors.Errorf("result must be provided") - } - c, res := cfg.ResultCtx.Client, cfg.ResultCtx.Res - - mainCtx := ctx - - _, err := c.Build(context.TODO(), client.SolveOpt{}, "buildx", func(ctx context.Context, c gateway.Client) (*gateway.Result, error) { - ctx, cancel := context.WithCancel(ctx) - go func() { - <-mainCtx.Done() - cancel() - }() - - if res.Ref == nil { - return nil, errors.Errorf("no reference is registered") - } - st, err := res.Ref.ToState() - if err != nil { - return nil, err - } - def, err := st.Marshal(ctx) - if err != nil { - return nil, err - } - imgRef, err := c.Solve(ctx, gateway.SolveRequest{ - Definition: def.ToPB(), - }) - if err != nil { - return nil, err - } - ctr, err := c.NewContainer(ctx, gateway.NewContainerRequest{ - Mounts: []gateway.Mount{ - { - Dest: "/", - MountType: pb.MountType_BIND, - Ref: imgRef.Ref, - }, - }, - }) - if err != nil { - return nil, err - } - defer ctr.Release(context.TODO()) - - imgData := res.Metadata[exptypes.ExporterImageConfigKey] - var img *specs.Image - if len(imgData) > 0 { - img = &specs.Image{} - if err := json.Unmarshal(imgData, img); err != nil { - fmt.Println(err) - return nil, err - } - } - - user := "" - if cfg.User != nil { - user = *cfg.User - } else if img != nil { - user = img.Config.User - } - - cwd := "" - if cfg.Cwd != nil { - cwd = *cfg.Cwd - } else if img != nil { - cwd = img.Config.WorkingDir - } - - env := []string{} - if img != nil { - env = append(env, img.Config.Env...) - } - env = append(env, cfg.Env...) - - args := []string{} - if cfg.Entrypoint != nil { - args = append(args, cfg.Entrypoint...) - } else if img != nil { - args = append(args, img.Config.Entrypoint...) - } - if cfg.Cmd != nil { - args = append(args, cfg.Cmd...) - } else if img != nil { - args = append(args, img.Config.Cmd...) - } - - proc, err := ctr.Start(ctx, gateway.StartRequest{ - Args: args, - Env: env, - User: user, - Cwd: cwd, - Tty: cfg.Tty, - Stdin: cfg.Stdin, - Stdout: cfg.Stdout, - Stderr: cfg.Stderr, - }) - if err != nil { - return nil, errors.Errorf("failed to start container: %v", err) - } - errCh := make(chan error) - doneCh := make(chan struct{}) - go func() { - if err := proc.Wait(); err != nil { - errCh <- err - return - } - close(doneCh) - }() - select { - case <-doneCh: - case <-ctx.Done(): - return nil, ctx.Err() - case err := <-errCh: - return nil, err - } - return nil, nil - }, nil) - return err -} - func Build(ctx context.Context, nodes []builder.Node, opt map[string]Options, docker *dockerutil.Client, configDir string, w progress.Writer) (resp map[string]*client.SolveResponse, err error) { return BuildWithResultHandler(ctx, nodes, opt, docker, configDir, w, nil) } diff --git a/build/invoke.go b/build/invoke.go new file mode 100644 index 00000000..427b3e1c --- /dev/null +++ b/build/invoke.go @@ -0,0 +1,216 @@ +package build + +import ( + "context" + _ "crypto/sha256" // ensure digests can be computed + "encoding/json" + "fmt" + "io" + "sync" + "sync/atomic" + "syscall" + + controllerapi "github.com/docker/buildx/controller/pb" + "github.com/moby/buildkit/client" + "github.com/moby/buildkit/exporter/containerimage/exptypes" + gateway "github.com/moby/buildkit/frontend/gateway/client" + "github.com/moby/buildkit/solver/pb" + specs "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +// ResultContext is a build result with the client that built it. +type ResultContext struct { + Client *client.Client + Res *gateway.Result +} + +type Container struct { + cancelOnce sync.Once + containerCancel func() + + isUnavailable atomic.Bool + + initStarted atomic.Bool + + container gateway.Container + image *specs.Image + + releaseCh chan struct{} +} + +func NewContainer(ctx context.Context, resultCtx *ResultContext) (*Container, error) { + c, res := resultCtx.Client, resultCtx.Res + + mainCtx := ctx + + ctrCh := make(chan *Container) + errCh := make(chan error) + go func() { + _, err := c.Build(context.TODO(), client.SolveOpt{}, "buildx", func(ctx context.Context, c gateway.Client) (*gateway.Result, error) { + ctx, cancel := context.WithCancel(ctx) + go func() { + <-mainCtx.Done() + cancel() + }() + + if res.Ref == nil { + return nil, errors.Errorf("no reference is registered") + } + st, err := res.Ref.ToState() + if err != nil { + return nil, err + } + def, err := st.Marshal(ctx) + if err != nil { + return nil, err + } + imgRef, err := c.Solve(ctx, gateway.SolveRequest{ + Definition: def.ToPB(), + }) + if err != nil { + return nil, err + } + containerCtx, containerCancel := context.WithCancel(ctx) + defer containerCancel() + bkContainer, err := c.NewContainer(containerCtx, gateway.NewContainerRequest{ + Mounts: []gateway.Mount{ + { + Dest: "/", + MountType: pb.MountType_BIND, + Ref: imgRef.Ref, + }, + }, + }) + if err != nil { + return nil, err + } + imgData := res.Metadata[exptypes.ExporterImageConfigKey] + var img *specs.Image + if len(imgData) > 0 { + img = &specs.Image{} + if err := json.Unmarshal(imgData, img); err != nil { + fmt.Println(err) + return nil, err + } + } + releaseCh := make(chan struct{}) + container := &Container{ + containerCancel: containerCancel, + container: bkContainer, + image: img, + releaseCh: releaseCh, + } + ctrCh <- container + <-container.releaseCh + + return nil, bkContainer.Release(ctx) + }, nil) + if err != nil { + errCh <- err + } + }() + select { + case ctr := <-ctrCh: + return ctr, nil + case err := <-errCh: + return nil, err + case <-mainCtx.Done(): + return nil, mainCtx.Err() + } +} + +func (c *Container) Cancel() { + c.markUnavailable() + c.cancelOnce.Do(func() { + if c.containerCancel != nil { + c.containerCancel() + } + close(c.releaseCh) + }) +} + +func (c *Container) IsUnavailable() bool { + return c.isUnavailable.Load() +} + +func (c *Container) markUnavailable() { + c.isUnavailable.Store(true) +} + +func (c *Container) Exec(ctx context.Context, cfg *controllerapi.InvokeConfig, stdin io.ReadCloser, stdout io.WriteCloser, stderr io.WriteCloser) error { + if isInit := c.initStarted.CompareAndSwap(false, true); isInit { + defer func() { + // container can't be used after init exits + c.markUnavailable() + }() + } + err := exec(ctx, cfg, c.container, c.image, stdin, stdout, stderr) + if err != nil { + // Container becomes unavailable if one of the processes fails in it. + c.markUnavailable() + } + return err +} + +func exec(ctx context.Context, cfg *controllerapi.InvokeConfig, ctr gateway.Container, img *specs.Image, stdin io.ReadCloser, stdout io.WriteCloser, stderr io.WriteCloser) error { + user := "" + if !cfg.NoUser { + user = cfg.User + } else if img != nil { + user = img.Config.User + } + + cwd := "" + if !cfg.NoCwd { + cwd = cfg.Cwd + } else if img != nil { + cwd = img.Config.WorkingDir + } + + env := []string{} + if img != nil { + env = append(env, img.Config.Env...) + } + env = append(env, cfg.Env...) + + args := []string{} + if cfg.Entrypoint != nil { + args = append(args, cfg.Entrypoint...) + } + if cfg.Cmd != nil { + args = append(args, cfg.Cmd...) + } + // caller should always set args + if len(args) == 0 { + return errors.Errorf("specify args to execute") + } + proc, err := ctr.Start(ctx, gateway.StartRequest{ + Args: args, + Env: env, + User: user, + Cwd: cwd, + Tty: cfg.Tty, + Stdin: stdin, + Stdout: stdout, + Stderr: stderr, + }) + if err != nil { + return errors.Errorf("failed to start container: %v", err) + } + + doneCh := make(chan struct{}) + defer close(doneCh) + go func() { + select { + case <-ctx.Done(): + if err := proc.Signal(ctx, syscall.SIGKILL); err != nil { + logrus.Warnf("failed to kill process: %v", err) + } + case <-doneCh: + } + }() + + return proc.Wait() +} diff --git a/commands/build.go b/commands/build.go index 90ce152f..046af1f0 100644 --- a/commands/build.go +++ b/commands/build.go @@ -469,10 +469,10 @@ func launchControllerAndRunBuild(dockerCli command.Cli, options buildOptions) er // stdin must be usable for monitor return errors.Errorf("Dockerfile or context from stdin is not supported with invoke") } - var invokeConfig controllerapi.ContainerConfig + var invokeConfig controllerapi.InvokeConfig if inv := options.invoke; inv != "" { var err error - invokeConfig, err = parseInvokeConfig(inv) // TODO: produce *controller.ContainerConfig directly. + invokeConfig, err = parseInvokeConfig(inv) if err != nil { return err } @@ -566,7 +566,7 @@ func launchControllerAndRunBuild(dockerCli command.Cli, options buildOptions) er return nil } -func parseInvokeConfig(invoke string) (cfg controllerapi.ContainerConfig, err error) { +func parseInvokeConfig(invoke string) (cfg controllerapi.InvokeConfig, err error) { cfg.Tty = true if invoke == "default" { return cfg, nil diff --git a/controller/control/controller.go b/controller/control/controller.go index eb9b83a9..177e770a 100644 --- a/controller/control/controller.go +++ b/controller/control/controller.go @@ -11,11 +11,17 @@ import ( type BuildxController interface { Build(ctx context.Context, options controllerapi.BuildOptions, in io.ReadCloser, w io.Writer, out console.File, progressMode string) (ref string, resp *client.SolveResponse, err error) - Invoke(ctx context.Context, ref string, options controllerapi.ContainerConfig, ioIn io.ReadCloser, ioOut io.WriteCloser, ioErr io.WriteCloser) (err error) + // Invoke starts an IO session into the specified process. + // If pid doesn't matche to any running processes, it starts a new process with the specified config. + // If there is no container running or InvokeConfig.Rollback is speicfied, the process will start in a newly created container. + // NOTE: If needed, in the future, we can split this API into three APIs (NewContainer, NewProcess and Attach). + Invoke(ctx context.Context, ref, pid string, options controllerapi.InvokeConfig, ioIn io.ReadCloser, ioOut io.WriteCloser, ioErr io.WriteCloser) error Kill(ctx context.Context) error Close() error List(ctx context.Context) (refs []string, _ error) Disconnect(ctx context.Context, ref string) error + ListProcesses(ctx context.Context, ref string) (infos []*controllerapi.ProcessInfo, retErr error) + DisconnectProcess(ctx context.Context, ref, pid string) error } type ControlOptions struct { diff --git a/controller/local/controller.go b/controller/local/controller.go index 3ec8a177..4c462d7d 100644 --- a/controller/local/controller.go +++ b/controller/local/controller.go @@ -3,12 +3,15 @@ package local import ( "context" "io" + "sync/atomic" "github.com/containerd/console" "github.com/docker/buildx/build" cbuild "github.com/docker/buildx/controller/build" "github.com/docker/buildx/controller/control" controllerapi "github.com/docker/buildx/controller/pb" + "github.com/docker/buildx/controller/processes" + "github.com/docker/buildx/util/ioset" "github.com/docker/cli/cli/command" "github.com/moby/buildkit/client" "github.com/pkg/errors" @@ -18,6 +21,7 @@ func NewLocalBuildxController(ctx context.Context, dockerCli command.Cli) contro return &localController{ dockerCli: dockerCli, ref: "local", + processes: processes.NewManager(), } } @@ -25,9 +29,17 @@ type localController struct { dockerCli command.Cli ref string resultCtx *build.ResultContext + processes *processes.Manager + + buildOnGoing atomic.Bool } func (b *localController) Build(ctx context.Context, options controllerapi.BuildOptions, in io.ReadCloser, w io.Writer, out console.File, progressMode string) (string, *client.SolveResponse, error) { + if !b.buildOnGoing.CompareAndSwap(false, true) { + return "", nil, errors.New("build ongoing") + } + defer b.buildOnGoing.Store(false) + resp, res, err := cbuild.RunBuild(ctx, b.dockerCli, options, in, progressMode, nil) if err != nil { return "", nil, err @@ -36,38 +48,64 @@ func (b *localController) Build(ctx context.Context, options controllerapi.Build return b.ref, resp, nil } -func (b *localController) Invoke(ctx context.Context, ref string, cfg controllerapi.ContainerConfig, ioIn io.ReadCloser, ioOut io.WriteCloser, ioErr io.WriteCloser) error { +func (b *localController) ListProcesses(ctx context.Context, ref string) (infos []*controllerapi.ProcessInfo, retErr error) { + if ref != b.ref { + return nil, errors.Errorf("unknown ref %q", ref) + } + return b.processes.ListProcesses(), nil +} + +func (b *localController) DisconnectProcess(ctx context.Context, ref, pid string) error { if ref != b.ref { return errors.Errorf("unknown ref %q", ref) } - if b.resultCtx == nil { - return errors.New("no build result is registered") + return b.processes.DeleteProcess(pid) +} + +func (b *localController) cancelRunningProcesses() { + b.processes.CancelRunningProcesses() +} + +func (b *localController) Invoke(ctx context.Context, ref string, pid string, cfg controllerapi.InvokeConfig, ioIn io.ReadCloser, ioOut io.WriteCloser, ioErr io.WriteCloser) error { + if ref != b.ref { + return errors.Errorf("unknown ref %q", ref) } - ccfg := build.ContainerConfig{ - ResultCtx: b.resultCtx, - Entrypoint: cfg.Entrypoint, - Cmd: cfg.Cmd, - Env: cfg.Env, - Tty: cfg.Tty, - Stdin: ioIn, - Stdout: ioOut, - Stderr: ioErr, + + proc, ok := b.processes.Get(pid) + if !ok { + // Start a new process. + if b.resultCtx == nil { + return errors.New("no build result is registered") + } + var err error + proc, err = b.processes.StartProcess(pid, b.resultCtx, &cfg) + if err != nil { + return err + } } - if !cfg.NoUser { - ccfg.User = &cfg.User + + // Attach containerIn to this process + ioCancelledCh := make(chan struct{}) + proc.ForwardIO(&ioset.In{Stdin: ioIn, Stdout: ioOut, Stderr: ioErr}, func() { close(ioCancelledCh) }) + + select { + case <-ioCancelledCh: + return errors.Errorf("io cancelled") + case err := <-proc.Done(): + return err + case <-ctx.Done(): + return ctx.Err() } - if !cfg.NoCwd { - ccfg.Cwd = &cfg.Cwd - } - return build.Invoke(ctx, ccfg) } func (b *localController) Kill(context.Context) error { - return nil // nop + b.cancelRunningProcesses() + return nil } func (b *localController) Close() error { - // TODO: cancel current build and invoke + b.cancelRunningProcesses() + // TODO: cancel ongoing builds? return nil } @@ -76,5 +114,6 @@ func (b *localController) List(ctx context.Context) (res []string, _ error) { } func (b *localController) Disconnect(ctx context.Context, key string) error { - return nil // nop + b.cancelRunningProcesses() + return nil } diff --git a/controller/pb/controller.pb.go b/controller/pb/controller.pb.go index 55c0a737..ec675210 100644 --- a/controller/pb/controller.pb.go +++ b/controller/pb/controller.pb.go @@ -25,6 +25,204 @@ var _ = math.Inf // proto package needs to be updated. const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package +type ListProcessesRequest struct { + Ref string `protobuf:"bytes,1,opt,name=Ref,proto3" json:"Ref,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ListProcessesRequest) Reset() { *m = ListProcessesRequest{} } +func (m *ListProcessesRequest) String() string { return proto.CompactTextString(m) } +func (*ListProcessesRequest) ProtoMessage() {} +func (*ListProcessesRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_ed7f10298fa1d90f, []int{0} +} +func (m *ListProcessesRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ListProcessesRequest.Unmarshal(m, b) +} +func (m *ListProcessesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ListProcessesRequest.Marshal(b, m, deterministic) +} +func (m *ListProcessesRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListProcessesRequest.Merge(m, src) +} +func (m *ListProcessesRequest) XXX_Size() int { + return xxx_messageInfo_ListProcessesRequest.Size(m) +} +func (m *ListProcessesRequest) XXX_DiscardUnknown() { + xxx_messageInfo_ListProcessesRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_ListProcessesRequest proto.InternalMessageInfo + +func (m *ListProcessesRequest) GetRef() string { + if m != nil { + return m.Ref + } + return "" +} + +type ListProcessesResponse struct { + Infos []*ProcessInfo `protobuf:"bytes,1,rep,name=Infos,proto3" json:"Infos,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ListProcessesResponse) Reset() { *m = ListProcessesResponse{} } +func (m *ListProcessesResponse) String() string { return proto.CompactTextString(m) } +func (*ListProcessesResponse) ProtoMessage() {} +func (*ListProcessesResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_ed7f10298fa1d90f, []int{1} +} +func (m *ListProcessesResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ListProcessesResponse.Unmarshal(m, b) +} +func (m *ListProcessesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ListProcessesResponse.Marshal(b, m, deterministic) +} +func (m *ListProcessesResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListProcessesResponse.Merge(m, src) +} +func (m *ListProcessesResponse) XXX_Size() int { + return xxx_messageInfo_ListProcessesResponse.Size(m) +} +func (m *ListProcessesResponse) XXX_DiscardUnknown() { + xxx_messageInfo_ListProcessesResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_ListProcessesResponse proto.InternalMessageInfo + +func (m *ListProcessesResponse) GetInfos() []*ProcessInfo { + if m != nil { + return m.Infos + } + return nil +} + +type ProcessInfo struct { + ProcessID string `protobuf:"bytes,1,opt,name=ProcessID,proto3" json:"ProcessID,omitempty"` + InvokeConfig *InvokeConfig `protobuf:"bytes,2,opt,name=InvokeConfig,proto3" json:"InvokeConfig,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ProcessInfo) Reset() { *m = ProcessInfo{} } +func (m *ProcessInfo) String() string { return proto.CompactTextString(m) } +func (*ProcessInfo) ProtoMessage() {} +func (*ProcessInfo) Descriptor() ([]byte, []int) { + return fileDescriptor_ed7f10298fa1d90f, []int{2} +} +func (m *ProcessInfo) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ProcessInfo.Unmarshal(m, b) +} +func (m *ProcessInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ProcessInfo.Marshal(b, m, deterministic) +} +func (m *ProcessInfo) XXX_Merge(src proto.Message) { + xxx_messageInfo_ProcessInfo.Merge(m, src) +} +func (m *ProcessInfo) XXX_Size() int { + return xxx_messageInfo_ProcessInfo.Size(m) +} +func (m *ProcessInfo) XXX_DiscardUnknown() { + xxx_messageInfo_ProcessInfo.DiscardUnknown(m) +} + +var xxx_messageInfo_ProcessInfo proto.InternalMessageInfo + +func (m *ProcessInfo) GetProcessID() string { + if m != nil { + return m.ProcessID + } + return "" +} + +func (m *ProcessInfo) GetInvokeConfig() *InvokeConfig { + if m != nil { + return m.InvokeConfig + } + return nil +} + +type DisconnectProcessRequest struct { + Ref string `protobuf:"bytes,1,opt,name=Ref,proto3" json:"Ref,omitempty"` + ProcessID string `protobuf:"bytes,2,opt,name=ProcessID,proto3" json:"ProcessID,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *DisconnectProcessRequest) Reset() { *m = DisconnectProcessRequest{} } +func (m *DisconnectProcessRequest) String() string { return proto.CompactTextString(m) } +func (*DisconnectProcessRequest) ProtoMessage() {} +func (*DisconnectProcessRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_ed7f10298fa1d90f, []int{3} +} +func (m *DisconnectProcessRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_DisconnectProcessRequest.Unmarshal(m, b) +} +func (m *DisconnectProcessRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_DisconnectProcessRequest.Marshal(b, m, deterministic) +} +func (m *DisconnectProcessRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_DisconnectProcessRequest.Merge(m, src) +} +func (m *DisconnectProcessRequest) XXX_Size() int { + return xxx_messageInfo_DisconnectProcessRequest.Size(m) +} +func (m *DisconnectProcessRequest) XXX_DiscardUnknown() { + xxx_messageInfo_DisconnectProcessRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_DisconnectProcessRequest proto.InternalMessageInfo + +func (m *DisconnectProcessRequest) GetRef() string { + if m != nil { + return m.Ref + } + return "" +} + +func (m *DisconnectProcessRequest) GetProcessID() string { + if m != nil { + return m.ProcessID + } + return "" +} + +type DisconnectProcessResponse struct { + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *DisconnectProcessResponse) Reset() { *m = DisconnectProcessResponse{} } +func (m *DisconnectProcessResponse) String() string { return proto.CompactTextString(m) } +func (*DisconnectProcessResponse) ProtoMessage() {} +func (*DisconnectProcessResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_ed7f10298fa1d90f, []int{4} +} +func (m *DisconnectProcessResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_DisconnectProcessResponse.Unmarshal(m, b) +} +func (m *DisconnectProcessResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_DisconnectProcessResponse.Marshal(b, m, deterministic) +} +func (m *DisconnectProcessResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_DisconnectProcessResponse.Merge(m, src) +} +func (m *DisconnectProcessResponse) XXX_Size() int { + return xxx_messageInfo_DisconnectProcessResponse.Size(m) +} +func (m *DisconnectProcessResponse) XXX_DiscardUnknown() { + xxx_messageInfo_DisconnectProcessResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_DisconnectProcessResponse proto.InternalMessageInfo + type BuildRequest struct { Ref string `protobuf:"bytes,1,opt,name=Ref,proto3" json:"Ref,omitempty"` Options *BuildOptions `protobuf:"bytes,2,opt,name=Options,proto3" json:"Options,omitempty"` @@ -37,7 +235,7 @@ func (m *BuildRequest) Reset() { *m = BuildRequest{} } func (m *BuildRequest) String() string { return proto.CompactTextString(m) } func (*BuildRequest) ProtoMessage() {} func (*BuildRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_ed7f10298fa1d90f, []int{0} + return fileDescriptor_ed7f10298fa1d90f, []int{5} } func (m *BuildRequest) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_BuildRequest.Unmarshal(m, b) @@ -104,7 +302,7 @@ func (m *BuildOptions) Reset() { *m = BuildOptions{} } func (m *BuildOptions) String() string { return proto.CompactTextString(m) } func (*BuildOptions) ProtoMessage() {} func (*BuildOptions) Descriptor() ([]byte, []int) { - return fileDescriptor_ed7f10298fa1d90f, []int{1} + return fileDescriptor_ed7f10298fa1d90f, []int{6} } func (m *BuildOptions) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_BuildOptions.Unmarshal(m, b) @@ -298,7 +496,7 @@ func (m *ExportEntry) Reset() { *m = ExportEntry{} } func (m *ExportEntry) String() string { return proto.CompactTextString(m) } func (*ExportEntry) ProtoMessage() {} func (*ExportEntry) Descriptor() ([]byte, []int) { - return fileDescriptor_ed7f10298fa1d90f, []int{2} + return fileDescriptor_ed7f10298fa1d90f, []int{7} } func (m *ExportEntry) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_ExportEntry.Unmarshal(m, b) @@ -351,7 +549,7 @@ func (m *CacheOptionsEntry) Reset() { *m = CacheOptionsEntry{} } func (m *CacheOptionsEntry) String() string { return proto.CompactTextString(m) } func (*CacheOptionsEntry) ProtoMessage() {} func (*CacheOptionsEntry) Descriptor() ([]byte, []int) { - return fileDescriptor_ed7f10298fa1d90f, []int{3} + return fileDescriptor_ed7f10298fa1d90f, []int{8} } func (m *CacheOptionsEntry) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_CacheOptionsEntry.Unmarshal(m, b) @@ -398,7 +596,7 @@ func (m *Attest) Reset() { *m = Attest{} } func (m *Attest) String() string { return proto.CompactTextString(m) } func (*Attest) ProtoMessage() {} func (*Attest) Descriptor() ([]byte, []int) { - return fileDescriptor_ed7f10298fa1d90f, []int{4} + return fileDescriptor_ed7f10298fa1d90f, []int{9} } func (m *Attest) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_Attest.Unmarshal(m, b) @@ -451,7 +649,7 @@ func (m *SSH) Reset() { *m = SSH{} } func (m *SSH) String() string { return proto.CompactTextString(m) } func (*SSH) ProtoMessage() {} func (*SSH) Descriptor() ([]byte, []int) { - return fileDescriptor_ed7f10298fa1d90f, []int{5} + return fileDescriptor_ed7f10298fa1d90f, []int{10} } func (m *SSH) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_SSH.Unmarshal(m, b) @@ -498,7 +696,7 @@ func (m *Secret) Reset() { *m = Secret{} } func (m *Secret) String() string { return proto.CompactTextString(m) } func (*Secret) ProtoMessage() {} func (*Secret) Descriptor() ([]byte, []int) { - return fileDescriptor_ed7f10298fa1d90f, []int{6} + return fileDescriptor_ed7f10298fa1d90f, []int{11} } func (m *Secret) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_Secret.Unmarshal(m, b) @@ -550,7 +748,7 @@ func (m *UlimitOpt) Reset() { *m = UlimitOpt{} } func (m *UlimitOpt) String() string { return proto.CompactTextString(m) } func (*UlimitOpt) ProtoMessage() {} func (*UlimitOpt) Descriptor() ([]byte, []int) { - return fileDescriptor_ed7f10298fa1d90f, []int{7} + return fileDescriptor_ed7f10298fa1d90f, []int{12} } func (m *UlimitOpt) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_UlimitOpt.Unmarshal(m, b) @@ -590,7 +788,7 @@ func (m *Ulimit) Reset() { *m = Ulimit{} } func (m *Ulimit) String() string { return proto.CompactTextString(m) } func (*Ulimit) ProtoMessage() {} func (*Ulimit) Descriptor() ([]byte, []int) { - return fileDescriptor_ed7f10298fa1d90f, []int{8} + return fileDescriptor_ed7f10298fa1d90f, []int{13} } func (m *Ulimit) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_Ulimit.Unmarshal(m, b) @@ -648,7 +846,7 @@ func (m *CommonOptions) Reset() { *m = CommonOptions{} } func (m *CommonOptions) String() string { return proto.CompactTextString(m) } func (*CommonOptions) ProtoMessage() {} func (*CommonOptions) Descriptor() ([]byte, []int) { - return fileDescriptor_ed7f10298fa1d90f, []int{9} + return fileDescriptor_ed7f10298fa1d90f, []int{14} } func (m *CommonOptions) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_CommonOptions.Unmarshal(m, b) @@ -721,7 +919,7 @@ func (m *BuildResponse) Reset() { *m = BuildResponse{} } func (m *BuildResponse) String() string { return proto.CompactTextString(m) } func (*BuildResponse) ProtoMessage() {} func (*BuildResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_ed7f10298fa1d90f, []int{10} + return fileDescriptor_ed7f10298fa1d90f, []int{15} } func (m *BuildResponse) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_BuildResponse.Unmarshal(m, b) @@ -759,7 +957,7 @@ func (m *DisconnectRequest) Reset() { *m = DisconnectRequest{} } func (m *DisconnectRequest) String() string { return proto.CompactTextString(m) } func (*DisconnectRequest) ProtoMessage() {} func (*DisconnectRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_ed7f10298fa1d90f, []int{11} + return fileDescriptor_ed7f10298fa1d90f, []int{16} } func (m *DisconnectRequest) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_DisconnectRequest.Unmarshal(m, b) @@ -796,7 +994,7 @@ func (m *DisconnectResponse) Reset() { *m = DisconnectResponse{} } func (m *DisconnectResponse) String() string { return proto.CompactTextString(m) } func (*DisconnectResponse) ProtoMessage() {} func (*DisconnectResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_ed7f10298fa1d90f, []int{12} + return fileDescriptor_ed7f10298fa1d90f, []int{17} } func (m *DisconnectResponse) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_DisconnectResponse.Unmarshal(m, b) @@ -827,7 +1025,7 @@ func (m *ListRequest) Reset() { *m = ListRequest{} } func (m *ListRequest) String() string { return proto.CompactTextString(m) } func (*ListRequest) ProtoMessage() {} func (*ListRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_ed7f10298fa1d90f, []int{13} + return fileDescriptor_ed7f10298fa1d90f, []int{18} } func (m *ListRequest) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_ListRequest.Unmarshal(m, b) @@ -865,7 +1063,7 @@ func (m *ListResponse) Reset() { *m = ListResponse{} } func (m *ListResponse) String() string { return proto.CompactTextString(m) } func (*ListResponse) ProtoMessage() {} func (*ListResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_ed7f10298fa1d90f, []int{14} + return fileDescriptor_ed7f10298fa1d90f, []int{19} } func (m *ListResponse) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_ListResponse.Unmarshal(m, b) @@ -906,7 +1104,7 @@ func (m *InputMessage) Reset() { *m = InputMessage{} } func (m *InputMessage) String() string { return proto.CompactTextString(m) } func (*InputMessage) ProtoMessage() {} func (*InputMessage) Descriptor() ([]byte, []int) { - return fileDescriptor_ed7f10298fa1d90f, []int{15} + return fileDescriptor_ed7f10298fa1d90f, []int{20} } func (m *InputMessage) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_InputMessage.Unmarshal(m, b) @@ -980,7 +1178,7 @@ func (m *InputInitMessage) Reset() { *m = InputInitMessage{} } func (m *InputInitMessage) String() string { return proto.CompactTextString(m) } func (*InputInitMessage) ProtoMessage() {} func (*InputInitMessage) Descriptor() ([]byte, []int) { - return fileDescriptor_ed7f10298fa1d90f, []int{16} + return fileDescriptor_ed7f10298fa1d90f, []int{21} } func (m *InputInitMessage) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_InputInitMessage.Unmarshal(m, b) @@ -1019,7 +1217,7 @@ func (m *DataMessage) Reset() { *m = DataMessage{} } func (m *DataMessage) String() string { return proto.CompactTextString(m) } func (*DataMessage) ProtoMessage() {} func (*DataMessage) Descriptor() ([]byte, []int) { - return fileDescriptor_ed7f10298fa1d90f, []int{17} + return fileDescriptor_ed7f10298fa1d90f, []int{22} } func (m *DataMessage) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_DataMessage.Unmarshal(m, b) @@ -1063,7 +1261,7 @@ func (m *InputResponse) Reset() { *m = InputResponse{} } func (m *InputResponse) String() string { return proto.CompactTextString(m) } func (*InputResponse) ProtoMessage() {} func (*InputResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_ed7f10298fa1d90f, []int{18} + return fileDescriptor_ed7f10298fa1d90f, []int{23} } func (m *InputResponse) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_InputResponse.Unmarshal(m, b) @@ -1099,7 +1297,7 @@ func (m *Message) Reset() { *m = Message{} } func (m *Message) String() string { return proto.CompactTextString(m) } func (*Message) ProtoMessage() {} func (*Message) Descriptor() ([]byte, []int) { - return fileDescriptor_ed7f10298fa1d90f, []int{19} + return fileDescriptor_ed7f10298fa1d90f, []int{24} } func (m *Message) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_Message.Unmarshal(m, b) @@ -1187,18 +1385,21 @@ func (*Message) XXX_OneofWrappers() []interface{} { } type InitMessage struct { - Ref string `protobuf:"bytes,1,opt,name=Ref,proto3" json:"Ref,omitempty"` - ContainerConfig *ContainerConfig `protobuf:"bytes,2,opt,name=ContainerConfig,proto3" json:"ContainerConfig,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + Ref string `protobuf:"bytes,1,opt,name=Ref,proto3" json:"Ref,omitempty"` + // If ProcessID already exists in the server, it tries to connect to it + // instead of invoking the new one. In this case, InvokeConfig will be ignored. + ProcessID string `protobuf:"bytes,2,opt,name=ProcessID,proto3" json:"ProcessID,omitempty"` + InvokeConfig *InvokeConfig `protobuf:"bytes,3,opt,name=InvokeConfig,proto3" json:"InvokeConfig,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *InitMessage) Reset() { *m = InitMessage{} } func (m *InitMessage) String() string { return proto.CompactTextString(m) } func (*InitMessage) ProtoMessage() {} func (*InitMessage) Descriptor() ([]byte, []int) { - return fileDescriptor_ed7f10298fa1d90f, []int{20} + return fileDescriptor_ed7f10298fa1d90f, []int{25} } func (m *InitMessage) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_InitMessage.Unmarshal(m, b) @@ -1225,14 +1426,21 @@ func (m *InitMessage) GetRef() string { return "" } -func (m *InitMessage) GetContainerConfig() *ContainerConfig { +func (m *InitMessage) GetProcessID() string { if m != nil { - return m.ContainerConfig + return m.ProcessID + } + return "" +} + +func (m *InitMessage) GetInvokeConfig() *InvokeConfig { + if m != nil { + return m.InvokeConfig } return nil } -type ContainerConfig struct { +type InvokeConfig struct { Entrypoint []string `protobuf:"bytes,1,rep,name=Entrypoint,proto3" json:"Entrypoint,omitempty"` Cmd []string `protobuf:"bytes,2,rep,name=Cmd,proto3" json:"Cmd,omitempty"` Env []string `protobuf:"bytes,3,rep,name=Env,proto3" json:"Env,omitempty"` @@ -1241,91 +1449,99 @@ type ContainerConfig struct { Cwd string `protobuf:"bytes,6,opt,name=Cwd,proto3" json:"Cwd,omitempty"` NoCwd bool `protobuf:"varint,7,opt,name=NoCwd,proto3" json:"NoCwd,omitempty"` Tty bool `protobuf:"varint,8,opt,name=Tty,proto3" json:"Tty,omitempty"` + Rollback bool `protobuf:"varint,9,opt,name=Rollback,proto3" json:"Rollback,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } -func (m *ContainerConfig) Reset() { *m = ContainerConfig{} } -func (m *ContainerConfig) String() string { return proto.CompactTextString(m) } -func (*ContainerConfig) ProtoMessage() {} -func (*ContainerConfig) Descriptor() ([]byte, []int) { - return fileDescriptor_ed7f10298fa1d90f, []int{21} +func (m *InvokeConfig) Reset() { *m = InvokeConfig{} } +func (m *InvokeConfig) String() string { return proto.CompactTextString(m) } +func (*InvokeConfig) ProtoMessage() {} +func (*InvokeConfig) Descriptor() ([]byte, []int) { + return fileDescriptor_ed7f10298fa1d90f, []int{26} } -func (m *ContainerConfig) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_ContainerConfig.Unmarshal(m, b) +func (m *InvokeConfig) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_InvokeConfig.Unmarshal(m, b) } -func (m *ContainerConfig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_ContainerConfig.Marshal(b, m, deterministic) +func (m *InvokeConfig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_InvokeConfig.Marshal(b, m, deterministic) } -func (m *ContainerConfig) XXX_Merge(src proto.Message) { - xxx_messageInfo_ContainerConfig.Merge(m, src) +func (m *InvokeConfig) XXX_Merge(src proto.Message) { + xxx_messageInfo_InvokeConfig.Merge(m, src) } -func (m *ContainerConfig) XXX_Size() int { - return xxx_messageInfo_ContainerConfig.Size(m) +func (m *InvokeConfig) XXX_Size() int { + return xxx_messageInfo_InvokeConfig.Size(m) } -func (m *ContainerConfig) XXX_DiscardUnknown() { - xxx_messageInfo_ContainerConfig.DiscardUnknown(m) +func (m *InvokeConfig) XXX_DiscardUnknown() { + xxx_messageInfo_InvokeConfig.DiscardUnknown(m) } -var xxx_messageInfo_ContainerConfig proto.InternalMessageInfo +var xxx_messageInfo_InvokeConfig proto.InternalMessageInfo -func (m *ContainerConfig) GetEntrypoint() []string { +func (m *InvokeConfig) GetEntrypoint() []string { if m != nil { return m.Entrypoint } return nil } -func (m *ContainerConfig) GetCmd() []string { +func (m *InvokeConfig) GetCmd() []string { if m != nil { return m.Cmd } return nil } -func (m *ContainerConfig) GetEnv() []string { +func (m *InvokeConfig) GetEnv() []string { if m != nil { return m.Env } return nil } -func (m *ContainerConfig) GetUser() string { +func (m *InvokeConfig) GetUser() string { if m != nil { return m.User } return "" } -func (m *ContainerConfig) GetNoUser() bool { +func (m *InvokeConfig) GetNoUser() bool { if m != nil { return m.NoUser } return false } -func (m *ContainerConfig) GetCwd() string { +func (m *InvokeConfig) GetCwd() string { if m != nil { return m.Cwd } return "" } -func (m *ContainerConfig) GetNoCwd() bool { +func (m *InvokeConfig) GetNoCwd() bool { if m != nil { return m.NoCwd } return false } -func (m *ContainerConfig) GetTty() bool { +func (m *InvokeConfig) GetTty() bool { if m != nil { return m.Tty } return false } +func (m *InvokeConfig) GetRollback() bool { + if m != nil { + return m.Rollback + } + return false +} + type FdMessage struct { Fd uint32 `protobuf:"varint,1,opt,name=Fd,proto3" json:"Fd,omitempty"` EOF bool `protobuf:"varint,2,opt,name=EOF,proto3" json:"EOF,omitempty"` @@ -1339,7 +1555,7 @@ func (m *FdMessage) Reset() { *m = FdMessage{} } func (m *FdMessage) String() string { return proto.CompactTextString(m) } func (*FdMessage) ProtoMessage() {} func (*FdMessage) Descriptor() ([]byte, []int) { - return fileDescriptor_ed7f10298fa1d90f, []int{22} + return fileDescriptor_ed7f10298fa1d90f, []int{27} } func (m *FdMessage) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_FdMessage.Unmarshal(m, b) @@ -1392,7 +1608,7 @@ func (m *ResizeMessage) Reset() { *m = ResizeMessage{} } func (m *ResizeMessage) String() string { return proto.CompactTextString(m) } func (*ResizeMessage) ProtoMessage() {} func (*ResizeMessage) Descriptor() ([]byte, []int) { - return fileDescriptor_ed7f10298fa1d90f, []int{23} + return fileDescriptor_ed7f10298fa1d90f, []int{28} } func (m *ResizeMessage) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_ResizeMessage.Unmarshal(m, b) @@ -1439,7 +1655,7 @@ func (m *SignalMessage) Reset() { *m = SignalMessage{} } func (m *SignalMessage) String() string { return proto.CompactTextString(m) } func (*SignalMessage) ProtoMessage() {} func (*SignalMessage) Descriptor() ([]byte, []int) { - return fileDescriptor_ed7f10298fa1d90f, []int{24} + return fileDescriptor_ed7f10298fa1d90f, []int{29} } func (m *SignalMessage) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_SignalMessage.Unmarshal(m, b) @@ -1477,7 +1693,7 @@ func (m *StatusRequest) Reset() { *m = StatusRequest{} } func (m *StatusRequest) String() string { return proto.CompactTextString(m) } func (*StatusRequest) ProtoMessage() {} func (*StatusRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_ed7f10298fa1d90f, []int{25} + return fileDescriptor_ed7f10298fa1d90f, []int{30} } func (m *StatusRequest) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_StatusRequest.Unmarshal(m, b) @@ -1518,7 +1734,7 @@ func (m *StatusResponse) Reset() { *m = StatusResponse{} } func (m *StatusResponse) String() string { return proto.CompactTextString(m) } func (*StatusResponse) ProtoMessage() {} func (*StatusResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_ed7f10298fa1d90f, []int{26} + return fileDescriptor_ed7f10298fa1d90f, []int{31} } func (m *StatusResponse) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_StatusResponse.Unmarshal(m, b) @@ -1576,7 +1792,7 @@ func (m *InfoRequest) Reset() { *m = InfoRequest{} } func (m *InfoRequest) String() string { return proto.CompactTextString(m) } func (*InfoRequest) ProtoMessage() {} func (*InfoRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_ed7f10298fa1d90f, []int{27} + return fileDescriptor_ed7f10298fa1d90f, []int{32} } func (m *InfoRequest) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_InfoRequest.Unmarshal(m, b) @@ -1607,7 +1823,7 @@ func (m *InfoResponse) Reset() { *m = InfoResponse{} } func (m *InfoResponse) String() string { return proto.CompactTextString(m) } func (*InfoResponse) ProtoMessage() {} func (*InfoResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_ed7f10298fa1d90f, []int{28} + return fileDescriptor_ed7f10298fa1d90f, []int{33} } func (m *InfoResponse) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_InfoResponse.Unmarshal(m, b) @@ -1647,7 +1863,7 @@ func (m *BuildxVersion) Reset() { *m = BuildxVersion{} } func (m *BuildxVersion) String() string { return proto.CompactTextString(m) } func (*BuildxVersion) ProtoMessage() {} func (*BuildxVersion) Descriptor() ([]byte, []int) { - return fileDescriptor_ed7f10298fa1d90f, []int{29} + return fileDescriptor_ed7f10298fa1d90f, []int{34} } func (m *BuildxVersion) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_BuildxVersion.Unmarshal(m, b) @@ -1689,6 +1905,11 @@ func (m *BuildxVersion) GetRevision() string { } func init() { + proto.RegisterType((*ListProcessesRequest)(nil), "buildx.controller.v1.ListProcessesRequest") + proto.RegisterType((*ListProcessesResponse)(nil), "buildx.controller.v1.ListProcessesResponse") + proto.RegisterType((*ProcessInfo)(nil), "buildx.controller.v1.ProcessInfo") + proto.RegisterType((*DisconnectProcessRequest)(nil), "buildx.controller.v1.DisconnectProcessRequest") + proto.RegisterType((*DisconnectProcessResponse)(nil), "buildx.controller.v1.DisconnectProcessResponse") proto.RegisterType((*BuildRequest)(nil), "buildx.controller.v1.BuildRequest") proto.RegisterType((*BuildOptions)(nil), "buildx.controller.v1.BuildOptions") proto.RegisterMapType((map[string]string)(nil), "buildx.controller.v1.BuildOptions.BuildArgsEntry") @@ -1717,7 +1938,7 @@ func init() { proto.RegisterType((*InputResponse)(nil), "buildx.controller.v1.InputResponse") proto.RegisterType((*Message)(nil), "buildx.controller.v1.Message") proto.RegisterType((*InitMessage)(nil), "buildx.controller.v1.InitMessage") - proto.RegisterType((*ContainerConfig)(nil), "buildx.controller.v1.ContainerConfig") + proto.RegisterType((*InvokeConfig)(nil), "buildx.controller.v1.InvokeConfig") proto.RegisterType((*FdMessage)(nil), "buildx.controller.v1.FdMessage") proto.RegisterType((*ResizeMessage)(nil), "buildx.controller.v1.ResizeMessage") proto.RegisterType((*SignalMessage)(nil), "buildx.controller.v1.SignalMessage") @@ -1731,111 +1952,119 @@ func init() { func init() { proto.RegisterFile("controller.proto", fileDescriptor_ed7f10298fa1d90f) } var fileDescriptor_ed7f10298fa1d90f = []byte{ - // 1657 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x58, 0xdd, 0x6e, 0xdb, 0xc6, - 0x12, 0x3e, 0x94, 0x64, 0xfd, 0x8c, 0x2c, 0xc7, 0xd9, 0xe3, 0x04, 0x3c, 0x3a, 0x39, 0x89, 0xc3, - 0xfc, 0x1c, 0x01, 0x29, 0xe4, 0xc4, 0x69, 0x9a, 0xdf, 0x02, 0xb5, 0x25, 0x0b, 0x76, 0xe1, 0x3f, - 0x50, 0x4e, 0x82, 0xb6, 0x40, 0x03, 0x5a, 0x5a, 0xcb, 0x84, 0x28, 0xae, 0xca, 0x5d, 0xc9, 0x56, - 0xaf, 0x7a, 0xd3, 0xdb, 0xbe, 0x47, 0xd1, 0x47, 0xe8, 0x55, 0xdf, 0xa1, 0x0f, 0xd2, 0x47, 0x28, - 0x76, 0x76, 0x49, 0x51, 0x96, 0x28, 0xdb, 0xe8, 0x95, 0x77, 0x86, 0xdf, 0x37, 0xb3, 0x3b, 0x3b, - 0x3f, 0x2b, 0xc3, 0x72, 0x8b, 0xf9, 0x22, 0x60, 0x9e, 0x47, 0x83, 0x6a, 0x3f, 0x60, 0x82, 0x91, - 0x95, 0xe3, 0x81, 0xeb, 0xb5, 0xcf, 0xab, 0xb1, 0x0f, 0xc3, 0x67, 0xe5, 0xb7, 0x1d, 0x57, 0x9c, - 0x0e, 0x8e, 0xab, 0x2d, 0xd6, 0x5b, 0xeb, 0xb1, 0xe3, 0xd1, 0x1a, 0xa2, 0xba, 0xae, 0x58, 0x73, - 0xfa, 0xee, 0x1a, 0xa7, 0xc1, 0xd0, 0x6d, 0x51, 0xbe, 0xa6, 0x49, 0xe1, 0x5f, 0x65, 0xd2, 0xfa, - 0x1e, 0x16, 0x37, 0x25, 0xdc, 0xa6, 0x3f, 0x0c, 0x28, 0x17, 0x64, 0x19, 0xd2, 0x36, 0x3d, 0x31, - 0x8d, 0x55, 0xa3, 0x52, 0xb0, 0xe5, 0x92, 0xbc, 0x83, 0xdc, 0x41, 0x5f, 0xb8, 0xcc, 0xe7, 0x66, - 0x6a, 0xd5, 0xa8, 0x14, 0xd7, 0xad, 0xea, 0xac, 0x6d, 0x54, 0xd1, 0x8c, 0x46, 0xda, 0x21, 0xc5, - 0xfa, 0x19, 0xb4, 0x03, 0xad, 0x20, 0xab, 0x50, 0xac, 0x31, 0x5f, 0xd0, 0x73, 0x71, 0xe8, 0x88, - 0x53, 0xed, 0x28, 0xae, 0x22, 0x8f, 0x61, 0xa9, 0xce, 0x5a, 0x5d, 0x1a, 0x9c, 0xb8, 0x1e, 0xdd, - 0x77, 0x7a, 0x14, 0xfd, 0x16, 0xec, 0x0b, 0x5a, 0x72, 0x07, 0x0a, 0x87, 0x81, 0xeb, 0x8b, 0xc6, - 0xc0, 0x6f, 0x99, 0x69, 0x84, 0x8c, 0x15, 0xe4, 0x3b, 0x28, 0x49, 0x54, 0x5b, 0x5b, 0xe6, 0x66, - 0x66, 0x35, 0x5d, 0x29, 0xae, 0xbf, 0xb8, 0x7c, 0xf3, 0xd5, 0x09, 0xde, 0x96, 0x2f, 0x82, 0x91, - 0x3d, 0x69, 0x8b, 0xac, 0xc0, 0xc2, 0x86, 0xe7, 0xb1, 0x33, 0x73, 0x61, 0x35, 0x5d, 0x29, 0xd8, - 0x4a, 0x20, 0x5f, 0x40, 0x6e, 0x43, 0x08, 0xca, 0x05, 0x37, 0xb3, 0xe8, 0xec, 0xce, 0x6c, 0x67, - 0x0a, 0x64, 0x87, 0x60, 0x72, 0x00, 0x05, 0xf4, 0xbf, 0x11, 0x74, 0xb8, 0x99, 0x43, 0xe6, 0xb3, - 0x2b, 0x6c, 0x33, 0xe2, 0xa8, 0x2d, 0x8e, 0x6d, 0x90, 0x2d, 0x28, 0xd4, 0x9c, 0xd6, 0x29, 0x6d, - 0x04, 0xac, 0x67, 0xe6, 0xd1, 0xe0, 0xff, 0x67, 0x1b, 0x44, 0x98, 0x36, 0xa8, 0xcd, 0x44, 0x4c, - 0xb2, 0x01, 0x39, 0x14, 0x8e, 0x98, 0x59, 0xb8, 0x9e, 0x91, 0x90, 0x47, 0x2c, 0x58, 0xac, 0x75, - 0x02, 0x36, 0xe8, 0x1f, 0x3a, 0x01, 0xf5, 0x85, 0x09, 0x78, 0x4d, 0x13, 0x3a, 0xf2, 0x16, 0x72, - 0x5b, 0xe7, 0x7d, 0x16, 0x08, 0x6e, 0x16, 0xd1, 0xcd, 0xfd, 0xd9, 0x6e, 0x14, 0x48, 0x3b, 0xd0, - 0x0c, 0x72, 0x17, 0x60, 0xeb, 0x5c, 0x04, 0xce, 0x36, 0x93, 0x61, 0x5f, 0xc4, 0xeb, 0x88, 0x69, - 0x48, 0x03, 0xb2, 0xbb, 0xce, 0x31, 0xf5, 0xb8, 0x59, 0x42, 0xdb, 0xd5, 0x2b, 0x04, 0x56, 0x11, - 0x94, 0x23, 0xcd, 0x96, 0x69, 0xbb, 0x4f, 0xc5, 0x19, 0x0b, 0xba, 0x7b, 0xac, 0x4d, 0xcd, 0x25, - 0x95, 0xb6, 0x31, 0x15, 0x79, 0x08, 0xa5, 0x7d, 0xa6, 0x82, 0xe7, 0x7a, 0x82, 0x06, 0xe6, 0x0d, - 0xdc, 0xcc, 0xa4, 0x12, 0x93, 0xd6, 0x73, 0xc4, 0x09, 0x0b, 0x7a, 0xdc, 0x5c, 0x46, 0xc4, 0x58, - 0x21, 0x33, 0xa8, 0x49, 0x5b, 0x01, 0x15, 0xdc, 0xbc, 0x39, 0x2f, 0x83, 0x14, 0xc8, 0x0e, 0xc1, - 0xc4, 0x84, 0x5c, 0xf3, 0xb4, 0xd7, 0x74, 0x7f, 0xa4, 0x26, 0x59, 0x35, 0x2a, 0x69, 0x3b, 0x14, - 0xc9, 0x13, 0x48, 0x37, 0x9b, 0xdb, 0xe6, 0xbf, 0xd1, 0xda, 0x7f, 0x12, 0xac, 0x35, 0xb7, 0x6d, - 0x89, 0x22, 0x04, 0x32, 0x47, 0x4e, 0x87, 0x9b, 0x2b, 0xb8, 0x2f, 0x5c, 0x93, 0xdb, 0x90, 0x3d, - 0x72, 0x82, 0x0e, 0x15, 0xe6, 0x2d, 0x3c, 0xb3, 0x96, 0xc8, 0x6b, 0xc8, 0xbd, 0xf7, 0xdc, 0x9e, - 0x2b, 0xb8, 0x79, 0x1b, 0xdb, 0xc2, 0xbd, 0xd9, 0xc6, 0x15, 0xe8, 0xa0, 0x2f, 0xec, 0x10, 0x4f, - 0x5e, 0x42, 0xe6, 0xa0, 0x2f, 0xb8, 0x69, 0x22, 0xef, 0x41, 0x42, 0x52, 0xb1, 0x5e, 0x8f, 0xf9, - 0x61, 0x3f, 0x41, 0x42, 0xf9, 0x2b, 0x20, 0xd3, 0xb5, 0x29, 0x5b, 0x56, 0x97, 0x8e, 0xc2, 0x96, - 0xd5, 0xa5, 0x23, 0x59, 0x9e, 0x43, 0xc7, 0x1b, 0x84, 0x8d, 0x43, 0x09, 0x6f, 0x52, 0xaf, 0x8c, - 0xf2, 0x3b, 0x58, 0x9a, 0x2c, 0x9b, 0x6b, 0xb1, 0x5f, 0x43, 0x31, 0x96, 0x1b, 0xd7, 0xa1, 0x5a, - 0x7f, 0x18, 0x50, 0x8c, 0x25, 0x30, 0x86, 0x7a, 0xd4, 0xa7, 0x9a, 0x8c, 0x6b, 0xb2, 0x09, 0x0b, - 0x1b, 0x42, 0x04, 0xb2, 0xcf, 0xca, 0xdb, 0xfa, 0xec, 0xd2, 0x32, 0xa8, 0x22, 0x5c, 0x25, 0xaa, - 0xa2, 0xca, 0x3c, 0xad, 0x53, 0x2e, 0x5c, 0xdf, 0x91, 0x81, 0xd3, 0x6d, 0x31, 0xae, 0x2a, 0xbf, - 0x02, 0x18, 0xd3, 0xae, 0x75, 0x86, 0xdf, 0x0c, 0xb8, 0x39, 0x55, 0xeb, 0x33, 0x4f, 0xb2, 0x3d, - 0x79, 0x92, 0xf5, 0x2b, 0xf6, 0x8d, 0xe9, 0xf3, 0xfc, 0x83, 0xdd, 0xee, 0x43, 0x56, 0x35, 0xd8, - 0x99, 0x3b, 0x2c, 0x43, 0xbe, 0xee, 0x72, 0xe7, 0xd8, 0xa3, 0x6d, 0xa4, 0xe6, 0xed, 0x48, 0xc6, - 0xee, 0x8e, 0xbb, 0x57, 0xd1, 0x53, 0x82, 0xa5, 0x2a, 0x89, 0x2c, 0x41, 0x6a, 0xa7, 0xae, 0x4d, - 0xa5, 0x76, 0xea, 0x12, 0x2c, 0xa7, 0x96, 0x3a, 0x6a, 0xc1, 0x56, 0x82, 0xd5, 0x80, 0xac, 0xaa, - 0xcd, 0x29, 0x7c, 0x19, 0xf2, 0x0d, 0xd7, 0xa3, 0x38, 0xfc, 0xd4, 0x9e, 0x23, 0x59, 0x1e, 0x6f, - 0xcb, 0x1f, 0x6a, 0xb7, 0x72, 0x69, 0xfd, 0x6a, 0x40, 0x21, 0xaa, 0x20, 0x52, 0x83, 0x2c, 0x9e, - 0x8f, 0x9b, 0x06, 0xc6, 0xf5, 0xc9, 0x25, 0x25, 0x57, 0xfd, 0x80, 0x68, 0xdd, 0xc9, 0x14, 0xb5, - 0xfc, 0x11, 0x8a, 0x31, 0xf5, 0x8c, 0x90, 0xae, 0xc7, 0x43, 0x9a, 0xd8, 0x82, 0x94, 0x93, 0x78, - 0xc0, 0xeb, 0x90, 0x55, 0x4a, 0x19, 0x70, 0x9c, 0xdb, 0x3a, 0xe0, 0x38, 0xad, 0x09, 0x64, 0xb6, - 0x9d, 0x40, 0x05, 0x3b, 0x6d, 0xe3, 0x5a, 0xea, 0x9a, 0xec, 0x44, 0xe0, 0x81, 0xd3, 0x36, 0xae, - 0xad, 0xdf, 0x0d, 0x28, 0x4d, 0xd4, 0xbe, 0x6c, 0x6e, 0x58, 0xb3, 0x34, 0xd0, 0x06, 0x43, 0x51, - 0x4e, 0x97, 0x3d, 0x2a, 0x9c, 0xb6, 0x23, 0x1c, 0x19, 0x43, 0x1d, 0xcf, 0x09, 0x9d, 0x64, 0xeb, - 0x0e, 0x8c, 0x6e, 0xf2, 0x76, 0x28, 0x4a, 0xef, 0x87, 0x03, 0xcf, 0x33, 0x33, 0xa8, 0xc6, 0xb5, - 0x1a, 0x27, 0xb2, 0xbe, 0x0e, 0x07, 0xfc, 0xd4, 0x5c, 0xc0, 0x2f, 0x31, 0xcd, 0xf8, 0xfb, 0x2e, - 0x73, 0xda, 0x66, 0x36, 0xfe, 0x5d, 0x6a, 0x70, 0xf7, 0xfa, 0x3d, 0xc5, 0xfb, 0xcc, 0xe7, 0x94, - 0x50, 0x58, 0x56, 0xdf, 0x69, 0x10, 0xea, 0xf4, 0xed, 0xbd, 0x9e, 0x33, 0x8a, 0x42, 0x68, 0xf5, - 0x22, 0x57, 0xdd, 0xe5, 0x94, 0xc9, 0x72, 0x0d, 0x6e, 0xcd, 0x84, 0x5e, 0xab, 0x64, 0x1e, 0xc1, - 0xcd, 0xba, 0xcb, 0x5b, 0xcc, 0xf7, 0x69, 0x4b, 0x24, 0xbe, 0x08, 0xad, 0x15, 0x20, 0x71, 0x98, - 0xf2, 0x66, 0xdd, 0x83, 0xe2, 0xae, 0xcb, 0xe7, 0xd0, 0x2c, 0x58, 0x54, 0x00, 0x1d, 0x19, 0x02, - 0x99, 0x2e, 0x1d, 0xa9, 0x5c, 0x2e, 0xd8, 0xb8, 0xb6, 0x7e, 0x31, 0x60, 0x71, 0xc7, 0xef, 0x0f, - 0xc4, 0x1e, 0xe5, 0xdc, 0xe9, 0x50, 0xf2, 0x0e, 0x32, 0x3b, 0xbe, 0x2b, 0xd0, 0x4e, 0x71, 0xfd, - 0xf1, 0xec, 0x90, 0x21, 0x43, 0xc2, 0x34, 0x6b, 0xfb, 0x5f, 0x36, 0xb2, 0xe4, 0xa4, 0xa9, 0x3b, - 0xc2, 0xd1, 0x99, 0x9c, 0xf0, 0xae, 0x90, 0x88, 0x18, 0x51, 0x8a, 0x9b, 0x39, 0x58, 0x40, 0xa3, - 0xd6, 0x43, 0x58, 0xbe, 0x68, 0x7d, 0xc6, 0xd1, 0x9e, 0x43, 0x31, 0x66, 0x05, 0xeb, 0xf8, 0xa0, - 0x81, 0x80, 0xbc, 0x2d, 0x97, 0xf2, 0xac, 0xd1, 0x46, 0x16, 0x95, 0x0f, 0xeb, 0x06, 0x94, 0xd0, - 0x74, 0x14, 0xc1, 0x9f, 0x52, 0x90, 0x0b, 0x4d, 0xbc, 0x9c, 0x38, 0xf7, 0xfd, 0xa4, 0x73, 0x4f, - 0x1f, 0xf9, 0x05, 0x64, 0xa2, 0x5a, 0x48, 0x1c, 0xca, 0x8d, 0x76, 0x8c, 0x86, 0x65, 0xf2, 0x25, - 0x64, 0x6d, 0xca, 0xe5, 0x03, 0x22, 0x3d, 0x6f, 0x2a, 0x2b, 0xcc, 0x98, 0xac, 0x49, 0x92, 0xde, - 0x74, 0x3b, 0xbe, 0xa3, 0xaa, 0x29, 0x91, 0xae, 0x30, 0x31, 0xba, 0x52, 0x8c, 0xc3, 0xdd, 0x87, - 0xe2, 0xdc, 0x48, 0x93, 0x03, 0xb8, 0x21, 0xa7, 0xbf, 0xe3, 0xfa, 0x34, 0xa8, 0x31, 0xff, 0xc4, - 0xed, 0xe8, 0x93, 0x3e, 0x4a, 0x7a, 0x46, 0x4c, 0x80, 0xed, 0x8b, 0x6c, 0x59, 0xb1, 0x17, 0x75, - 0x58, 0xe5, 0xb2, 0x78, 0xfa, 0xcc, 0xf5, 0x85, 0xce, 0xcf, 0x98, 0x46, 0x6e, 0xab, 0xd6, 0x6b, - 0xeb, 0x8e, 0x2f, 0x97, 0xe3, 0xce, 0x9d, 0xd6, 0x9d, 0x5b, 0xde, 0xf8, 0x7b, 0x4e, 0x03, 0x8c, - 0x47, 0xc1, 0xc6, 0xb5, 0x7c, 0x4b, 0xed, 0x33, 0xd4, 0xaa, 0xce, 0xa2, 0x25, 0xb4, 0x77, 0xa6, - 0xda, 0x89, 0xb4, 0x77, 0x86, 0x23, 0x68, 0x9f, 0x49, 0x5d, 0x0e, 0x81, 0x4a, 0x90, 0xb8, 0x23, - 0x31, 0x32, 0xf3, 0x2a, 0xaf, 0x8e, 0xc4, 0xc8, 0xda, 0x80, 0x42, 0x74, 0x97, 0x72, 0xd4, 0x34, - 0xda, 0x18, 0xac, 0x92, 0x9d, 0x6a, 0xb4, 0xc3, 0x34, 0x4c, 0x4d, 0xa7, 0x61, 0x3a, 0x96, 0x86, - 0x2f, 0xa1, 0x34, 0x71, 0xab, 0x12, 0x64, 0xb3, 0x33, 0xae, 0x0d, 0xe1, 0x5a, 0xea, 0x6a, 0xcc, - 0x53, 0xbf, 0x00, 0x4b, 0x36, 0xae, 0xad, 0x07, 0x50, 0x9a, 0xb8, 0xcf, 0x59, 0x6d, 0xdf, 0xba, - 0x0f, 0xa5, 0xa6, 0x70, 0xc4, 0x80, 0x27, 0xf7, 0x85, 0xbf, 0x0c, 0x58, 0x0a, 0x31, 0xba, 0x35, - 0x7c, 0x0e, 0xf9, 0x21, 0x0d, 0x04, 0x3d, 0x8f, 0x46, 0x9d, 0x59, 0x95, 0x3f, 0x6d, 0xab, 0xe1, - 0x4f, 0x5b, 0x79, 0xb5, 0x1f, 0x10, 0x61, 0x47, 0x48, 0xf2, 0x06, 0xf2, 0x1c, 0xed, 0xd0, 0xf0, - 0xe1, 0x71, 0x37, 0x89, 0xa5, 0xfd, 0x45, 0x78, 0xb2, 0x06, 0x19, 0x8f, 0x75, 0x38, 0xde, 0x60, - 0x71, 0xfd, 0xbf, 0x49, 0xbc, 0x5d, 0xd6, 0xb1, 0x11, 0x48, 0xde, 0x42, 0xfe, 0xcc, 0x09, 0x7c, - 0xd7, 0xef, 0x84, 0x3f, 0x2d, 0xef, 0x25, 0x91, 0x3e, 0x2a, 0x9c, 0x1d, 0x11, 0xac, 0x92, 0x4c, - 0xf3, 0x13, 0xa6, 0x63, 0x62, 0x7d, 0x23, 0x9b, 0x9e, 0x14, 0xf5, 0xf1, 0x77, 0xa0, 0xa4, 0x92, - 0xf9, 0x03, 0x0d, 0xb8, 0x7c, 0xc6, 0x19, 0xf3, 0x8a, 0x6a, 0x33, 0x0e, 0xb5, 0x27, 0x99, 0xd6, - 0x27, 0x3d, 0x8f, 0x42, 0x85, 0x9c, 0x87, 0x7d, 0xa7, 0xd5, 0x75, 0x3a, 0xe1, 0x3d, 0x85, 0xa2, - 0xfc, 0x32, 0xd4, 0xfe, 0xd4, 0x64, 0x08, 0x45, 0xf9, 0x66, 0x09, 0xe8, 0xd0, 0xe5, 0xe3, 0x17, - 0x65, 0x24, 0xaf, 0xff, 0x99, 0x01, 0xa8, 0x45, 0xfb, 0x21, 0x87, 0xb0, 0x80, 0xfe, 0x88, 0x35, - 0x77, 0xba, 0xe1, 0xb9, 0xcb, 0x0f, 0xae, 0x30, 0x01, 0xc9, 0x7b, 0xc8, 0xaa, 0xdb, 0x22, 0x49, - 0x4d, 0x25, 0x9e, 0x5f, 0xe5, 0x87, 0xf3, 0x41, 0xca, 0xe8, 0x53, 0x83, 0xd8, 0xba, 0xe5, 0x24, - 0x6d, 0x34, 0x3e, 0x85, 0x92, 0x36, 0x3a, 0xd1, 0xbe, 0x2b, 0x06, 0xf9, 0x1a, 0xb2, 0x3b, 0xfe, - 0x90, 0x75, 0x29, 0xf9, 0xdf, 0x6c, 0x42, 0x68, 0x6f, 0xfe, 0xe7, 0x8a, 0xf1, 0xd4, 0x20, 0x7b, - 0x90, 0x91, 0xd3, 0x92, 0x24, 0xb4, 0xfe, 0xd8, 0xa8, 0x2d, 0x5b, 0xf3, 0x20, 0x3a, 0x8a, 0x9f, - 0x00, 0xc6, 0x33, 0x9b, 0x24, 0xfc, 0x90, 0x9f, 0x1a, 0xfe, 0xe5, 0xca, 0xe5, 0x40, 0xed, 0x60, - 0x4f, 0x0e, 0xac, 0x13, 0x46, 0x12, 0x47, 0x55, 0x94, 0xee, 0x65, 0x6b, 0x1e, 0x44, 0x99, 0xdb, - 0xcc, 0x7c, 0x9b, 0xea, 0x1f, 0x1f, 0x67, 0xf1, 0x9f, 0x54, 0xcf, 0xff, 0x0e, 0x00, 0x00, 0xff, - 0xff, 0xf4, 0x2e, 0xaa, 0xc4, 0x0b, 0x13, 0x00, 0x00, + // 1790 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x58, 0xcd, 0x6e, 0x23, 0xc7, + 0x11, 0xce, 0x90, 0x14, 0x7f, 0x8a, 0xa2, 0xac, 0xed, 0x68, 0x8d, 0x31, 0xed, 0x78, 0xb5, 0xb3, + 0x1b, 0x87, 0xc8, 0x06, 0x94, 0x2d, 0xc7, 0x59, 0xaf, 0x77, 0x03, 0x44, 0xa2, 0x44, 0x48, 0xc6, + 0xea, 0x07, 0x4d, 0xed, 0x1a, 0x49, 0x80, 0x18, 0x43, 0xb2, 0x45, 0x0d, 0x38, 0x9c, 0x66, 0xa6, + 0x9b, 0x94, 0x98, 0x53, 0x2e, 0xbe, 0xe6, 0x3d, 0x82, 0x5c, 0x73, 0xcb, 0x29, 0xef, 0x90, 0x07, + 0xc9, 0x23, 0x04, 0x5d, 0xdd, 0x33, 0x9c, 0x11, 0x39, 0x94, 0x14, 0x9f, 0xd8, 0x55, 0xf3, 0x55, + 0x55, 0x57, 0x75, 0xfd, 0x74, 0x13, 0x36, 0x7b, 0x3c, 0x90, 0x21, 0xf7, 0x7d, 0x16, 0x36, 0xc7, + 0x21, 0x97, 0x9c, 0x6c, 0x75, 0x27, 0x9e, 0xdf, 0xbf, 0x69, 0x26, 0x3e, 0x4c, 0xbf, 0xa8, 0xbf, + 0x1e, 0x78, 0xf2, 0x6a, 0xd2, 0x6d, 0xf6, 0xf8, 0x68, 0x67, 0xc4, 0xbb, 0xb3, 0x1d, 0x44, 0x0d, + 0x3d, 0xb9, 0xe3, 0x8e, 0xbd, 0x1d, 0xc1, 0xc2, 0xa9, 0xd7, 0x63, 0x62, 0xc7, 0x08, 0x45, 0xbf, + 0x5a, 0xa5, 0xd3, 0x80, 0xad, 0xb7, 0x9e, 0x90, 0xe7, 0x21, 0xef, 0x31, 0x21, 0x98, 0xa0, 0xec, + 0xcf, 0x13, 0x26, 0x24, 0xd9, 0x84, 0x3c, 0x65, 0x97, 0xb6, 0xb5, 0x6d, 0x35, 0x2a, 0x54, 0x2d, + 0x9d, 0x73, 0x78, 0x7c, 0x0b, 0x29, 0xc6, 0x3c, 0x10, 0x8c, 0xbc, 0x84, 0xb5, 0xe3, 0xe0, 0x92, + 0x0b, 0xdb, 0xda, 0xce, 0x37, 0xaa, 0xbb, 0x4f, 0x9b, 0xcb, 0x76, 0xd9, 0x34, 0x72, 0x0a, 0x49, + 0x35, 0xde, 0x11, 0x50, 0x4d, 0x70, 0xc9, 0x27, 0x50, 0x89, 0xc8, 0x03, 0x63, 0x78, 0xce, 0x20, + 0x6d, 0x58, 0x3f, 0x0e, 0xa6, 0x7c, 0xc8, 0x5a, 0x3c, 0xb8, 0xf4, 0x06, 0x76, 0x6e, 0xdb, 0x6a, + 0x54, 0x77, 0x9d, 0xe5, 0xc6, 0x92, 0x48, 0x9a, 0x92, 0x73, 0xbe, 0x05, 0xfb, 0xc0, 0x13, 0x3d, + 0x1e, 0x04, 0xac, 0x17, 0x39, 0x93, 0xe9, 0x74, 0x7a, 0x4f, 0xb9, 0x5b, 0x7b, 0x72, 0x3e, 0x86, + 0x8f, 0x96, 0xe8, 0xd2, 0x61, 0x71, 0xfe, 0x04, 0xeb, 0xfb, 0x6a, 0x6f, 0xd9, 0xca, 0xdf, 0x40, + 0xe9, 0x6c, 0x2c, 0x3d, 0x1e, 0x88, 0xd5, 0xde, 0xa0, 0x1a, 0x83, 0xa4, 0x91, 0x88, 0xf3, 0x03, + 0x18, 0x03, 0x86, 0x41, 0xb6, 0xa1, 0xda, 0xe2, 0x81, 0x64, 0x37, 0xf2, 0xdc, 0x95, 0x57, 0xc6, + 0x50, 0x92, 0x45, 0x3e, 0x83, 0x8d, 0x03, 0xde, 0x1b, 0xb2, 0xf0, 0xd2, 0xf3, 0xd9, 0xa9, 0x3b, + 0x62, 0xc6, 0xa5, 0x5b, 0x5c, 0xed, 0xb5, 0x17, 0xc8, 0xf6, 0x24, 0xe8, 0xd9, 0xf9, 0xc8, 0x6b, + 0xc3, 0x20, 0x7f, 0x84, 0x9a, 0x42, 0xf5, 0x8d, 0x66, 0x61, 0x17, 0xf0, 0xdc, 0xbf, 0xba, 0x7b, + 0xf3, 0xcd, 0x94, 0xdc, 0x61, 0x20, 0xc3, 0x19, 0x4d, 0xeb, 0x22, 0x5b, 0xb0, 0xb6, 0xe7, 0xfb, + 0xfc, 0xda, 0x5e, 0xdb, 0xce, 0x37, 0x2a, 0x54, 0x13, 0xe4, 0x37, 0x50, 0xda, 0x93, 0x92, 0x09, + 0x29, 0xec, 0x22, 0x1a, 0xfb, 0x64, 0xb9, 0x31, 0x0d, 0xa2, 0x11, 0x98, 0x9c, 0x41, 0x05, 0xed, + 0xef, 0x85, 0x03, 0x61, 0x97, 0x50, 0xf2, 0x8b, 0x7b, 0x6c, 0x33, 0x96, 0xd1, 0x5b, 0x9c, 0xeb, + 0x20, 0x87, 0x50, 0x69, 0xb9, 0xbd, 0x2b, 0xd6, 0x0e, 0xf9, 0xc8, 0x2e, 0xa3, 0xc2, 0x5f, 0x2c, + 0x57, 0x88, 0x30, 0xa3, 0xd0, 0xa8, 0x89, 0x25, 0xc9, 0x1e, 0x94, 0x90, 0xb8, 0xe0, 0x76, 0xe5, + 0x61, 0x4a, 0x22, 0x39, 0xe2, 0xc0, 0x7a, 0x6b, 0x10, 0xf2, 0xc9, 0xf8, 0xdc, 0x0d, 0x59, 0x20, + 0x6d, 0xc0, 0x63, 0x4a, 0xf1, 0xc8, 0x6b, 0x28, 0x1d, 0xde, 0x8c, 0x79, 0x28, 0x85, 0x5d, 0x5d, + 0x55, 0x9b, 0x1a, 0x64, 0x0c, 0x18, 0x09, 0xf2, 0x29, 0xc0, 0xe1, 0x8d, 0x0c, 0xdd, 0x23, 0xae, + 0xc2, 0xbe, 0x8e, 0xc7, 0x91, 0xe0, 0x90, 0x36, 0x14, 0xdf, 0xba, 0x5d, 0xe6, 0x0b, 0xbb, 0x86, + 0xba, 0x9b, 0xf7, 0x08, 0xac, 0x16, 0xd0, 0x86, 0x8c, 0xb4, 0x4a, 0xdb, 0x53, 0x26, 0xaf, 0x79, + 0x38, 0x3c, 0xe1, 0x7d, 0x66, 0x6f, 0xe8, 0xb4, 0x4d, 0xb0, 0xc8, 0x73, 0xa8, 0x9d, 0x72, 0x1d, + 0x3c, 0xcf, 0x97, 0x2c, 0xb4, 0x3f, 0xc0, 0xcd, 0xa4, 0x99, 0x98, 0xb4, 0xbe, 0x2b, 0x2f, 0x79, + 0x38, 0x12, 0xf6, 0x26, 0x22, 0xe6, 0x0c, 0x95, 0x41, 0x1d, 0xd6, 0x0b, 0x99, 0x14, 0xf6, 0xa3, + 0x55, 0x19, 0xa4, 0x41, 0x34, 0x02, 0x13, 0x1b, 0x4a, 0x9d, 0xab, 0x51, 0xc7, 0xfb, 0x0b, 0xb3, + 0xc9, 0xb6, 0xd5, 0xc8, 0xd3, 0x88, 0x24, 0x2f, 0x20, 0xdf, 0xe9, 0x1c, 0xd9, 0x3f, 0x45, 0x6d, + 0x1f, 0x65, 0x68, 0xeb, 0x1c, 0x51, 0x85, 0x22, 0x04, 0x0a, 0x17, 0xee, 0x40, 0xd8, 0x5b, 0xb8, + 0x2f, 0x5c, 0x93, 0x0f, 0xa1, 0x78, 0xe1, 0x86, 0x03, 0x26, 0xed, 0xc7, 0xe8, 0xb3, 0xa1, 0xc8, + 0x2b, 0x28, 0xbd, 0xf3, 0xbd, 0x91, 0x27, 0x85, 0xfd, 0x21, 0xb6, 0x85, 0x27, 0xcb, 0x95, 0x6b, + 0xd0, 0xd9, 0x58, 0xd2, 0x08, 0x4f, 0x5e, 0x42, 0xe1, 0x6c, 0x2c, 0x85, 0x6d, 0xa3, 0xdc, 0xb3, + 0x8c, 0xa4, 0xe2, 0xa3, 0x11, 0x0f, 0xa2, 0x7e, 0x82, 0x02, 0xf5, 0xdf, 0x01, 0x59, 0xac, 0x4d, + 0xd5, 0xb2, 0x86, 0x6c, 0x16, 0xb5, 0xac, 0x21, 0x9b, 0xa9, 0xf2, 0x9c, 0xba, 0xfe, 0x24, 0x6a, + 0x1c, 0x9a, 0xf8, 0x26, 0xf7, 0xb5, 0x55, 0x7f, 0x03, 0x1b, 0xe9, 0xb2, 0x79, 0x90, 0xf4, 0x2b, + 0xa8, 0x26, 0x72, 0xe3, 0x21, 0xa2, 0xce, 0xbf, 0x2d, 0xa8, 0x26, 0x12, 0x18, 0x43, 0x3d, 0x1b, + 0x33, 0x23, 0x8c, 0x6b, 0xb2, 0x0f, 0x6b, 0x7b, 0x52, 0x86, 0xaa, 0xcf, 0xaa, 0xd3, 0xfa, 0xd5, + 0x9d, 0x65, 0xd0, 0x44, 0xb8, 0x4e, 0x54, 0x2d, 0xaa, 0xf2, 0xf4, 0x80, 0x09, 0xe9, 0x05, 0xae, + 0x0a, 0x9c, 0x69, 0x8b, 0x49, 0x56, 0xfd, 0x6b, 0x80, 0xb9, 0xd8, 0x83, 0x7c, 0xf8, 0x87, 0x05, + 0x8f, 0x16, 0x6a, 0x7d, 0xa9, 0x27, 0x47, 0x69, 0x4f, 0x76, 0xef, 0xd9, 0x37, 0x16, 0xfd, 0xf9, + 0x11, 0xbb, 0x3d, 0x85, 0xa2, 0x6e, 0xb0, 0x4b, 0x77, 0x58, 0x87, 0xf2, 0x81, 0x27, 0xdc, 0xae, + 0xcf, 0xfa, 0x28, 0x5a, 0xa6, 0x31, 0x8d, 0xdd, 0x1d, 0x77, 0xaf, 0xa3, 0xa7, 0x09, 0x47, 0x57, + 0x12, 0xd9, 0x80, 0x5c, 0x3c, 0xf8, 0x73, 0xc7, 0x07, 0x0a, 0xac, 0xa6, 0x96, 0x76, 0xb5, 0x42, + 0x35, 0xe1, 0xb4, 0xa1, 0xa8, 0x6b, 0x73, 0x01, 0x5f, 0x87, 0x72, 0xdb, 0xf3, 0x19, 0x0e, 0x3f, + 0xbd, 0xe7, 0x98, 0x56, 0xee, 0x1d, 0x06, 0x53, 0x63, 0x56, 0x2d, 0x9d, 0xbf, 0x5b, 0x50, 0x89, + 0x2b, 0x88, 0xb4, 0xa0, 0x88, 0xfe, 0x45, 0x97, 0x98, 0x17, 0x77, 0x94, 0x5c, 0xf3, 0x3d, 0xa2, + 0x4d, 0x27, 0xd3, 0xa2, 0xf5, 0xef, 0xa0, 0x9a, 0x60, 0x2f, 0x09, 0xe9, 0x6e, 0x32, 0xa4, 0x99, + 0x2d, 0x48, 0x1b, 0x49, 0x06, 0xfc, 0x00, 0x8a, 0x9a, 0xa9, 0x02, 0x8e, 0x73, 0xdb, 0x04, 0x1c, + 0xa7, 0x35, 0x81, 0xc2, 0x91, 0x1b, 0xea, 0x60, 0xe7, 0x29, 0xae, 0x15, 0xaf, 0xc3, 0x2f, 0x25, + 0x3a, 0x9c, 0xa7, 0xb8, 0x76, 0xfe, 0x65, 0x41, 0x2d, 0x55, 0xfb, 0xaa, 0xb9, 0x61, 0xcd, 0xb2, + 0xd0, 0x28, 0x8c, 0x48, 0x35, 0x5d, 0x4e, 0x98, 0x74, 0xfb, 0xae, 0x74, 0x55, 0x0c, 0x4d, 0x3c, + 0x53, 0x3c, 0x25, 0x6d, 0x3a, 0x30, 0x9a, 0x29, 0xd3, 0x88, 0x54, 0xd6, 0xcf, 0x27, 0xbe, 0x6f, + 0x17, 0x90, 0x8d, 0x6b, 0x3d, 0x4e, 0x54, 0x7d, 0x9d, 0x4f, 0xc4, 0x95, 0xbd, 0x86, 0x5f, 0x12, + 0x9c, 0xf9, 0xf7, 0xb7, 0xdc, 0xed, 0xdb, 0xc5, 0xe4, 0x77, 0xc5, 0xc1, 0xdd, 0x9b, 0xfb, 0x94, + 0xb9, 0x77, 0x32, 0xd8, 0xd4, 0xdf, 0x59, 0x18, 0xf1, 0xcc, 0xe9, 0xbd, 0x5a, 0x31, 0x8a, 0x22, + 0x68, 0xf3, 0xb6, 0xac, 0x3e, 0xcb, 0x05, 0x95, 0xf5, 0x16, 0x3c, 0x5e, 0x0a, 0x7d, 0x50, 0xc9, + 0xfc, 0x1c, 0x1e, 0xcd, 0x6f, 0x8a, 0xd9, 0x77, 0xec, 0x2d, 0x20, 0x49, 0x98, 0xb9, 0x49, 0x3e, + 0x81, 0xaa, 0xba, 0x79, 0x67, 0x8b, 0x39, 0xb0, 0xae, 0x01, 0x26, 0x32, 0x04, 0x0a, 0x43, 0x36, + 0xd3, 0xb9, 0x5c, 0xa1, 0xb8, 0x76, 0xfe, 0x66, 0xa9, 0x0b, 0xf4, 0x78, 0x22, 0x4f, 0x98, 0x10, + 0xee, 0x80, 0x91, 0x37, 0x50, 0x38, 0x0e, 0x3c, 0x89, 0x7a, 0xaa, 0xbb, 0x9f, 0x65, 0x5d, 0xa4, + 0xc7, 0x13, 0xa9, 0x60, 0x46, 0xea, 0xe8, 0x27, 0x14, 0xa5, 0xd4, 0xa4, 0x39, 0x70, 0xa5, 0x6b, + 0x32, 0x39, 0xe3, 0x5e, 0xa1, 0x10, 0x09, 0x41, 0x45, 0xee, 0x97, 0xd4, 0x6b, 0x61, 0x3c, 0x91, + 0xce, 0x73, 0xd8, 0xbc, 0xad, 0x7d, 0x89, 0x6b, 0x5f, 0x42, 0x35, 0xa1, 0x05, 0xeb, 0xf8, 0xac, + 0x8d, 0x80, 0x32, 0x55, 0x4b, 0xe5, 0x6b, 0xbc, 0x91, 0x75, 0x6d, 0xc3, 0xf9, 0x00, 0x6a, 0xa8, + 0x3a, 0x8e, 0xe0, 0x5f, 0x73, 0x50, 0x8a, 0x54, 0xbc, 0x4c, 0xf9, 0xfd, 0x34, 0xcb, 0xef, 0x45, + 0x97, 0xbf, 0x82, 0x42, 0x5c, 0x0b, 0x99, 0x43, 0xb9, 0xdd, 0x4f, 0x88, 0x61, 0x99, 0xfc, 0x16, + 0x8a, 0x94, 0x09, 0x75, 0x81, 0xc8, 0xaf, 0x9a, 0xca, 0x1a, 0x33, 0x17, 0x36, 0x42, 0x4a, 0xbc, + 0xe3, 0x0d, 0x02, 0x57, 0x57, 0x53, 0xa6, 0xb8, 0xc6, 0x24, 0xc4, 0x35, 0x63, 0x1e, 0xee, 0x1f, + 0x2c, 0xa8, 0xae, 0x0c, 0xf5, 0xea, 0xb7, 0xce, 0xc2, 0xfb, 0x2b, 0xff, 0x7f, 0xbe, 0xbf, 0xfe, + 0x63, 0xa5, 0x15, 0x61, 0xe1, 0xab, 0x7a, 0x1a, 0x73, 0x2f, 0x90, 0x26, 0x65, 0x13, 0x1c, 0xb5, + 0xd1, 0xd6, 0xa8, 0x6f, 0x86, 0x80, 0x5a, 0xce, 0x9b, 0x79, 0xde, 0x34, 0x73, 0x95, 0x04, 0xef, + 0x04, 0x0b, 0x31, 0x44, 0x15, 0x8a, 0x6b, 0x75, 0xbd, 0x3a, 0xe5, 0xc8, 0xd5, 0xcd, 0xc6, 0x50, + 0xa8, 0xef, 0x5a, 0x77, 0x18, 0xa5, 0xef, 0x1a, 0xa7, 0xd2, 0x29, 0x57, 0xbc, 0x12, 0x02, 0x35, + 0xa1, 0x70, 0x17, 0x72, 0x66, 0x97, 0x75, 0xaa, 0x5d, 0xc8, 0x99, 0x1a, 0x30, 0x94, 0xfb, 0x7e, + 0xd7, 0xed, 0x0d, 0xed, 0x8a, 0x9e, 0x6c, 0x11, 0xed, 0xec, 0x41, 0x25, 0x3e, 0x7a, 0x35, 0x99, + 0xda, 0x7d, 0x0c, 0x6d, 0x8d, 0xe6, 0xda, 0xfd, 0x28, 0x6b, 0x73, 0x8b, 0x59, 0x9b, 0x4f, 0x64, + 0xed, 0x4b, 0xa8, 0xa5, 0x92, 0x40, 0x81, 0x28, 0xbf, 0x16, 0x46, 0x11, 0xae, 0x15, 0xaf, 0xc5, + 0x7d, 0xfd, 0x60, 0xac, 0x51, 0x5c, 0x3b, 0xcf, 0xa0, 0x96, 0x3a, 0xfe, 0x65, 0x53, 0xc2, 0x79, + 0x0a, 0xb5, 0x8e, 0x74, 0xe5, 0x64, 0xc5, 0x0b, 0xff, 0xbf, 0x16, 0x6c, 0x44, 0x18, 0xd3, 0x49, + 0x7e, 0x0d, 0xe5, 0x29, 0x0b, 0x25, 0xbb, 0x89, 0x27, 0xa3, 0xdd, 0x1c, 0xf1, 0xee, 0xac, 0x19, + 0xfd, 0xc7, 0xa0, 0x4e, 0xfb, 0x3d, 0x22, 0x68, 0x8c, 0x24, 0xdf, 0x40, 0x59, 0xa0, 0x1e, 0x16, + 0xdd, 0x53, 0x3e, 0xcd, 0x92, 0x32, 0xf6, 0x62, 0x3c, 0xd9, 0x81, 0x82, 0xcf, 0x07, 0x02, 0x4f, + 0xb7, 0xba, 0xfb, 0x71, 0x96, 0xdc, 0x5b, 0x3e, 0xa0, 0x08, 0x24, 0xaf, 0xa1, 0x7c, 0xed, 0x86, + 0x81, 0x17, 0x0c, 0xa2, 0x97, 0xe8, 0x93, 0x2c, 0xa1, 0xef, 0x34, 0x8e, 0xc6, 0x02, 0x4e, 0x4d, + 0x15, 0xc5, 0x25, 0x37, 0x31, 0x71, 0x7e, 0xaf, 0x72, 0x53, 0x91, 0xc6, 0xfd, 0x63, 0xa8, 0xe9, + 0xfc, 0x7e, 0xcf, 0x42, 0xa1, 0x6e, 0x7d, 0xd6, 0xaa, 0x1a, 0xdc, 0x4f, 0x42, 0x69, 0x5a, 0xd2, + 0xf9, 0xde, 0x8c, 0xaf, 0x88, 0xa1, 0xc6, 0xe7, 0xd8, 0xed, 0x0d, 0xdd, 0x41, 0x74, 0x4e, 0x11, + 0xa9, 0xbe, 0x4c, 0x8d, 0x3d, 0x5d, 0x86, 0x11, 0xa9, 0x32, 0x30, 0x64, 0x53, 0x4f, 0xcc, 0x2f, + 0xa0, 0x31, 0xbd, 0xfb, 0xcf, 0x22, 0x40, 0x2b, 0xde, 0x0f, 0x39, 0x87, 0x35, 0xb4, 0x47, 0x9c, + 0x95, 0xc3, 0x10, 0xfd, 0xae, 0x3f, 0xbb, 0xc7, 0xc0, 0x24, 0xef, 0xa0, 0xa8, 0x4f, 0x8b, 0x64, + 0xf5, 0xa0, 0x64, 0x7e, 0xd5, 0x9f, 0xaf, 0x06, 0x69, 0xa5, 0x9f, 0x5b, 0x84, 0x9a, 0x0e, 0x45, + 0x9c, 0x15, 0x23, 0xc8, 0x64, 0x76, 0xd6, 0x46, 0x53, 0xdd, 0xbe, 0x61, 0x91, 0x6f, 0xa1, 0xa8, + 0x7b, 0x0c, 0xf9, 0xd9, 0x72, 0x81, 0x48, 0xdf, 0xea, 0xcf, 0x0d, 0xeb, 0x73, 0x8b, 0x9c, 0x40, + 0x41, 0x0d, 0x57, 0x92, 0x31, 0x29, 0x12, 0x93, 0xb9, 0xee, 0xac, 0x82, 0x98, 0x28, 0x7e, 0x0f, + 0x30, 0x1f, 0xf1, 0x24, 0xe3, 0xdd, 0xbf, 0x70, 0x57, 0xa8, 0x37, 0xee, 0x06, 0x1a, 0x03, 0x27, + 0x6a, 0xbe, 0x5d, 0x72, 0x92, 0x39, 0xd9, 0xe2, 0x74, 0xaf, 0x3b, 0xab, 0x20, 0x46, 0xdd, 0x15, + 0xd4, 0x52, 0x7f, 0xfb, 0x91, 0x5f, 0x66, 0x3b, 0x79, 0xfb, 0x5f, 0xc4, 0xfa, 0x8b, 0x7b, 0x61, + 0x8d, 0x25, 0x99, 0xbc, 0x23, 0x99, 0xcf, 0xa4, 0x79, 0x97, 0xdf, 0xe9, 0xbf, 0xf0, 0xea, 0x3b, + 0xf7, 0xc6, 0x6b, 0xab, 0xfb, 0x85, 0x3f, 0xe4, 0xc6, 0xdd, 0x6e, 0x11, 0xff, 0x0d, 0xfd, 0xf2, + 0x7f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x41, 0x5d, 0x93, 0xf2, 0x74, 0x15, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -1857,6 +2086,8 @@ type ControllerClient interface { List(ctx context.Context, in *ListRequest, opts ...grpc.CallOption) (*ListResponse, error) Disconnect(ctx context.Context, in *DisconnectRequest, opts ...grpc.CallOption) (*DisconnectResponse, error) Info(ctx context.Context, in *InfoRequest, opts ...grpc.CallOption) (*InfoResponse, error) + ListProcesses(ctx context.Context, in *ListProcessesRequest, opts ...grpc.CallOption) (*ListProcessesResponse, error) + DisconnectProcess(ctx context.Context, in *DisconnectProcessRequest, opts ...grpc.CallOption) (*DisconnectProcessResponse, error) } type controllerClient struct { @@ -2000,6 +2231,24 @@ func (c *controllerClient) Info(ctx context.Context, in *InfoRequest, opts ...gr return out, nil } +func (c *controllerClient) ListProcesses(ctx context.Context, in *ListProcessesRequest, opts ...grpc.CallOption) (*ListProcessesResponse, error) { + out := new(ListProcessesResponse) + err := c.cc.Invoke(ctx, "/buildx.controller.v1.Controller/ListProcesses", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *controllerClient) DisconnectProcess(ctx context.Context, in *DisconnectProcessRequest, opts ...grpc.CallOption) (*DisconnectProcessResponse, error) { + out := new(DisconnectProcessResponse) + err := c.cc.Invoke(ctx, "/buildx.controller.v1.Controller/DisconnectProcess", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // ControllerServer is the server API for Controller service. type ControllerServer interface { Build(context.Context, *BuildRequest) (*BuildResponse, error) @@ -2009,6 +2258,8 @@ type ControllerServer interface { List(context.Context, *ListRequest) (*ListResponse, error) Disconnect(context.Context, *DisconnectRequest) (*DisconnectResponse, error) Info(context.Context, *InfoRequest) (*InfoResponse, error) + ListProcesses(context.Context, *ListProcessesRequest) (*ListProcessesResponse, error) + DisconnectProcess(context.Context, *DisconnectProcessRequest) (*DisconnectProcessResponse, error) } // UnimplementedControllerServer can be embedded to have forward compatible implementations. @@ -2036,6 +2287,12 @@ func (*UnimplementedControllerServer) Disconnect(ctx context.Context, req *Disco func (*UnimplementedControllerServer) Info(ctx context.Context, req *InfoRequest) (*InfoResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method Info not implemented") } +func (*UnimplementedControllerServer) ListProcesses(ctx context.Context, req *ListProcessesRequest) (*ListProcessesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListProcesses not implemented") +} +func (*UnimplementedControllerServer) DisconnectProcess(ctx context.Context, req *DisconnectProcessRequest) (*DisconnectProcessResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DisconnectProcess not implemented") +} func RegisterControllerServer(s *grpc.Server, srv ControllerServer) { s.RegisterService(&_Controller_serviceDesc, srv) @@ -2186,6 +2443,42 @@ func _Controller_Info_Handler(srv interface{}, ctx context.Context, dec func(int return interceptor(ctx, in, info, handler) } +func _Controller_ListProcesses_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListProcessesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControllerServer).ListProcesses(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/buildx.controller.v1.Controller/ListProcesses", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControllerServer).ListProcesses(ctx, req.(*ListProcessesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Controller_DisconnectProcess_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DisconnectProcessRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControllerServer).DisconnectProcess(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/buildx.controller.v1.Controller/DisconnectProcess", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControllerServer).DisconnectProcess(ctx, req.(*DisconnectProcessRequest)) + } + return interceptor(ctx, in, info, handler) +} + var _Controller_serviceDesc = grpc.ServiceDesc{ ServiceName: "buildx.controller.v1.Controller", HandlerType: (*ControllerServer)(nil), @@ -2206,6 +2499,14 @@ var _Controller_serviceDesc = grpc.ServiceDesc{ MethodName: "Info", Handler: _Controller_Info_Handler, }, + { + MethodName: "ListProcesses", + Handler: _Controller_ListProcesses_Handler, + }, + { + MethodName: "DisconnectProcess", + Handler: _Controller_DisconnectProcess_Handler, + }, }, Streams: []grpc.StreamDesc{ { diff --git a/controller/pb/controller.proto b/controller/pb/controller.proto index cd380732..944bbc26 100644 --- a/controller/pb/controller.proto +++ b/controller/pb/controller.proto @@ -14,6 +14,29 @@ service Controller { rpc List(ListRequest) returns (ListResponse); rpc Disconnect(DisconnectRequest) returns (DisconnectResponse); rpc Info(InfoRequest) returns (InfoResponse); + rpc ListProcesses(ListProcessesRequest) returns (ListProcessesResponse); + rpc DisconnectProcess(DisconnectProcessRequest) returns (DisconnectProcessResponse); +} + +message ListProcessesRequest { + string Ref = 1; +} + +message ListProcessesResponse { + repeated ProcessInfo Infos = 1; +} + +message ProcessInfo { + string ProcessID = 1; + InvokeConfig InvokeConfig = 2; +} + +message DisconnectProcessRequest { + string Ref = 1; + string ProcessID = 2; +} + +message DisconnectProcessResponse { } message BuildRequest { @@ -149,10 +172,14 @@ message Message { message InitMessage { string Ref = 1; - ContainerConfig ContainerConfig = 2; + + // If ProcessID already exists in the server, it tries to connect to it + // instead of invoking the new one. In this case, InvokeConfig will be ignored. + string ProcessID = 2; + InvokeConfig InvokeConfig = 3; } -message ContainerConfig { +message InvokeConfig { repeated string Entrypoint = 1; repeated string Cmd = 2; repeated string Env = 3; @@ -161,6 +188,7 @@ message ContainerConfig { string Cwd = 6; bool NoCwd = 7; // Do not set cwd but use the image's default bool Tty = 8; + bool Rollback = 9; // Kill all process in the container and recreate it. } message FdMessage { diff --git a/controller/processes/processes.go b/controller/processes/processes.go new file mode 100644 index 00000000..51f164fe --- /dev/null +++ b/controller/processes/processes.go @@ -0,0 +1,149 @@ +package processes + +import ( + "context" + "sync" + "sync/atomic" + + "github.com/docker/buildx/build" + "github.com/docker/buildx/controller/pb" + "github.com/docker/buildx/util/ioset" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +// Process provides methods to control a process. +type Process struct { + inEnd *ioset.Forwarder + invokeConfig *pb.InvokeConfig + errCh chan error + processCancel func() + serveIOCancel func() +} + +// ForwardIO forwards process's io to the specified reader/writer. +// Optionally specify ioCancelCallback which will be called when +// the process closes the specified IO. This will be useful for additional cleanup. +func (p *Process) ForwardIO(in *ioset.In, ioCancelCallback func()) { + p.inEnd.SetIn(in) + if f := p.serveIOCancel; f != nil { + f() + } + p.serveIOCancel = ioCancelCallback +} + +// Done returns a channel where error or nil will be sent +// when the process exits. +// TODO: change this to Wait() +func (p *Process) Done() <-chan error { + return p.errCh +} + +// Manager manages a set of proceses. +type Manager struct { + container atomic.Value + processes sync.Map +} + +// NewManager creates and returns a Manager. +func NewManager() *Manager { + return &Manager{} +} + +// Get returns the specified process. +func (m *Manager) Get(id string) (*Process, bool) { + v, ok := m.processes.Load(id) + if !ok { + return nil, false + } + return v.(*Process), true +} + +// CancelRunningProcesses cancels execution of all running processes. +func (m *Manager) CancelRunningProcesses() { + var funcs []func() + m.processes.Range(func(key, value any) bool { + funcs = append(funcs, value.(*Process).processCancel) + m.processes.Delete(key) + return true + }) + for _, f := range funcs { + f() + } +} + +// ListProcesses lists all running processes. +func (m *Manager) ListProcesses() (res []*pb.ProcessInfo) { + m.processes.Range(func(key, value any) bool { + res = append(res, &pb.ProcessInfo{ + ProcessID: key.(string), + InvokeConfig: value.(*Process).invokeConfig, + }) + return true + }) + return res +} + +// DeleteProcess deletes the specified process. +func (m *Manager) DeleteProcess(id string) error { + p, ok := m.processes.LoadAndDelete(id) + if !ok { + return errors.Errorf("unknown process %q", id) + } + p.(*Process).processCancel() + return nil +} + +// StartProcess starts a process in the container. +// When a container isn't available (i.e. first time invoking or the container has exited) or cfg.Rollback is set, +// this method will start a new container and run the process in it. Otherwise, this method starts a new process in the +// existing container. +func (m *Manager) StartProcess(pid string, resultCtx *build.ResultContext, cfg *pb.InvokeConfig) (*Process, error) { + // Get the target result to invoke a container from + var ctr *build.Container + if a := m.container.Load(); a != nil { + ctr = a.(*build.Container) + } + if cfg.Rollback || ctr == nil || ctr.IsUnavailable() { + go m.CancelRunningProcesses() + // (Re)create a new container if this is rollback or first time to invoke a process. + if ctr != nil { + go ctr.Cancel() // Finish the existing container + } + var err error + ctr, err = build.NewContainer(context.TODO(), resultCtx) + if err != nil { + return nil, errors.Errorf("failed to create container %v", err) + } + m.container.Store(ctr) + } + // [client(ForwardIO)] <-forwarder(switchable)-> [out] <-pipe-> [in] <- [process] + in, out := ioset.Pipe() + f := ioset.NewForwarder() + f.PropagateStdinClose = false + f.SetOut(&out) + + // Register process + ctx, cancel := context.WithCancel(context.TODO()) + var cancelOnce sync.Once + processCancelFunc := func() { cancelOnce.Do(func() { cancel(); f.Close(); in.Close(); out.Close() }) } + p := &Process{ + inEnd: f, + invokeConfig: cfg, + processCancel: processCancelFunc, + errCh: make(chan error), + } + m.processes.Store(pid, p) + go func() { + var err error + if err = ctr.Exec(ctx, cfg, in.Stdin, in.Stdout, in.Stderr); err != nil { + logrus.Errorf("failed to exec process: %v", err) + } + logrus.Debugf("finished process %s %v", pid, cfg.Entrypoint) + m.processes.Delete(pid) + processCancelFunc() + p.errCh <- err + }() + + return p, nil +} diff --git a/controller/remote/client.go b/controller/remote/client.go index 8898c7f0..9eeab3f1 100644 --- a/controller/remote/client.go +++ b/controller/remote/client.go @@ -75,15 +75,28 @@ func (c *Client) Disconnect(ctx context.Context, key string) error { return err } -func (c *Client) Invoke(ctx context.Context, ref string, containerConfig pb.ContainerConfig, in io.ReadCloser, stdout io.WriteCloser, stderr io.WriteCloser) error { - if ref == "" { +func (c *Client) ListProcesses(ctx context.Context, ref string) (infos []*pb.ProcessInfo, retErr error) { + res, err := c.client().ListProcesses(ctx, &pb.ListProcessesRequest{Ref: ref}) + if err != nil { + return nil, err + } + return res.Infos, nil +} + +func (c *Client) DisconnectProcess(ctx context.Context, ref, pid string) error { + _, err := c.client().DisconnectProcess(ctx, &pb.DisconnectProcessRequest{Ref: ref, ProcessID: pid}) + return err +} + +func (c *Client) Invoke(ctx context.Context, ref string, pid string, invokeConfig pb.InvokeConfig, in io.ReadCloser, stdout io.WriteCloser, stderr io.WriteCloser) error { + if ref == "" || pid == "" { return errors.New("build reference must be specified") } stream, err := c.client().Invoke(ctx) if err != nil { return err } - return attachIO(ctx, stream, &pb.InitMessage{Ref: ref, ContainerConfig: &containerConfig}, ioAttachConfig{ + return attachIO(ctx, stream, &pb.InitMessage{Ref: ref, ProcessID: pid, InvokeConfig: &invokeConfig}, ioAttachConfig{ stdin: in, stdout: stdout, stderr: stderr, diff --git a/controller/remote/server.go b/controller/remote/server.go index 072b5225..ae387755 100644 --- a/controller/remote/server.go +++ b/controller/remote/server.go @@ -4,16 +4,17 @@ import ( "context" "io" "sync" + "sync/atomic" "time" "github.com/docker/buildx/build" "github.com/docker/buildx/controller/pb" + "github.com/docker/buildx/controller/processes" "github.com/docker/buildx/util/ioset" "github.com/docker/buildx/version" controlapi "github.com/moby/buildkit/api/services/control" "github.com/moby/buildkit/client" "github.com/pkg/errors" - "github.com/sirupsen/logrus" "golang.org/x/sync/errgroup" ) @@ -27,16 +28,47 @@ func NewServer(buildFunc BuildFunc) *Server { type Server struct { buildFunc BuildFunc - session map[string]session + session map[string]*session sessionMu sync.Mutex } type session struct { - statusChan chan *client.SolveStatus - result *build.ResultContext - inputPipe *io.PipeWriter - curInvokeCancel func() - curBuildCancel func() + buildOnGoing atomic.Bool + statusChan chan *client.SolveStatus + cancelBuild func() + inputPipe *io.PipeWriter + + result *build.ResultContext + + processes *processes.Manager +} + +func (s *session) cancelRunningProcesses() { + s.processes.CancelRunningProcesses() +} + +func (m *Server) ListProcesses(ctx context.Context, req *pb.ListProcessesRequest) (res *pb.ListProcessesResponse, err error) { + m.sessionMu.Lock() + defer m.sessionMu.Unlock() + s, ok := m.session[req.Ref] + if !ok { + return nil, errors.Errorf("unknown ref %q", req.Ref) + } + res = new(pb.ListProcessesResponse) + for _, p := range s.processes.ListProcesses() { + res.Infos = append(res.Infos, p) + } + return res, nil +} + +func (m *Server) DisconnectProcess(ctx context.Context, req *pb.DisconnectProcessRequest) (res *pb.DisconnectProcessResponse, err error) { + m.sessionMu.Lock() + defer m.sessionMu.Unlock() + s, ok := m.session[req.Ref] + if !ok { + return nil, errors.Errorf("unknown ref %q", req.Ref) + } + return res, s.processes.DeleteProcess(req.ProcessID) } func (m *Server) Info(ctx context.Context, req *pb.InfoRequest) (res *pb.InfoResponse, err error) { @@ -75,12 +107,10 @@ func (m *Server) Disconnect(ctx context.Context, req *pb.DisconnectRequest) (res m.sessionMu.Lock() if s, ok := m.session[key]; ok { - if s.curBuildCancel != nil { - s.curBuildCancel() - } - if s.curInvokeCancel != nil { - s.curInvokeCancel() + if s.cancelBuild != nil { + s.cancelBuild() } + s.cancelRunningProcesses() } delete(m.session, key) m.sessionMu.Unlock() @@ -92,12 +122,10 @@ func (m *Server) Close() error { m.sessionMu.Lock() for k := range m.session { if s, ok := m.session[k]; ok { - if s.curBuildCancel != nil { - s.curBuildCancel() - } - if s.curInvokeCancel != nil { - s.curInvokeCancel() + if s.cancelBuild != nil { + s.cancelBuild() } + s.cancelRunningProcesses() } } m.sessionMu.Unlock() @@ -110,19 +138,30 @@ func (m *Server) Build(ctx context.Context, req *pb.BuildRequest) (*pb.BuildResp return nil, errors.New("build: empty key") } - // Prepare status channel and session if not exists + // Prepare status channel and session m.sessionMu.Lock() if m.session == nil { - m.session = make(map[string]session) + m.session = make(map[string]*session) } s, ok := m.session[ref] - if ok && m.session[ref].statusChan != nil { - m.sessionMu.Unlock() - return &pb.BuildResponse{}, errors.New("build or status ongoing or status didn't call") + if ok { + if !s.buildOnGoing.CompareAndSwap(false, true) { + m.sessionMu.Unlock() + return &pb.BuildResponse{}, errors.New("build ongoing") + } + s.cancelRunningProcesses() + s.result = nil + } else { + s = &session{} + s.buildOnGoing.Store(true) } + s.processes = processes.NewManager() statusChan := make(chan *client.SolveStatus) s.statusChan = statusChan - m.session[ref] = session{statusChan: statusChan} + inR, inW := io.Pipe() + defer inR.Close() + s.inputPipe = inW + m.session[ref] = s m.sessionMu.Unlock() defer func() { close(statusChan) @@ -130,23 +169,11 @@ func (m *Server) Build(ctx context.Context, req *pb.BuildRequest) (*pb.BuildResp s, ok := m.session[ref] if ok { s.statusChan = nil + s.buildOnGoing.Store(false) } m.sessionMu.Unlock() }() - // Prepare input stream pipe - inR, inW := io.Pipe() - m.sessionMu.Lock() - if s, ok := m.session[ref]; ok { - s.inputPipe = inW - m.session[ref] = s - } else { - m.sessionMu.Unlock() - return nil, errors.Errorf("build: unknown key %v", ref) - } - m.sessionMu.Unlock() - defer inR.Close() - // Build the specified request ctx, cancel := context.WithCancel(ctx) defer cancel() @@ -154,7 +181,7 @@ func (m *Server) Build(ctx context.Context, req *pb.BuildRequest) (*pb.BuildResp m.sessionMu.Lock() if s, ok := m.session[ref]; ok { s.result = res - s.curBuildCancel = cancel + s.cancelBuild = cancel m.session[ref] = s } else { m.sessionMu.Unlock() @@ -298,56 +325,51 @@ func (m *Server) Input(stream pb.Controller_InputServer) (err error) { } func (m *Server) Invoke(srv pb.Controller_InvokeServer) error { - ctx, cancel := context.WithCancel(context.TODO()) - defer cancel() containerIn, containerOut := ioset.Pipe() - waitInvokeDoneCh := make(chan struct{}) - var cancelOnce sync.Once - curInvokeCancel := func() { - cancelOnce.Do(func() { containerOut.Close(); containerIn.Close(); cancel() }) - <-waitInvokeDoneCh - } - defer curInvokeCancel() + defer func() { containerOut.Close(); containerIn.Close() }() - var cfg *pb.ContainerConfig - var resultCtx *build.ResultContext - initDoneCh := make(chan struct{}) + initDoneCh := make(chan *processes.Process) initErrCh := make(chan error) - eg, egCtx := errgroup.WithContext(ctx) + eg, egCtx := errgroup.WithContext(context.TODO()) + srvIOCtx, srvIOCancel := context.WithCancel(egCtx) eg.Go(func() error { - return serveIO(egCtx, srv, func(initMessage *pb.InitMessage) (retErr error) { + defer srvIOCancel() + return serveIO(srvIOCtx, srv, func(initMessage *pb.InitMessage) (retErr error) { defer func() { if retErr != nil { initErrCh <- retErr } - close(initDoneCh) }() ref := initMessage.Ref - cfg = initMessage.ContainerConfig + cfg := initMessage.InvokeConfig - // Register cancel callback m.sessionMu.Lock() - if s, ok := m.session[ref]; ok { - if cancel := s.curInvokeCancel; cancel != nil { - logrus.Warnf("invoke: cancelling ongoing invoke of %q", ref) - cancel() - } - s.curInvokeCancel = curInvokeCancel - m.session[ref] = s - } else { + s, ok := m.session[ref] + if !ok { m.sessionMu.Unlock() return errors.Errorf("invoke: unknown key %v", ref) } m.sessionMu.Unlock() - // Get the target result to invoke a container from - m.sessionMu.Lock() - if _, ok := m.session[ref]; !ok || m.session[ref].result == nil { - m.sessionMu.Unlock() - return errors.Errorf("unknown reference: %q", ref) + pid := initMessage.ProcessID + if pid == "" { + return errors.Errorf("invoke: specify process ID") } - resultCtx = m.session[ref].result - m.sessionMu.Unlock() + proc, ok := s.processes.Get(pid) + if !ok { + // Start a new process. + if cfg == nil { + return errors.New("no container config is provided") + } + var err error + proc, err = s.processes.StartProcess(pid, s.result, cfg) + if err != nil { + return err + } + } + // Attach containerIn to this process + proc.ForwardIO(&containerIn, srvIOCancel) + initDoneCh <- proc return nil }, &ioServerConfig{ stdin: containerOut.Stdin, @@ -356,43 +378,31 @@ func (m *Server) Invoke(srv pb.Controller_InvokeServer) error { // TODO: signal, resize }) }) - eg.Go(func() error { - defer containerIn.Close() - defer cancel() + eg.Go(func() (rErr error) { + defer srvIOCancel() + // Wait for init done + var proc *processes.Process select { - case <-initDoneCh: + case p := <-initDoneCh: + proc = p case err := <-initErrCh: return err + case <-egCtx.Done(): + return egCtx.Err() } - if cfg == nil { - return errors.New("no container config is provided") - } - if resultCtx == nil { - return errors.New("no result is provided") - } - ccfg := build.ContainerConfig{ - ResultCtx: resultCtx, - Entrypoint: cfg.Entrypoint, - Cmd: cfg.Cmd, - Env: cfg.Env, - Tty: cfg.Tty, - Stdin: containerIn.Stdin, - Stdout: containerIn.Stdout, - Stderr: containerIn.Stderr, - } - if !cfg.NoUser { - ccfg.User = &cfg.User - } - if !cfg.NoCwd { - ccfg.Cwd = &cfg.Cwd - } - return build.Invoke(egCtx, ccfg) - }) - err := eg.Wait() - close(waitInvokeDoneCh) - curInvokeCancel() - return err + // Wait for IO done + select { + case <-srvIOCtx.Done(): + return srvIOCtx.Err() + case err := <-proc.Done(): + return err + case <-egCtx.Done(): + return egCtx.Err() + } + }) + + return eg.Wait() } func toControlStatus(s *client.SolveStatus) *pb.StatusResponse { diff --git a/driver/docker-container/driver.go b/driver/docker-container/driver.go index f81ad8a3..14e589d8 100644 --- a/driver/docker-container/driver.go +++ b/driver/docker-container/driver.go @@ -108,6 +108,7 @@ func (d *Driver) create(ctx context.Context, l progress.SubLogger) error { cfg.Cmd = d.InitConfig.BuildkitFlags } + useInit := true // let it cleanup exited processes created by BuildKit's container API if err := l.Wrap("creating container "+d.Name, func() error { hc := &container.HostConfig{ Privileged: true, @@ -118,6 +119,7 @@ func (d *Driver) create(ctx context.Context, l progress.SubLogger) error { Target: confutil.DefaultBuildKitStateDir, }, }, + Init: &useInit, } if d.netMode != "" { hc.NetworkMode = container.NetworkMode(d.netMode) diff --git a/monitor/monitor.go b/monitor/monitor.go index 0e2b62cb..ca7dfc9b 100644 --- a/monitor/monitor.go +++ b/monitor/monitor.go @@ -7,12 +7,15 @@ import ( "sort" "strings" "sync" + "sync/atomic" "text/tabwriter" "github.com/containerd/console" "github.com/docker/buildx/controller/control" controllerapi "github.com/docker/buildx/controller/pb" "github.com/docker/buildx/util/ioset" + "github.com/moby/buildkit/identity" + "github.com/pkg/errors" "github.com/sirupsen/logrus" "golang.org/x/term" ) @@ -22,7 +25,9 @@ Available commands are: reload reloads the context and build it. rollback re-runs the interactive container with initial rootfs contents. list list buildx sessions. - attach attach to a buildx server. + attach attach to a buildx server or a process in the container. + exec execute a process in the interactive container. + ps list processes invoked by "exec". Use "attach" to attach IO to that process. disconnect disconnect a client from a buildx server. Specific session ID can be specified an arg. kill kill buildx server. exit exits monitor. @@ -30,7 +35,7 @@ Available commands are: ` // 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.ContainerConfig, c control.BuildxController, progressMode string, stdin io.ReadCloser, stdout io.WriteCloser, stderr console.File) error { +func RunMonitor(ctx context.Context, curRef string, options controllerapi.BuildOptions, invokeConfig controllerapi.InvokeConfig, c control.BuildxController, progressMode string, stdin io.ReadCloser, stdout io.WriteCloser, stderr console.File) error { defer func() { if err := c.Disconnect(ctx, curRef); err != nil { logrus.Warnf("disconnect error: %v", err) @@ -70,18 +75,17 @@ func RunMonitor(ctx context.Context, curRef string, options controllerapi.BuildO }, []ioset.MuxOut{monitorOutCtx, containerOutCtx}, 1, func(prev int, res int) string { if prev == 0 && res == 0 { // No toggle happened because container I/O isn't enabled. - return "No running interactive containers. You can start one by issuing rollback command\n" + return "Process isn't attached (previous \"exec\" exited?). Use \"attach\" for attaching or \"rollback\" or \"exec\" for running new one.\n" } return "Switched IO\n" }), - invokeFunc: func(ctx context.Context, ref string, in io.ReadCloser, out io.WriteCloser, err io.WriteCloser) error { - return c.Invoke(ctx, ref, invokeConfig, in, out, err) - }, + invokeFunc: c.Invoke, } // Start container automatically fmt.Fprintf(stdout, "Launching interactive container. Press Ctrl-a-c to switch to monitor console\n") - m.rollback(ctx, curRef) + id := m.rollback(ctx, curRef, invokeConfig) + fmt.Fprintf(stdout, "Interactive container was restarted with process %q. Press Ctrl-a-c to switch to the new container\n", id) // Serve monitor commands monitorForwarder := ioset.NewForwarder() @@ -127,12 +131,17 @@ func RunMonitor(ctx context.Context, curRef string, options controllerapi.BuildO } else { curRef = ref // rollback the running container with the new result - m.rollback(ctx, curRef) - fmt.Fprint(stdout, "Interactive container was restarted. Press Ctrl-a-c to switch to the new container\n") + id := m.rollback(ctx, curRef, invokeConfig) + fmt.Fprintf(stdout, "Interactive container was restarted with process %q. Press Ctrl-a-c to switch to the new container\n", id) } case "rollback": - m.rollback(ctx, curRef) - fmt.Fprint(stdout, "Interactive container was restarted. Press Ctrl-a-c to switch to the new container\n") + cfg := invokeConfig + if len(args) >= 2 { + cfg.Entrypoint = []string{args[1]} + cfg.Cmd = args[2:] + } + id := m.rollback(ctx, curRef, cfg) + fmt.Fprintf(stdout, "Interactive container was restarted with process %q. Press Ctrl-a-c to switch to the new container\n", id) case "list": refs, err := c.List(ctx) if err != nil { @@ -150,6 +159,14 @@ func RunMonitor(ctx context.Context, curRef string, options controllerapi.BuildO if len(args) >= 2 { target = args[1] } + isProcess, err := isProcessID(ctx, c, curRef, target) + if err == nil && isProcess { + if err := c.DisconnectProcess(ctx, curRef, target); err != nil { + fmt.Printf("disconnect process failed %v\n", target) + continue + } + continue + } if err := c.Disconnect(ctx, target); err != nil { fmt.Println("disconnect error", err) } @@ -163,8 +180,68 @@ func RunMonitor(ctx context.Context, curRef string, options controllerapi.BuildO continue } ref := args[1] - m.rollback(ctx, ref) - curRef = ref + var id string + + isProcess, err := isProcessID(ctx, c, curRef, ref) + if err == nil && isProcess { + m.attach(ctx, curRef, ref) + id = ref + } + if id == "" { + refs, err := c.List(ctx) + if err != nil { + fmt.Printf("failed to get the list of sessions: %v\n", err) + continue + } + found := false + for _, s := range refs { + if s == ref { + found = true + break + } + } + if !found { + fmt.Printf("unknown ID: %q\n", ref) + continue + } + if m.invokeCancel != nil { + m.invokeCancel() // Finish existing attach + } + curRef = ref + } + fmt.Fprintf(stdout, "Attached to process %q. Press Ctrl-a-c to switch to the new container\n", id) + case "exec": + if len(args) < 2 { + fmt.Println("exec: server name must be passed") + continue + } + if curRef == "" { + fmt.Println("attach to a session first") + continue + } + cfg := controllerapi.InvokeConfig{ + Entrypoint: []string{args[1]}, + Cmd: args[2:], + // TODO: support other options as well via flags + Env: invokeConfig.Env, + User: invokeConfig.User, + Cwd: invokeConfig.Cwd, + Tty: true, + } + pid := m.exec(ctx, curRef, cfg) + fmt.Fprintf(stdout, "Process %q started. Press Ctrl-a-c to switch to that process.\n", pid) + case "ps": + plist, err := c.ListProcesses(ctx, curRef) + if err != nil { + fmt.Println("cannot list process:", err) + continue + } + tw := tabwriter.NewWriter(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 == m.attachedPid.Load(), append(p.InvokeConfig.Entrypoint, p.InvokeConfig.Cmd...)) + } + tw.Flush() case "exit": return case "help": @@ -177,14 +254,10 @@ func RunMonitor(ctx context.Context, curRef string, options controllerapi.BuildO }() select { case <-doneCh: - if m.curInvokeCancel != nil { - m.curInvokeCancel() - } + m.invokeCancel() return nil case err := <-errCh: - if m.curInvokeCancel != nil { - m.curInvokeCancel() - } + m.invokeCancel() return err case <-monitorDisableCh: } @@ -198,34 +271,58 @@ type readWriter struct { } type monitor struct { - muxIO *ioset.MuxIO - invokeIO *ioset.Forwarder - invokeFunc func(context.Context, string, io.ReadCloser, io.WriteCloser, io.WriteCloser) error - curInvokeCancel func() + muxIO *ioset.MuxIO + invokeIO *ioset.Forwarder + invokeFunc func(ctx context.Context, ref, pid string, cfg controllerapi.InvokeConfig, in io.ReadCloser, out io.WriteCloser, err io.WriteCloser) error + invokeCancel func() + attachedPid atomic.Value } -func (m *monitor) rollback(ctx context.Context, ref string) { - if m.curInvokeCancel != nil { - m.curInvokeCancel() // Finish the running container if exists +func (m *monitor) rollback(ctx context.Context, ref string, cfg controllerapi.InvokeConfig) string { + pid := identity.NewID() + cfg1 := cfg + cfg1.Rollback = true + return m.startInvoke(ctx, ref, pid, cfg1) +} + +func (m *monitor) exec(ctx context.Context, ref string, cfg controllerapi.InvokeConfig) string { + return m.startInvoke(ctx, ref, identity.NewID(), cfg) +} + +func (m *monitor) attach(ctx context.Context, ref, pid string) { + m.startInvoke(ctx, ref, pid, controllerapi.InvokeConfig{}) +} + +func (m *monitor) startInvoke(ctx context.Context, ref, pid string, cfg controllerapi.InvokeConfig) string { + if m.invokeCancel != nil { + m.invokeCancel() // Finish existing attach } go func() { - // Start a new container - if err := m.invoke(ctx, ref); err != nil { + // Start a new invoke + if err := m.invoke(ctx, ref, pid, cfg); err != nil { logrus.Debugf("invoke error: %v", err) } + if pid == m.attachedPid.Load() { + m.attachedPid.Store("") + } }() + m.attachedPid.Store(pid) + return pid } -func (m *monitor) invoke(ctx context.Context, ref string) error { +func (m *monitor) invoke(ctx context.Context, ref, pid string, cfg controllerapi.InvokeConfig) error { m.muxIO.Enable(1) defer m.muxIO.Disable(1) + if err := m.muxIO.SwitchTo(1); err != nil { + return errors.Errorf("failed to switch to process IO: %v", err) + } invokeCtx, invokeCancel := context.WithCancel(ctx) containerIn, containerOut := ioset.Pipe() m.invokeIO.SetOut(&containerOut) waitInvokeDoneCh := make(chan struct{}) var cancelOnce sync.Once - curInvokeCancel := func() { + invokeCancelAndDetachFn := func() { cancelOnce.Do(func() { containerIn.Close() m.invokeIO.SetOut(nil) @@ -233,10 +330,10 @@ func (m *monitor) invoke(ctx context.Context, ref string) error { }) <-waitInvokeDoneCh } - defer curInvokeCancel() - m.curInvokeCancel = curInvokeCancel + defer invokeCancelAndDetachFn() + m.invokeCancel = invokeCancelAndDetachFn - err := m.invokeFunc(invokeCtx, ref, containerIn.Stdin, containerIn.Stdout, containerIn.Stderr) + err := m.invokeFunc(invokeCtx, ref, pid, cfg, containerIn.Stdin, containerIn.Stdout, containerIn.Stderr) close(waitInvokeDoneCh) return err @@ -247,3 +344,16 @@ type nopCloser struct { } func (c nopCloser) Close() error { return nil } + +func isProcessID(ctx context.Context, c control.BuildxController, curRef, ref string) (bool, error) { + infos, err := c.ListProcesses(ctx, curRef) + if err != nil { + return false, err + } + for _, p := range infos { + if p.ProcessID == ref { + return true, nil + } + } + return false, nil +} diff --git a/util/ioset/ioset.go b/util/ioset/ioset.go index 38cdeb4d..40858bce 100644 --- a/util/ioset/ioset.go +++ b/util/ioset/ioset.go @@ -159,15 +159,27 @@ func (f *SingleForwarder) doForward() { var r io.ReadCloser for { readerInvalid := false + var readerInvalidMu sync.Mutex + copyReaderToWriter := false if r != nil { + copyReaderToWriter = true + } + if copyReaderToWriter { + srcR := r go func() { buf := make([]byte, 4096) + readerClosed := false for { - n, readErr := r.Read(buf) - if readErr != nil && !errors.Is(readErr, io.EOF) && !errors.Is(readErr, io.ErrClosedPipe) { - logrus.Debugf("single forwarder: reader error: %v", readErr) - return + n, readErr := srcR.Read(buf) + if readErr != nil { + srcR.Close() + readerClosed = true + if !errors.Is(readErr, io.EOF) && !errors.Is(readErr, io.ErrClosedPipe) { + logrus.Debugf("single forwarder: reader error: %v", readErr) + return + } } + f.curWMu.Lock() w := f.curW f.curWMu.Unlock() @@ -176,10 +188,14 @@ func (f *SingleForwarder) doForward() { logrus.Debugf("single forwarder: writer error: %v", err) } } - if readerInvalid { + readerInvalidMu.Lock() + ri := readerInvalid + readerInvalidMu.Unlock() + if ri || readerClosed { return } if readErr != io.EOF { + logrus.Debugf("unknown error: %v\n", readErr) continue } @@ -202,7 +218,9 @@ func (f *SingleForwarder) doForward() { } f.curR = newR r = newR + readerInvalidMu.Lock() readerInvalid = true + readerInvalidMu.Unlock() f.curRMu.Unlock() case <-f.doneCh: return diff --git a/util/ioset/mux.go b/util/ioset/mux.go index 278fd529..1d3ec616 100644 --- a/util/ioset/mux.go +++ b/util/ioset/mux.go @@ -133,6 +133,27 @@ func (m *MuxIO) Enable(i int) { m.enabled[i] = struct{}{} } +func (m *MuxIO) SwitchTo(i int) error { + m.mu.Lock() + defer m.mu.Unlock() + if m.cur == i { + return nil + } + if _, ok := m.enabled[i]; !ok { + return errors.Errorf("IO index %d isn't active", i) + } + if m.outs[m.cur].DisableHook != nil { + m.outs[m.cur].DisableHook() + } + prev := m.cur + m.cur = i + if m.outs[m.cur].EnableHook != nil { + m.outs[m.cur].EnableHook() + } + fmt.Fprint(m.in.Stdout, m.toggleMessage(prev, i)) + return nil +} + func (m *MuxIO) Disable(i int) error { m.mu.Lock() defer m.mu.Unlock()