Merge pull request #1626 from ktock/monitor-exec

monitor: Enable to exec into the container
This commit is contained in:
Tõnis Tiigi 2023-03-07 17:46:22 +00:00 committed by GitHub
commit c5ce08bf3c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 1249 additions and 481 deletions

View File

@ -660,151 +660,6 @@ func toSolveOpt(ctx context.Context, node builder.Node, multiDriver bool, opt Op
return &so, releaseF, nil 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) { 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) return BuildWithResultHandler(ctx, nodes, opt, docker, configDir, w, nil)
} }

216
build/invoke.go Normal file
View File

@ -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()
}

View File

@ -471,10 +471,10 @@ func launchControllerAndRunBuild(dockerCli command.Cli, options buildOptions) er
// stdin must be usable for monitor // stdin must be usable for monitor
return errors.Errorf("Dockerfile or context from stdin is not supported with invoke") 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 != "" { if inv := options.invoke; inv != "" {
var err error var err error
invokeConfig, err = parseInvokeConfig(inv) // TODO: produce *controller.ContainerConfig directly. invokeConfig, err = parseInvokeConfig(inv)
if err != nil { if err != nil {
return err return err
} }
@ -575,7 +575,7 @@ func launchControllerAndRunBuild(dockerCli command.Cli, options buildOptions) er
return nil return nil
} }
func parseInvokeConfig(invoke string) (cfg controllerapi.ContainerConfig, err error) { func parseInvokeConfig(invoke string) (cfg controllerapi.InvokeConfig, err error) {
cfg.Tty = true cfg.Tty = true
if invoke == "default" { if invoke == "default" {
return cfg, nil return cfg, nil

View File

@ -11,11 +11,17 @@ import (
type BuildxController interface { 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) 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 Kill(ctx context.Context) error
Close() error Close() error
List(ctx context.Context) (refs []string, _ error) List(ctx context.Context) (refs []string, _ error)
Disconnect(ctx context.Context, ref 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 { type ControlOptions struct {

View File

@ -3,12 +3,15 @@ package local
import ( import (
"context" "context"
"io" "io"
"sync/atomic"
"github.com/containerd/console" "github.com/containerd/console"
"github.com/docker/buildx/build" "github.com/docker/buildx/build"
cbuild "github.com/docker/buildx/controller/build" cbuild "github.com/docker/buildx/controller/build"
"github.com/docker/buildx/controller/control" "github.com/docker/buildx/controller/control"
controllerapi "github.com/docker/buildx/controller/pb" 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/docker/cli/cli/command"
"github.com/moby/buildkit/client" "github.com/moby/buildkit/client"
"github.com/pkg/errors" "github.com/pkg/errors"
@ -18,6 +21,7 @@ func NewLocalBuildxController(ctx context.Context, dockerCli command.Cli) contro
return &localController{ return &localController{
dockerCli: dockerCli, dockerCli: dockerCli,
ref: "local", ref: "local",
processes: processes.NewManager(),
} }
} }
@ -25,9 +29,17 @@ type localController struct {
dockerCli command.Cli dockerCli command.Cli
ref string ref string
resultCtx *build.ResultContext 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) { 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) resp, res, err := cbuild.RunBuild(ctx, b.dockerCli, options, in, progressMode, nil)
if err != nil { if err != nil {
return "", nil, err return "", nil, err
@ -36,38 +48,64 @@ func (b *localController) Build(ctx context.Context, options controllerapi.Build
return b.ref, resp, nil 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 { if ref != b.ref {
return errors.Errorf("unknown ref %q", ref) return errors.Errorf("unknown ref %q", ref)
} }
if b.resultCtx == nil { return b.processes.DeleteProcess(pid)
return errors.New("no build result is registered") }
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, proc, ok := b.processes.Get(pid)
Entrypoint: cfg.Entrypoint, if !ok {
Cmd: cfg.Cmd, // Start a new process.
Env: cfg.Env, if b.resultCtx == nil {
Tty: cfg.Tty, return errors.New("no build result is registered")
Stdin: ioIn, }
Stdout: ioOut, var err error
Stderr: ioErr, 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 { func (b *localController) Kill(context.Context) error {
return nil // nop b.cancelRunningProcesses()
return nil
} }
func (b *localController) Close() error { func (b *localController) Close() error {
// TODO: cancel current build and invoke b.cancelRunningProcesses()
// TODO: cancel ongoing builds?
return nil 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 { func (b *localController) Disconnect(ctx context.Context, key string) error {
return nil // nop b.cancelRunningProcesses()
return nil
} }

View File

@ -25,6 +25,204 @@ var _ = math.Inf
// proto package needs to be updated. // proto package needs to be updated.
const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package 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 { type BuildRequest struct {
Ref string `protobuf:"bytes,1,opt,name=Ref,proto3" json:"Ref,omitempty"` Ref string `protobuf:"bytes,1,opt,name=Ref,proto3" json:"Ref,omitempty"`
Options *BuildOptions `protobuf:"bytes,2,opt,name=Options,proto3" json:"Options,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 (m *BuildRequest) String() string { return proto.CompactTextString(m) }
func (*BuildRequest) ProtoMessage() {} func (*BuildRequest) ProtoMessage() {}
func (*BuildRequest) Descriptor() ([]byte, []int) { func (*BuildRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_ed7f10298fa1d90f, []int{0} return fileDescriptor_ed7f10298fa1d90f, []int{5}
} }
func (m *BuildRequest) XXX_Unmarshal(b []byte) error { func (m *BuildRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_BuildRequest.Unmarshal(m, b) 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 (m *BuildOptions) String() string { return proto.CompactTextString(m) }
func (*BuildOptions) ProtoMessage() {} func (*BuildOptions) ProtoMessage() {}
func (*BuildOptions) Descriptor() ([]byte, []int) { func (*BuildOptions) Descriptor() ([]byte, []int) {
return fileDescriptor_ed7f10298fa1d90f, []int{1} return fileDescriptor_ed7f10298fa1d90f, []int{6}
} }
func (m *BuildOptions) XXX_Unmarshal(b []byte) error { func (m *BuildOptions) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_BuildOptions.Unmarshal(m, b) 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 (m *ExportEntry) String() string { return proto.CompactTextString(m) }
func (*ExportEntry) ProtoMessage() {} func (*ExportEntry) ProtoMessage() {}
func (*ExportEntry) Descriptor() ([]byte, []int) { func (*ExportEntry) Descriptor() ([]byte, []int) {
return fileDescriptor_ed7f10298fa1d90f, []int{2} return fileDescriptor_ed7f10298fa1d90f, []int{7}
} }
func (m *ExportEntry) XXX_Unmarshal(b []byte) error { func (m *ExportEntry) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ExportEntry.Unmarshal(m, b) 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 (m *CacheOptionsEntry) String() string { return proto.CompactTextString(m) }
func (*CacheOptionsEntry) ProtoMessage() {} func (*CacheOptionsEntry) ProtoMessage() {}
func (*CacheOptionsEntry) Descriptor() ([]byte, []int) { func (*CacheOptionsEntry) Descriptor() ([]byte, []int) {
return fileDescriptor_ed7f10298fa1d90f, []int{3} return fileDescriptor_ed7f10298fa1d90f, []int{8}
} }
func (m *CacheOptionsEntry) XXX_Unmarshal(b []byte) error { func (m *CacheOptionsEntry) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_CacheOptionsEntry.Unmarshal(m, b) 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 (m *Attest) String() string { return proto.CompactTextString(m) }
func (*Attest) ProtoMessage() {} func (*Attest) ProtoMessage() {}
func (*Attest) Descriptor() ([]byte, []int) { func (*Attest) Descriptor() ([]byte, []int) {
return fileDescriptor_ed7f10298fa1d90f, []int{4} return fileDescriptor_ed7f10298fa1d90f, []int{9}
} }
func (m *Attest) XXX_Unmarshal(b []byte) error { func (m *Attest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Attest.Unmarshal(m, b) 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 (m *SSH) String() string { return proto.CompactTextString(m) }
func (*SSH) ProtoMessage() {} func (*SSH) ProtoMessage() {}
func (*SSH) Descriptor() ([]byte, []int) { func (*SSH) Descriptor() ([]byte, []int) {
return fileDescriptor_ed7f10298fa1d90f, []int{5} return fileDescriptor_ed7f10298fa1d90f, []int{10}
} }
func (m *SSH) XXX_Unmarshal(b []byte) error { func (m *SSH) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_SSH.Unmarshal(m, b) 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 (m *Secret) String() string { return proto.CompactTextString(m) }
func (*Secret) ProtoMessage() {} func (*Secret) ProtoMessage() {}
func (*Secret) Descriptor() ([]byte, []int) { func (*Secret) Descriptor() ([]byte, []int) {
return fileDescriptor_ed7f10298fa1d90f, []int{6} return fileDescriptor_ed7f10298fa1d90f, []int{11}
} }
func (m *Secret) XXX_Unmarshal(b []byte) error { func (m *Secret) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Secret.Unmarshal(m, b) 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 (m *UlimitOpt) String() string { return proto.CompactTextString(m) }
func (*UlimitOpt) ProtoMessage() {} func (*UlimitOpt) ProtoMessage() {}
func (*UlimitOpt) Descriptor() ([]byte, []int) { func (*UlimitOpt) Descriptor() ([]byte, []int) {
return fileDescriptor_ed7f10298fa1d90f, []int{7} return fileDescriptor_ed7f10298fa1d90f, []int{12}
} }
func (m *UlimitOpt) XXX_Unmarshal(b []byte) error { func (m *UlimitOpt) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_UlimitOpt.Unmarshal(m, b) 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 (m *Ulimit) String() string { return proto.CompactTextString(m) }
func (*Ulimit) ProtoMessage() {} func (*Ulimit) ProtoMessage() {}
func (*Ulimit) Descriptor() ([]byte, []int) { func (*Ulimit) Descriptor() ([]byte, []int) {
return fileDescriptor_ed7f10298fa1d90f, []int{8} return fileDescriptor_ed7f10298fa1d90f, []int{13}
} }
func (m *Ulimit) XXX_Unmarshal(b []byte) error { func (m *Ulimit) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Ulimit.Unmarshal(m, b) 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 (m *CommonOptions) String() string { return proto.CompactTextString(m) }
func (*CommonOptions) ProtoMessage() {} func (*CommonOptions) ProtoMessage() {}
func (*CommonOptions) Descriptor() ([]byte, []int) { func (*CommonOptions) Descriptor() ([]byte, []int) {
return fileDescriptor_ed7f10298fa1d90f, []int{9} return fileDescriptor_ed7f10298fa1d90f, []int{14}
} }
func (m *CommonOptions) XXX_Unmarshal(b []byte) error { func (m *CommonOptions) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_CommonOptions.Unmarshal(m, b) 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 (m *BuildResponse) String() string { return proto.CompactTextString(m) }
func (*BuildResponse) ProtoMessage() {} func (*BuildResponse) ProtoMessage() {}
func (*BuildResponse) Descriptor() ([]byte, []int) { func (*BuildResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_ed7f10298fa1d90f, []int{10} return fileDescriptor_ed7f10298fa1d90f, []int{15}
} }
func (m *BuildResponse) XXX_Unmarshal(b []byte) error { func (m *BuildResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_BuildResponse.Unmarshal(m, b) 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 (m *DisconnectRequest) String() string { return proto.CompactTextString(m) }
func (*DisconnectRequest) ProtoMessage() {} func (*DisconnectRequest) ProtoMessage() {}
func (*DisconnectRequest) Descriptor() ([]byte, []int) { func (*DisconnectRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_ed7f10298fa1d90f, []int{11} return fileDescriptor_ed7f10298fa1d90f, []int{16}
} }
func (m *DisconnectRequest) XXX_Unmarshal(b []byte) error { func (m *DisconnectRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_DisconnectRequest.Unmarshal(m, b) 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 (m *DisconnectResponse) String() string { return proto.CompactTextString(m) }
func (*DisconnectResponse) ProtoMessage() {} func (*DisconnectResponse) ProtoMessage() {}
func (*DisconnectResponse) Descriptor() ([]byte, []int) { func (*DisconnectResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_ed7f10298fa1d90f, []int{12} return fileDescriptor_ed7f10298fa1d90f, []int{17}
} }
func (m *DisconnectResponse) XXX_Unmarshal(b []byte) error { func (m *DisconnectResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_DisconnectResponse.Unmarshal(m, b) 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 (m *ListRequest) String() string { return proto.CompactTextString(m) }
func (*ListRequest) ProtoMessage() {} func (*ListRequest) ProtoMessage() {}
func (*ListRequest) Descriptor() ([]byte, []int) { func (*ListRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_ed7f10298fa1d90f, []int{13} return fileDescriptor_ed7f10298fa1d90f, []int{18}
} }
func (m *ListRequest) XXX_Unmarshal(b []byte) error { func (m *ListRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ListRequest.Unmarshal(m, b) 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 (m *ListResponse) String() string { return proto.CompactTextString(m) }
func (*ListResponse) ProtoMessage() {} func (*ListResponse) ProtoMessage() {}
func (*ListResponse) Descriptor() ([]byte, []int) { func (*ListResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_ed7f10298fa1d90f, []int{14} return fileDescriptor_ed7f10298fa1d90f, []int{19}
} }
func (m *ListResponse) XXX_Unmarshal(b []byte) error { func (m *ListResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ListResponse.Unmarshal(m, b) 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 (m *InputMessage) String() string { return proto.CompactTextString(m) }
func (*InputMessage) ProtoMessage() {} func (*InputMessage) ProtoMessage() {}
func (*InputMessage) Descriptor() ([]byte, []int) { func (*InputMessage) Descriptor() ([]byte, []int) {
return fileDescriptor_ed7f10298fa1d90f, []int{15} return fileDescriptor_ed7f10298fa1d90f, []int{20}
} }
func (m *InputMessage) XXX_Unmarshal(b []byte) error { func (m *InputMessage) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_InputMessage.Unmarshal(m, b) 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 (m *InputInitMessage) String() string { return proto.CompactTextString(m) }
func (*InputInitMessage) ProtoMessage() {} func (*InputInitMessage) ProtoMessage() {}
func (*InputInitMessage) Descriptor() ([]byte, []int) { func (*InputInitMessage) Descriptor() ([]byte, []int) {
return fileDescriptor_ed7f10298fa1d90f, []int{16} return fileDescriptor_ed7f10298fa1d90f, []int{21}
} }
func (m *InputInitMessage) XXX_Unmarshal(b []byte) error { func (m *InputInitMessage) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_InputInitMessage.Unmarshal(m, b) 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 (m *DataMessage) String() string { return proto.CompactTextString(m) }
func (*DataMessage) ProtoMessage() {} func (*DataMessage) ProtoMessage() {}
func (*DataMessage) Descriptor() ([]byte, []int) { func (*DataMessage) Descriptor() ([]byte, []int) {
return fileDescriptor_ed7f10298fa1d90f, []int{17} return fileDescriptor_ed7f10298fa1d90f, []int{22}
} }
func (m *DataMessage) XXX_Unmarshal(b []byte) error { func (m *DataMessage) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_DataMessage.Unmarshal(m, b) 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 (m *InputResponse) String() string { return proto.CompactTextString(m) }
func (*InputResponse) ProtoMessage() {} func (*InputResponse) ProtoMessage() {}
func (*InputResponse) Descriptor() ([]byte, []int) { func (*InputResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_ed7f10298fa1d90f, []int{18} return fileDescriptor_ed7f10298fa1d90f, []int{23}
} }
func (m *InputResponse) XXX_Unmarshal(b []byte) error { func (m *InputResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_InputResponse.Unmarshal(m, b) 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 (m *Message) String() string { return proto.CompactTextString(m) }
func (*Message) ProtoMessage() {} func (*Message) ProtoMessage() {}
func (*Message) Descriptor() ([]byte, []int) { func (*Message) Descriptor() ([]byte, []int) {
return fileDescriptor_ed7f10298fa1d90f, []int{19} return fileDescriptor_ed7f10298fa1d90f, []int{24}
} }
func (m *Message) XXX_Unmarshal(b []byte) error { func (m *Message) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Message.Unmarshal(m, b) return xxx_messageInfo_Message.Unmarshal(m, b)
@ -1187,18 +1385,21 @@ func (*Message) XXX_OneofWrappers() []interface{} {
} }
type InitMessage struct { type InitMessage struct {
Ref string `protobuf:"bytes,1,opt,name=Ref,proto3" json:"Ref,omitempty"` Ref string `protobuf:"bytes,1,opt,name=Ref,proto3" json:"Ref,omitempty"`
ContainerConfig *ContainerConfig `protobuf:"bytes,2,opt,name=ContainerConfig,proto3" json:"ContainerConfig,omitempty"` // If ProcessID already exists in the server, it tries to connect to it
XXX_NoUnkeyedLiteral struct{} `json:"-"` // instead of invoking the new one. In this case, InvokeConfig will be ignored.
XXX_unrecognized []byte `json:"-"` ProcessID string `protobuf:"bytes,2,opt,name=ProcessID,proto3" json:"ProcessID,omitempty"`
XXX_sizecache int32 `json:"-"` 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) Reset() { *m = InitMessage{} }
func (m *InitMessage) String() string { return proto.CompactTextString(m) } func (m *InitMessage) String() string { return proto.CompactTextString(m) }
func (*InitMessage) ProtoMessage() {} func (*InitMessage) ProtoMessage() {}
func (*InitMessage) Descriptor() ([]byte, []int) { func (*InitMessage) Descriptor() ([]byte, []int) {
return fileDescriptor_ed7f10298fa1d90f, []int{20} return fileDescriptor_ed7f10298fa1d90f, []int{25}
} }
func (m *InitMessage) XXX_Unmarshal(b []byte) error { func (m *InitMessage) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_InitMessage.Unmarshal(m, b) return xxx_messageInfo_InitMessage.Unmarshal(m, b)
@ -1225,14 +1426,21 @@ func (m *InitMessage) GetRef() string {
return "" return ""
} }
func (m *InitMessage) GetContainerConfig() *ContainerConfig { func (m *InitMessage) GetProcessID() string {
if m != nil { if m != nil {
return m.ContainerConfig return m.ProcessID
}
return ""
}
func (m *InitMessage) GetInvokeConfig() *InvokeConfig {
if m != nil {
return m.InvokeConfig
} }
return nil return nil
} }
type ContainerConfig struct { type InvokeConfig struct {
Entrypoint []string `protobuf:"bytes,1,rep,name=Entrypoint,proto3" json:"Entrypoint,omitempty"` Entrypoint []string `protobuf:"bytes,1,rep,name=Entrypoint,proto3" json:"Entrypoint,omitempty"`
Cmd []string `protobuf:"bytes,2,rep,name=Cmd,proto3" json:"Cmd,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"` 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"` Cwd string `protobuf:"bytes,6,opt,name=Cwd,proto3" json:"Cwd,omitempty"`
NoCwd bool `protobuf:"varint,7,opt,name=NoCwd,proto3" json:"NoCwd,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"` 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_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"` XXX_sizecache int32 `json:"-"`
} }
func (m *ContainerConfig) Reset() { *m = ContainerConfig{} } func (m *InvokeConfig) Reset() { *m = InvokeConfig{} }
func (m *ContainerConfig) String() string { return proto.CompactTextString(m) } func (m *InvokeConfig) String() string { return proto.CompactTextString(m) }
func (*ContainerConfig) ProtoMessage() {} func (*InvokeConfig) ProtoMessage() {}
func (*ContainerConfig) Descriptor() ([]byte, []int) { func (*InvokeConfig) Descriptor() ([]byte, []int) {
return fileDescriptor_ed7f10298fa1d90f, []int{21} return fileDescriptor_ed7f10298fa1d90f, []int{26}
} }
func (m *ContainerConfig) XXX_Unmarshal(b []byte) error { func (m *InvokeConfig) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ContainerConfig.Unmarshal(m, b) return xxx_messageInfo_InvokeConfig.Unmarshal(m, b)
} }
func (m *ContainerConfig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { func (m *InvokeConfig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ContainerConfig.Marshal(b, m, deterministic) return xxx_messageInfo_InvokeConfig.Marshal(b, m, deterministic)
} }
func (m *ContainerConfig) XXX_Merge(src proto.Message) { func (m *InvokeConfig) XXX_Merge(src proto.Message) {
xxx_messageInfo_ContainerConfig.Merge(m, src) xxx_messageInfo_InvokeConfig.Merge(m, src)
} }
func (m *ContainerConfig) XXX_Size() int { func (m *InvokeConfig) XXX_Size() int {
return xxx_messageInfo_ContainerConfig.Size(m) return xxx_messageInfo_InvokeConfig.Size(m)
} }
func (m *ContainerConfig) XXX_DiscardUnknown() { func (m *InvokeConfig) XXX_DiscardUnknown() {
xxx_messageInfo_ContainerConfig.DiscardUnknown(m) 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 { if m != nil {
return m.Entrypoint return m.Entrypoint
} }
return nil return nil
} }
func (m *ContainerConfig) GetCmd() []string { func (m *InvokeConfig) GetCmd() []string {
if m != nil { if m != nil {
return m.Cmd return m.Cmd
} }
return nil return nil
} }
func (m *ContainerConfig) GetEnv() []string { func (m *InvokeConfig) GetEnv() []string {
if m != nil { if m != nil {
return m.Env return m.Env
} }
return nil return nil
} }
func (m *ContainerConfig) GetUser() string { func (m *InvokeConfig) GetUser() string {
if m != nil { if m != nil {
return m.User return m.User
} }
return "" return ""
} }
func (m *ContainerConfig) GetNoUser() bool { func (m *InvokeConfig) GetNoUser() bool {
if m != nil { if m != nil {
return m.NoUser return m.NoUser
} }
return false return false
} }
func (m *ContainerConfig) GetCwd() string { func (m *InvokeConfig) GetCwd() string {
if m != nil { if m != nil {
return m.Cwd return m.Cwd
} }
return "" return ""
} }
func (m *ContainerConfig) GetNoCwd() bool { func (m *InvokeConfig) GetNoCwd() bool {
if m != nil { if m != nil {
return m.NoCwd return m.NoCwd
} }
return false return false
} }
func (m *ContainerConfig) GetTty() bool { func (m *InvokeConfig) GetTty() bool {
if m != nil { if m != nil {
return m.Tty return m.Tty
} }
return false return false
} }
func (m *InvokeConfig) GetRollback() bool {
if m != nil {
return m.Rollback
}
return false
}
type FdMessage struct { type FdMessage struct {
Fd uint32 `protobuf:"varint,1,opt,name=Fd,proto3" json:"Fd,omitempty"` Fd uint32 `protobuf:"varint,1,opt,name=Fd,proto3" json:"Fd,omitempty"`
EOF bool `protobuf:"varint,2,opt,name=EOF,proto3" json:"EOF,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 (m *FdMessage) String() string { return proto.CompactTextString(m) }
func (*FdMessage) ProtoMessage() {} func (*FdMessage) ProtoMessage() {}
func (*FdMessage) Descriptor() ([]byte, []int) { func (*FdMessage) Descriptor() ([]byte, []int) {
return fileDescriptor_ed7f10298fa1d90f, []int{22} return fileDescriptor_ed7f10298fa1d90f, []int{27}
} }
func (m *FdMessage) XXX_Unmarshal(b []byte) error { func (m *FdMessage) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_FdMessage.Unmarshal(m, b) 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 (m *ResizeMessage) String() string { return proto.CompactTextString(m) }
func (*ResizeMessage) ProtoMessage() {} func (*ResizeMessage) ProtoMessage() {}
func (*ResizeMessage) Descriptor() ([]byte, []int) { func (*ResizeMessage) Descriptor() ([]byte, []int) {
return fileDescriptor_ed7f10298fa1d90f, []int{23} return fileDescriptor_ed7f10298fa1d90f, []int{28}
} }
func (m *ResizeMessage) XXX_Unmarshal(b []byte) error { func (m *ResizeMessage) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ResizeMessage.Unmarshal(m, b) 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 (m *SignalMessage) String() string { return proto.CompactTextString(m) }
func (*SignalMessage) ProtoMessage() {} func (*SignalMessage) ProtoMessage() {}
func (*SignalMessage) Descriptor() ([]byte, []int) { func (*SignalMessage) Descriptor() ([]byte, []int) {
return fileDescriptor_ed7f10298fa1d90f, []int{24} return fileDescriptor_ed7f10298fa1d90f, []int{29}
} }
func (m *SignalMessage) XXX_Unmarshal(b []byte) error { func (m *SignalMessage) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_SignalMessage.Unmarshal(m, b) 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 (m *StatusRequest) String() string { return proto.CompactTextString(m) }
func (*StatusRequest) ProtoMessage() {} func (*StatusRequest) ProtoMessage() {}
func (*StatusRequest) Descriptor() ([]byte, []int) { func (*StatusRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_ed7f10298fa1d90f, []int{25} return fileDescriptor_ed7f10298fa1d90f, []int{30}
} }
func (m *StatusRequest) XXX_Unmarshal(b []byte) error { func (m *StatusRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_StatusRequest.Unmarshal(m, b) 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 (m *StatusResponse) String() string { return proto.CompactTextString(m) }
func (*StatusResponse) ProtoMessage() {} func (*StatusResponse) ProtoMessage() {}
func (*StatusResponse) Descriptor() ([]byte, []int) { func (*StatusResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_ed7f10298fa1d90f, []int{26} return fileDescriptor_ed7f10298fa1d90f, []int{31}
} }
func (m *StatusResponse) XXX_Unmarshal(b []byte) error { func (m *StatusResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_StatusResponse.Unmarshal(m, b) 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 (m *InfoRequest) String() string { return proto.CompactTextString(m) }
func (*InfoRequest) ProtoMessage() {} func (*InfoRequest) ProtoMessage() {}
func (*InfoRequest) Descriptor() ([]byte, []int) { func (*InfoRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_ed7f10298fa1d90f, []int{27} return fileDescriptor_ed7f10298fa1d90f, []int{32}
} }
func (m *InfoRequest) XXX_Unmarshal(b []byte) error { func (m *InfoRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_InfoRequest.Unmarshal(m, b) 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 (m *InfoResponse) String() string { return proto.CompactTextString(m) }
func (*InfoResponse) ProtoMessage() {} func (*InfoResponse) ProtoMessage() {}
func (*InfoResponse) Descriptor() ([]byte, []int) { func (*InfoResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_ed7f10298fa1d90f, []int{28} return fileDescriptor_ed7f10298fa1d90f, []int{33}
} }
func (m *InfoResponse) XXX_Unmarshal(b []byte) error { func (m *InfoResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_InfoResponse.Unmarshal(m, b) 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 (m *BuildxVersion) String() string { return proto.CompactTextString(m) }
func (*BuildxVersion) ProtoMessage() {} func (*BuildxVersion) ProtoMessage() {}
func (*BuildxVersion) Descriptor() ([]byte, []int) { func (*BuildxVersion) Descriptor() ([]byte, []int) {
return fileDescriptor_ed7f10298fa1d90f, []int{29} return fileDescriptor_ed7f10298fa1d90f, []int{34}
} }
func (m *BuildxVersion) XXX_Unmarshal(b []byte) error { func (m *BuildxVersion) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_BuildxVersion.Unmarshal(m, b) return xxx_messageInfo_BuildxVersion.Unmarshal(m, b)
@ -1689,6 +1905,11 @@ func (m *BuildxVersion) GetRevision() string {
} }
func init() { 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((*BuildRequest)(nil), "buildx.controller.v1.BuildRequest")
proto.RegisterType((*BuildOptions)(nil), "buildx.controller.v1.BuildOptions") proto.RegisterType((*BuildOptions)(nil), "buildx.controller.v1.BuildOptions")
proto.RegisterMapType((map[string]string)(nil), "buildx.controller.v1.BuildOptions.BuildArgsEntry") 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((*InputResponse)(nil), "buildx.controller.v1.InputResponse")
proto.RegisterType((*Message)(nil), "buildx.controller.v1.Message") proto.RegisterType((*Message)(nil), "buildx.controller.v1.Message")
proto.RegisterType((*InitMessage)(nil), "buildx.controller.v1.InitMessage") 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((*FdMessage)(nil), "buildx.controller.v1.FdMessage")
proto.RegisterType((*ResizeMessage)(nil), "buildx.controller.v1.ResizeMessage") proto.RegisterType((*ResizeMessage)(nil), "buildx.controller.v1.ResizeMessage")
proto.RegisterType((*SignalMessage)(nil), "buildx.controller.v1.SignalMessage") proto.RegisterType((*SignalMessage)(nil), "buildx.controller.v1.SignalMessage")
@ -1731,111 +1952,119 @@ func init() {
func init() { proto.RegisterFile("controller.proto", fileDescriptor_ed7f10298fa1d90f) } func init() { proto.RegisterFile("controller.proto", fileDescriptor_ed7f10298fa1d90f) }
var fileDescriptor_ed7f10298fa1d90f = []byte{ var fileDescriptor_ed7f10298fa1d90f = []byte{
// 1657 bytes of a gzipped FileDescriptorProto // 1790 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x58, 0xdd, 0x6e, 0xdb, 0xc6, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x58, 0xcd, 0x6e, 0x23, 0xc7,
0x12, 0x3e, 0x94, 0x64, 0xfd, 0x8c, 0x2c, 0xc7, 0xd9, 0xe3, 0x04, 0x3c, 0x3a, 0x39, 0x89, 0xc3, 0x11, 0xce, 0x90, 0x14, 0x7f, 0x8a, 0xa2, 0xac, 0xed, 0x68, 0x8d, 0x31, 0xed, 0x78, 0xb5, 0xb3,
0xfc, 0x1c, 0x01, 0x29, 0xe4, 0xc4, 0x69, 0x9a, 0xdf, 0x02, 0xb5, 0x25, 0x0b, 0x76, 0xe1, 0x3f, 0x1b, 0x87, 0xc8, 0x06, 0x94, 0x2d, 0xc7, 0x59, 0xaf, 0x77, 0x03, 0x44, 0xa2, 0x44, 0x48, 0xc6,
0x50, 0x4e, 0x82, 0xb6, 0x40, 0x03, 0x5a, 0x5a, 0xcb, 0x84, 0x28, 0xae, 0xca, 0x5d, 0xc9, 0x56, 0xea, 0x07, 0x4d, 0xed, 0x1a, 0x49, 0x80, 0x18, 0x43, 0xb2, 0x45, 0x0d, 0x38, 0x9c, 0x66, 0xa6,
0xaf, 0x7a, 0xd3, 0xdb, 0xbe, 0x47, 0xd1, 0x47, 0xe8, 0x55, 0xdf, 0xa1, 0x0f, 0xd2, 0x47, 0x28, 0x9b, 0x94, 0x98, 0x53, 0x2e, 0xbe, 0xe6, 0x3d, 0x82, 0x5c, 0x73, 0xcb, 0x29, 0xef, 0x90, 0x07,
0x76, 0x76, 0x49, 0x51, 0x96, 0x28, 0xdb, 0xe8, 0x95, 0x77, 0x86, 0xdf, 0x37, 0xb3, 0x3b, 0x3b, 0xc9, 0x23, 0x04, 0x5d, 0xdd, 0x33, 0x9c, 0x11, 0x39, 0x94, 0x14, 0x9f, 0xd8, 0x55, 0xf3, 0x55,
0x3f, 0x2b, 0xc3, 0x72, 0x8b, 0xf9, 0x22, 0x60, 0x9e, 0x47, 0x83, 0x6a, 0x3f, 0x60, 0x82, 0x91, 0x55, 0x57, 0x75, 0xfd, 0x74, 0x13, 0x36, 0x7b, 0x3c, 0x90, 0x21, 0xf7, 0x7d, 0x16, 0x36, 0xc7,
0x95, 0xe3, 0x81, 0xeb, 0xb5, 0xcf, 0xab, 0xb1, 0x0f, 0xc3, 0x67, 0xe5, 0xb7, 0x1d, 0x57, 0x9c, 0x21, 0x97, 0x9c, 0x6c, 0x75, 0x27, 0x9e, 0xdf, 0xbf, 0x69, 0x26, 0x3e, 0x4c, 0xbf, 0xa8, 0xbf,
0x0e, 0x8e, 0xab, 0x2d, 0xd6, 0x5b, 0xeb, 0xb1, 0xe3, 0xd1, 0x1a, 0xa2, 0xba, 0xae, 0x58, 0x73, 0x1e, 0x78, 0xf2, 0x6a, 0xd2, 0x6d, 0xf6, 0xf8, 0x68, 0x67, 0xc4, 0xbb, 0xb3, 0x1d, 0x44, 0x0d,
0xfa, 0xee, 0x1a, 0xa7, 0xc1, 0xd0, 0x6d, 0x51, 0xbe, 0xa6, 0x49, 0xe1, 0x5f, 0x65, 0xd2, 0xfa, 0x3d, 0xb9, 0xe3, 0x8e, 0xbd, 0x1d, 0xc1, 0xc2, 0xa9, 0xd7, 0x63, 0x62, 0xc7, 0x08, 0x45, 0xbf,
0x1e, 0x16, 0x37, 0x25, 0xdc, 0xa6, 0x3f, 0x0c, 0x28, 0x17, 0x64, 0x19, 0xd2, 0x36, 0x3d, 0x31, 0x5a, 0xa5, 0xd3, 0x80, 0xad, 0xb7, 0x9e, 0x90, 0xe7, 0x21, 0xef, 0x31, 0x21, 0x98, 0xa0, 0xec,
0x8d, 0x55, 0xa3, 0x52, 0xb0, 0xe5, 0x92, 0xbc, 0x83, 0xdc, 0x41, 0x5f, 0xb8, 0xcc, 0xe7, 0x66, 0xcf, 0x13, 0x26, 0x24, 0xd9, 0x84, 0x3c, 0x65, 0x97, 0xb6, 0xb5, 0x6d, 0x35, 0x2a, 0x54, 0x2d,
0x6a, 0xd5, 0xa8, 0x14, 0xd7, 0xad, 0xea, 0xac, 0x6d, 0x54, 0xd1, 0x8c, 0x46, 0xda, 0x21, 0xc5, 0x9d, 0x73, 0x78, 0x7c, 0x0b, 0x29, 0xc6, 0x3c, 0x10, 0x8c, 0xbc, 0x84, 0xb5, 0xe3, 0xe0, 0x92,
0xfa, 0x19, 0xb4, 0x03, 0xad, 0x20, 0xab, 0x50, 0xac, 0x31, 0x5f, 0xd0, 0x73, 0x71, 0xe8, 0x88, 0x0b, 0xdb, 0xda, 0xce, 0x37, 0xaa, 0xbb, 0x4f, 0x9b, 0xcb, 0x76, 0xd9, 0x34, 0x72, 0x0a, 0x49,
0x53, 0xed, 0x28, 0xae, 0x22, 0x8f, 0x61, 0xa9, 0xce, 0x5a, 0x5d, 0x1a, 0x9c, 0xb8, 0x1e, 0xdd, 0x35, 0xde, 0x11, 0x50, 0x4d, 0x70, 0xc9, 0x27, 0x50, 0x89, 0xc8, 0x03, 0x63, 0x78, 0xce, 0x20,
0x77, 0x7a, 0x14, 0xfd, 0x16, 0xec, 0x0b, 0x5a, 0x72, 0x07, 0x0a, 0x87, 0x81, 0xeb, 0x8b, 0xc6, 0x6d, 0x58, 0x3f, 0x0e, 0xa6, 0x7c, 0xc8, 0x5a, 0x3c, 0xb8, 0xf4, 0x06, 0x76, 0x6e, 0xdb, 0x6a,
0xc0, 0x6f, 0x99, 0x69, 0x84, 0x8c, 0x15, 0xe4, 0x3b, 0x28, 0x49, 0x54, 0x5b, 0x5b, 0xe6, 0x66, 0x54, 0x77, 0x9d, 0xe5, 0xc6, 0x92, 0x48, 0x9a, 0x92, 0x73, 0xbe, 0x05, 0xfb, 0xc0, 0x13, 0x3d,
0x66, 0x35, 0x5d, 0x29, 0xae, 0xbf, 0xb8, 0x7c, 0xf3, 0xd5, 0x09, 0xde, 0x96, 0x2f, 0x82, 0x91, 0x1e, 0x04, 0xac, 0x17, 0x39, 0x93, 0xe9, 0x74, 0x7a, 0x4f, 0xb9, 0x5b, 0x7b, 0x72, 0x3e, 0x86,
0x3d, 0x69, 0x8b, 0xac, 0xc0, 0xc2, 0x86, 0xe7, 0xb1, 0x33, 0x73, 0x61, 0x35, 0x5d, 0x29, 0xd8, 0x8f, 0x96, 0xe8, 0xd2, 0x61, 0x71, 0xfe, 0x04, 0xeb, 0xfb, 0x6a, 0x6f, 0xd9, 0xca, 0xdf, 0x40,
0x4a, 0x20, 0x5f, 0x40, 0x6e, 0x43, 0x08, 0xca, 0x05, 0x37, 0xb3, 0xe8, 0xec, 0xce, 0x6c, 0x67, 0xe9, 0x6c, 0x2c, 0x3d, 0x1e, 0x88, 0xd5, 0xde, 0xa0, 0x1a, 0x83, 0xa4, 0x91, 0x88, 0xf3, 0x03,
0x0a, 0x64, 0x87, 0x60, 0x72, 0x00, 0x05, 0xf4, 0xbf, 0x11, 0x74, 0xb8, 0x99, 0x43, 0xe6, 0xb3, 0x18, 0x03, 0x86, 0x41, 0xb6, 0xa1, 0xda, 0xe2, 0x81, 0x64, 0x37, 0xf2, 0xdc, 0x95, 0x57, 0xc6,
0x2b, 0x6c, 0x33, 0xe2, 0xa8, 0x2d, 0x8e, 0x6d, 0x90, 0x2d, 0x28, 0xd4, 0x9c, 0xd6, 0x29, 0x6d, 0x50, 0x92, 0x45, 0x3e, 0x83, 0x8d, 0x03, 0xde, 0x1b, 0xb2, 0xf0, 0xd2, 0xf3, 0xd9, 0xa9, 0x3b,
0x04, 0xac, 0x67, 0xe6, 0xd1, 0xe0, 0xff, 0x67, 0x1b, 0x44, 0x98, 0x36, 0xa8, 0xcd, 0x44, 0x4c, 0x62, 0xc6, 0xa5, 0x5b, 0x5c, 0xed, 0xb5, 0x17, 0xc8, 0xf6, 0x24, 0xe8, 0xd9, 0xf9, 0xc8, 0x6b,
0xb2, 0x01, 0x39, 0x14, 0x8e, 0x98, 0x59, 0xb8, 0x9e, 0x91, 0x90, 0x47, 0x2c, 0x58, 0xac, 0x75, 0xc3, 0x20, 0x7f, 0x84, 0x9a, 0x42, 0xf5, 0x8d, 0x66, 0x61, 0x17, 0xf0, 0xdc, 0xbf, 0xba, 0x7b,
0x02, 0x36, 0xe8, 0x1f, 0x3a, 0x01, 0xf5, 0x85, 0x09, 0x78, 0x4d, 0x13, 0x3a, 0xf2, 0x16, 0x72, 0xf3, 0xcd, 0x94, 0xdc, 0x61, 0x20, 0xc3, 0x19, 0x4d, 0xeb, 0x22, 0x5b, 0xb0, 0xb6, 0xe7, 0xfb,
0x5b, 0xe7, 0x7d, 0x16, 0x08, 0x6e, 0x16, 0xd1, 0xcd, 0xfd, 0xd9, 0x6e, 0x14, 0x48, 0x3b, 0xd0, 0xfc, 0xda, 0x5e, 0xdb, 0xce, 0x37, 0x2a, 0x54, 0x13, 0xe4, 0x37, 0x50, 0xda, 0x93, 0x92, 0x09,
0x0c, 0x72, 0x17, 0x60, 0xeb, 0x5c, 0x04, 0xce, 0x36, 0x93, 0x61, 0x5f, 0xc4, 0xeb, 0x88, 0x69, 0x29, 0xec, 0x22, 0x1a, 0xfb, 0x64, 0xb9, 0x31, 0x0d, 0xa2, 0x11, 0x98, 0x9c, 0x41, 0x05, 0xed,
0x48, 0x03, 0xb2, 0xbb, 0xce, 0x31, 0xf5, 0xb8, 0x59, 0x42, 0xdb, 0xd5, 0x2b, 0x04, 0x56, 0x11, 0xef, 0x85, 0x03, 0x61, 0x97, 0x50, 0xf2, 0x8b, 0x7b, 0x6c, 0x33, 0x96, 0xd1, 0x5b, 0x9c, 0xeb,
0x94, 0x23, 0xcd, 0x96, 0x69, 0xbb, 0x4f, 0xc5, 0x19, 0x0b, 0xba, 0x7b, 0xac, 0x4d, 0xcd, 0x25, 0x20, 0x87, 0x50, 0x69, 0xb9, 0xbd, 0x2b, 0xd6, 0x0e, 0xf9, 0xc8, 0x2e, 0xa3, 0xc2, 0x5f, 0x2c,
0x95, 0xb6, 0x31, 0x15, 0x79, 0x08, 0xa5, 0x7d, 0xa6, 0x82, 0xe7, 0x7a, 0x82, 0x06, 0xe6, 0x0d, 0x57, 0x88, 0x30, 0xa3, 0xd0, 0xa8, 0x89, 0x25, 0xc9, 0x1e, 0x94, 0x90, 0xb8, 0xe0, 0x76, 0xe5,
0xdc, 0xcc, 0xa4, 0x12, 0x93, 0xd6, 0x73, 0xc4, 0x09, 0x0b, 0x7a, 0xdc, 0x5c, 0x46, 0xc4, 0x58, 0x61, 0x4a, 0x22, 0x39, 0xe2, 0xc0, 0x7a, 0x6b, 0x10, 0xf2, 0xc9, 0xf8, 0xdc, 0x0d, 0x59, 0x20,
0x21, 0x33, 0xa8, 0x49, 0x5b, 0x01, 0x15, 0xdc, 0xbc, 0x39, 0x2f, 0x83, 0x14, 0xc8, 0x0e, 0xc1, 0x6d, 0xc0, 0x63, 0x4a, 0xf1, 0xc8, 0x6b, 0x28, 0x1d, 0xde, 0x8c, 0x79, 0x28, 0x85, 0x5d, 0x5d,
0xc4, 0x84, 0x5c, 0xf3, 0xb4, 0xd7, 0x74, 0x7f, 0xa4, 0x26, 0x59, 0x35, 0x2a, 0x69, 0x3b, 0x14, 0x55, 0x9b, 0x1a, 0x64, 0x0c, 0x18, 0x09, 0xf2, 0x29, 0xc0, 0xe1, 0x8d, 0x0c, 0xdd, 0x23, 0xae,
0xc9, 0x13, 0x48, 0x37, 0x9b, 0xdb, 0xe6, 0xbf, 0xd1, 0xda, 0x7f, 0x12, 0xac, 0x35, 0xb7, 0x6d, 0xc2, 0xbe, 0x8e, 0xc7, 0x91, 0xe0, 0x90, 0x36, 0x14, 0xdf, 0xba, 0x5d, 0xe6, 0x0b, 0xbb, 0x86,
0x89, 0x22, 0x04, 0x32, 0x47, 0x4e, 0x87, 0x9b, 0x2b, 0xb8, 0x2f, 0x5c, 0x93, 0xdb, 0x90, 0x3d, 0xba, 0x9b, 0xf7, 0x08, 0xac, 0x16, 0xd0, 0x86, 0x8c, 0xb4, 0x4a, 0xdb, 0x53, 0x26, 0xaf, 0x79,
0x72, 0x82, 0x0e, 0x15, 0xe6, 0x2d, 0x3c, 0xb3, 0x96, 0xc8, 0x6b, 0xc8, 0xbd, 0xf7, 0xdc, 0x9e, 0x38, 0x3c, 0xe1, 0x7d, 0x66, 0x6f, 0xe8, 0xb4, 0x4d, 0xb0, 0xc8, 0x73, 0xa8, 0x9d, 0x72, 0x1d,
0x2b, 0xb8, 0x79, 0x1b, 0xdb, 0xc2, 0xbd, 0xd9, 0xc6, 0x15, 0xe8, 0xa0, 0x2f, 0xec, 0x10, 0x4f, 0x3c, 0xcf, 0x97, 0x2c, 0xb4, 0x3f, 0xc0, 0xcd, 0xa4, 0x99, 0x98, 0xb4, 0xbe, 0x2b, 0x2f, 0x79,
0x5e, 0x42, 0xe6, 0xa0, 0x2f, 0xb8, 0x69, 0x22, 0xef, 0x41, 0x42, 0x52, 0xb1, 0x5e, 0x8f, 0xf9, 0x38, 0x12, 0xf6, 0x26, 0x22, 0xe6, 0x0c, 0x95, 0x41, 0x1d, 0xd6, 0x0b, 0x99, 0x14, 0xf6, 0xa3,
0x61, 0x3f, 0x41, 0x42, 0xf9, 0x2b, 0x20, 0xd3, 0xb5, 0x29, 0x5b, 0x56, 0x97, 0x8e, 0xc2, 0x96, 0x55, 0x19, 0xa4, 0x41, 0x34, 0x02, 0x13, 0x1b, 0x4a, 0x9d, 0xab, 0x51, 0xc7, 0xfb, 0x0b, 0xb3,
0xd5, 0xa5, 0x23, 0x59, 0x9e, 0x43, 0xc7, 0x1b, 0x84, 0x8d, 0x43, 0x09, 0x6f, 0x52, 0xaf, 0x8c, 0xc9, 0xb6, 0xd5, 0xc8, 0xd3, 0x88, 0x24, 0x2f, 0x20, 0xdf, 0xe9, 0x1c, 0xd9, 0x3f, 0x45, 0x6d,
0xf2, 0x3b, 0x58, 0x9a, 0x2c, 0x9b, 0x6b, 0xb1, 0x5f, 0x43, 0x31, 0x96, 0x1b, 0xd7, 0xa1, 0x5a, 0x1f, 0x65, 0x68, 0xeb, 0x1c, 0x51, 0x85, 0x22, 0x04, 0x0a, 0x17, 0xee, 0x40, 0xd8, 0x5b, 0xb8,
0x7f, 0x18, 0x50, 0x8c, 0x25, 0x30, 0x86, 0x7a, 0xd4, 0xa7, 0x9a, 0x8c, 0x6b, 0xb2, 0x09, 0x0b, 0x2f, 0x5c, 0x93, 0x0f, 0xa1, 0x78, 0xe1, 0x86, 0x03, 0x26, 0xed, 0xc7, 0xe8, 0xb3, 0xa1, 0xc8,
0x1b, 0x42, 0x04, 0xb2, 0xcf, 0xca, 0xdb, 0xfa, 0xec, 0xd2, 0x32, 0xa8, 0x22, 0x5c, 0x25, 0xaa, 0x2b, 0x28, 0xbd, 0xf3, 0xbd, 0x91, 0x27, 0x85, 0xfd, 0x21, 0xb6, 0x85, 0x27, 0xcb, 0x95, 0x6b,
0xa2, 0xca, 0x3c, 0xad, 0x53, 0x2e, 0x5c, 0xdf, 0x91, 0x81, 0xd3, 0x6d, 0x31, 0xae, 0x2a, 0xbf, 0xd0, 0xd9, 0x58, 0xd2, 0x08, 0x4f, 0x5e, 0x42, 0xe1, 0x6c, 0x2c, 0x85, 0x6d, 0xa3, 0xdc, 0xb3,
0x02, 0x18, 0xd3, 0xae, 0x75, 0x86, 0xdf, 0x0c, 0xb8, 0x39, 0x55, 0xeb, 0x33, 0x4f, 0xb2, 0x3d, 0x8c, 0xa4, 0xe2, 0xa3, 0x11, 0x0f, 0xa2, 0x7e, 0x82, 0x02, 0xf5, 0xdf, 0x01, 0x59, 0xac, 0x4d,
0x79, 0x92, 0xf5, 0x2b, 0xf6, 0x8d, 0xe9, 0xf3, 0xfc, 0x83, 0xdd, 0xee, 0x43, 0x56, 0x35, 0xd8, 0xd5, 0xb2, 0x86, 0x6c, 0x16, 0xb5, 0xac, 0x21, 0x9b, 0xa9, 0xf2, 0x9c, 0xba, 0xfe, 0x24, 0x6a,
0x99, 0x3b, 0x2c, 0x43, 0xbe, 0xee, 0x72, 0xe7, 0xd8, 0xa3, 0x6d, 0xa4, 0xe6, 0xed, 0x48, 0xc6, 0x1c, 0x9a, 0xf8, 0x26, 0xf7, 0xb5, 0x55, 0x7f, 0x03, 0x1b, 0xe9, 0xb2, 0x79, 0x90, 0xf4, 0x2b,
0xee, 0x8e, 0xbb, 0x57, 0xd1, 0x53, 0x82, 0xa5, 0x2a, 0x89, 0x2c, 0x41, 0x6a, 0xa7, 0xae, 0x4d, 0xa8, 0x26, 0x72, 0xe3, 0x21, 0xa2, 0xce, 0xbf, 0x2d, 0xa8, 0x26, 0x12, 0x18, 0x43, 0x3d, 0x1b,
0xa5, 0x76, 0xea, 0x12, 0x2c, 0xa7, 0x96, 0x3a, 0x6a, 0xc1, 0x56, 0x82, 0xd5, 0x80, 0xac, 0xaa, 0x33, 0x23, 0x8c, 0x6b, 0xb2, 0x0f, 0x6b, 0x7b, 0x52, 0x86, 0xaa, 0xcf, 0xaa, 0xd3, 0xfa, 0xd5,
0xcd, 0x29, 0x7c, 0x19, 0xf2, 0x0d, 0xd7, 0xa3, 0x38, 0xfc, 0xd4, 0x9e, 0x23, 0x59, 0x1e, 0x6f, 0x9d, 0x65, 0xd0, 0x44, 0xb8, 0x4e, 0x54, 0x2d, 0xaa, 0xf2, 0xf4, 0x80, 0x09, 0xe9, 0x05, 0xae,
0xcb, 0x1f, 0x6a, 0xb7, 0x72, 0x69, 0xfd, 0x6a, 0x40, 0x21, 0xaa, 0x20, 0x52, 0x83, 0x2c, 0x9e, 0x0a, 0x9c, 0x69, 0x8b, 0x49, 0x56, 0xfd, 0x6b, 0x80, 0xb9, 0xd8, 0x83, 0x7c, 0xf8, 0x87, 0x05,
0x8f, 0x9b, 0x06, 0xc6, 0xf5, 0xc9, 0x25, 0x25, 0x57, 0xfd, 0x80, 0x68, 0xdd, 0xc9, 0x14, 0xb5, 0x8f, 0x16, 0x6a, 0x7d, 0xa9, 0x27, 0x47, 0x69, 0x4f, 0x76, 0xef, 0xd9, 0x37, 0x16, 0xfd, 0xf9,
0xfc, 0x11, 0x8a, 0x31, 0xf5, 0x8c, 0x90, 0xae, 0xc7, 0x43, 0x9a, 0xd8, 0x82, 0x94, 0x93, 0x78, 0x11, 0xbb, 0x3d, 0x85, 0xa2, 0x6e, 0xb0, 0x4b, 0x77, 0x58, 0x87, 0xf2, 0x81, 0x27, 0xdc, 0xae,
0xc0, 0xeb, 0x90, 0x55, 0x4a, 0x19, 0x70, 0x9c, 0xdb, 0x3a, 0xe0, 0x38, 0xad, 0x09, 0x64, 0xb6, 0xcf, 0xfa, 0x28, 0x5a, 0xa6, 0x31, 0x8d, 0xdd, 0x1d, 0x77, 0xaf, 0xa3, 0xa7, 0x09, 0x47, 0x57,
0x9d, 0x40, 0x05, 0x3b, 0x6d, 0xe3, 0x5a, 0xea, 0x9a, 0xec, 0x44, 0xe0, 0x81, 0xd3, 0x36, 0xae, 0x12, 0xd9, 0x80, 0x5c, 0x3c, 0xf8, 0x73, 0xc7, 0x07, 0x0a, 0xac, 0xa6, 0x96, 0x76, 0xb5, 0x42,
0xad, 0xdf, 0x0d, 0x28, 0x4d, 0xd4, 0xbe, 0x6c, 0x6e, 0x58, 0xb3, 0x34, 0xd0, 0x06, 0x43, 0x51, 0x35, 0xe1, 0xb4, 0xa1, 0xa8, 0x6b, 0x73, 0x01, 0x5f, 0x87, 0x72, 0xdb, 0xf3, 0x19, 0x0e, 0x3f,
0x4e, 0x97, 0x3d, 0x2a, 0x9c, 0xb6, 0x23, 0x1c, 0x19, 0x43, 0x1d, 0xcf, 0x09, 0x9d, 0x64, 0xeb, 0xbd, 0xe7, 0x98, 0x56, 0xee, 0x1d, 0x06, 0x53, 0x63, 0x56, 0x2d, 0x9d, 0xbf, 0x5b, 0x50, 0x89,
0x0e, 0x8c, 0x6e, 0xf2, 0x76, 0x28, 0x4a, 0xef, 0x87, 0x03, 0xcf, 0x33, 0x33, 0xa8, 0xc6, 0xb5, 0x2b, 0x88, 0xb4, 0xa0, 0x88, 0xfe, 0x45, 0x97, 0x98, 0x17, 0x77, 0x94, 0x5c, 0xf3, 0x3d, 0xa2,
0x1a, 0x27, 0xb2, 0xbe, 0x0e, 0x07, 0xfc, 0xd4, 0x5c, 0xc0, 0x2f, 0x31, 0xcd, 0xf8, 0xfb, 0x2e, 0x4d, 0x27, 0xd3, 0xa2, 0xf5, 0xef, 0xa0, 0x9a, 0x60, 0x2f, 0x09, 0xe9, 0x6e, 0x32, 0xa4, 0x99,
0x73, 0xda, 0x66, 0x36, 0xfe, 0x5d, 0x6a, 0x70, 0xf7, 0xfa, 0x3d, 0xc5, 0xfb, 0xcc, 0xe7, 0x94, 0x2d, 0x48, 0x1b, 0x49, 0x06, 0xfc, 0x00, 0x8a, 0x9a, 0xa9, 0x02, 0x8e, 0x73, 0xdb, 0x04, 0x1c,
0x50, 0x58, 0x56, 0xdf, 0x69, 0x10, 0xea, 0xf4, 0xed, 0xbd, 0x9e, 0x33, 0x8a, 0x42, 0x68, 0xf5, 0xa7, 0x35, 0x81, 0xc2, 0x91, 0x1b, 0xea, 0x60, 0xe7, 0x29, 0xae, 0x15, 0xaf, 0xc3, 0x2f, 0x25,
0x22, 0x57, 0xdd, 0xe5, 0x94, 0xc9, 0x72, 0x0d, 0x6e, 0xcd, 0x84, 0x5e, 0xab, 0x64, 0x1e, 0xc1, 0x3a, 0x9c, 0xa7, 0xb8, 0x76, 0xfe, 0x65, 0x41, 0x2d, 0x55, 0xfb, 0xaa, 0xb9, 0x61, 0xcd, 0xb2,
0xcd, 0xba, 0xcb, 0x5b, 0xcc, 0xf7, 0x69, 0x4b, 0x24, 0xbe, 0x08, 0xad, 0x15, 0x20, 0x71, 0x98, 0xd0, 0x28, 0x8c, 0x48, 0x35, 0x5d, 0x4e, 0x98, 0x74, 0xfb, 0xae, 0x74, 0x55, 0x0c, 0x4d, 0x3c,
0xf2, 0x66, 0xdd, 0x83, 0xe2, 0xae, 0xcb, 0xe7, 0xd0, 0x2c, 0x58, 0x54, 0x00, 0x1d, 0x19, 0x02, 0x53, 0x3c, 0x25, 0x6d, 0x3a, 0x30, 0x9a, 0x29, 0xd3, 0x88, 0x54, 0xd6, 0xcf, 0x27, 0xbe, 0x6f,
0x99, 0x2e, 0x1d, 0xa9, 0x5c, 0x2e, 0xd8, 0xb8, 0xb6, 0x7e, 0x31, 0x60, 0x71, 0xc7, 0xef, 0x0f, 0x17, 0x90, 0x8d, 0x6b, 0x3d, 0x4e, 0x54, 0x7d, 0x9d, 0x4f, 0xc4, 0x95, 0xbd, 0x86, 0x5f, 0x12,
0xc4, 0x1e, 0xe5, 0xdc, 0xe9, 0x50, 0xf2, 0x0e, 0x32, 0x3b, 0xbe, 0x2b, 0xd0, 0x4e, 0x71, 0xfd, 0x9c, 0xf9, 0xf7, 0xb7, 0xdc, 0xed, 0xdb, 0xc5, 0xe4, 0x77, 0xc5, 0xc1, 0xdd, 0x9b, 0xfb, 0x94,
0xf1, 0xec, 0x90, 0x21, 0x43, 0xc2, 0x34, 0x6b, 0xfb, 0x5f, 0x36, 0xb2, 0xe4, 0xa4, 0xa9, 0x3b, 0xb9, 0x77, 0x32, 0xd8, 0xd4, 0xdf, 0x59, 0x18, 0xf1, 0xcc, 0xe9, 0xbd, 0x5a, 0x31, 0x8a, 0x22,
0xc2, 0xd1, 0x99, 0x9c, 0xf0, 0xae, 0x90, 0x88, 0x18, 0x51, 0x8a, 0x9b, 0x39, 0x58, 0x40, 0xa3, 0x68, 0xf3, 0xb6, 0xac, 0x3e, 0xcb, 0x05, 0x95, 0xf5, 0x16, 0x3c, 0x5e, 0x0a, 0x7d, 0x50, 0xc9,
0xd6, 0x43, 0x58, 0xbe, 0x68, 0x7d, 0xc6, 0xd1, 0x9e, 0x43, 0x31, 0x66, 0x05, 0xeb, 0xf8, 0xa0, 0xfc, 0x1c, 0x1e, 0xcd, 0x6f, 0x8a, 0xd9, 0x77, 0xec, 0x2d, 0x20, 0x49, 0x98, 0xb9, 0x49, 0x3e,
0x81, 0x80, 0xbc, 0x2d, 0x97, 0xf2, 0xac, 0xd1, 0x46, 0x16, 0x95, 0x0f, 0xeb, 0x06, 0x94, 0xd0, 0x81, 0xaa, 0xba, 0x79, 0x67, 0x8b, 0x39, 0xb0, 0xae, 0x01, 0x26, 0x32, 0x04, 0x0a, 0x43, 0x36,
0x74, 0x14, 0xc1, 0x9f, 0x52, 0x90, 0x0b, 0x4d, 0xbc, 0x9c, 0x38, 0xf7, 0xfd, 0xa4, 0x73, 0x4f, 0xd3, 0xb9, 0x5c, 0xa1, 0xb8, 0x76, 0xfe, 0x66, 0xa9, 0x0b, 0xf4, 0x78, 0x22, 0x4f, 0x98, 0x10,
0x1f, 0xf9, 0x05, 0x64, 0xa2, 0x5a, 0x48, 0x1c, 0xca, 0x8d, 0x76, 0x8c, 0x86, 0x65, 0xf2, 0x25, 0xee, 0x80, 0x91, 0x37, 0x50, 0x38, 0x0e, 0x3c, 0x89, 0x7a, 0xaa, 0xbb, 0x9f, 0x65, 0x5d, 0xa4,
0x64, 0x6d, 0xca, 0xe5, 0x03, 0x22, 0x3d, 0x6f, 0x2a, 0x2b, 0xcc, 0x98, 0xac, 0x49, 0x92, 0xde, 0xc7, 0x13, 0xa9, 0x60, 0x46, 0xea, 0xe8, 0x27, 0x14, 0xa5, 0xd4, 0xa4, 0x39, 0x70, 0xa5, 0x6b,
0x74, 0x3b, 0xbe, 0xa3, 0xaa, 0x29, 0x91, 0xae, 0x30, 0x31, 0xba, 0x52, 0x8c, 0xc3, 0xdd, 0x87, 0x32, 0x39, 0xe3, 0x5e, 0xa1, 0x10, 0x09, 0x41, 0x45, 0xee, 0x97, 0xd4, 0x6b, 0x61, 0x3c, 0x91,
0xe2, 0xdc, 0x48, 0x93, 0x03, 0xb8, 0x21, 0xa7, 0xbf, 0xe3, 0xfa, 0x34, 0xa8, 0x31, 0xff, 0xc4, 0xce, 0x73, 0xd8, 0xbc, 0xad, 0x7d, 0x89, 0x6b, 0x5f, 0x42, 0x35, 0xa1, 0x05, 0xeb, 0xf8, 0xac,
0xed, 0xe8, 0x93, 0x3e, 0x4a, 0x7a, 0x46, 0x4c, 0x80, 0xed, 0x8b, 0x6c, 0x59, 0xb1, 0x17, 0x75, 0x8d, 0x80, 0x32, 0x55, 0x4b, 0xe5, 0x6b, 0xbc, 0x91, 0x75, 0x6d, 0xc3, 0xf9, 0x00, 0x6a, 0xa8,
0x58, 0xe5, 0xb2, 0x78, 0xfa, 0xcc, 0xf5, 0x85, 0xce, 0xcf, 0x98, 0x46, 0x6e, 0xab, 0xd6, 0x6b, 0x3a, 0x8e, 0xe0, 0x5f, 0x73, 0x50, 0x8a, 0x54, 0xbc, 0x4c, 0xf9, 0xfd, 0x34, 0xcb, 0xef, 0x45,
0xeb, 0x8e, 0x2f, 0x97, 0xe3, 0xce, 0x9d, 0xd6, 0x9d, 0x5b, 0xde, 0xf8, 0x7b, 0x4e, 0x03, 0x8c, 0x97, 0xbf, 0x82, 0x42, 0x5c, 0x0b, 0x99, 0x43, 0xb9, 0xdd, 0x4f, 0x88, 0x61, 0x99, 0xfc, 0x16,
0x47, 0xc1, 0xc6, 0xb5, 0x7c, 0x4b, 0xed, 0x33, 0xd4, 0xaa, 0xce, 0xa2, 0x25, 0xb4, 0x77, 0xa6, 0x8a, 0x94, 0x09, 0x75, 0x81, 0xc8, 0xaf, 0x9a, 0xca, 0x1a, 0x33, 0x17, 0x36, 0x42, 0x4a, 0xbc,
0xda, 0x89, 0xb4, 0x77, 0x86, 0x23, 0x68, 0x9f, 0x49, 0x5d, 0x0e, 0x81, 0x4a, 0x90, 0xb8, 0x23, 0xe3, 0x0d, 0x02, 0x57, 0x57, 0x53, 0xa6, 0xb8, 0xc6, 0x24, 0xc4, 0x35, 0x63, 0x1e, 0xee, 0x1f,
0x31, 0x32, 0xf3, 0x2a, 0xaf, 0x8e, 0xc4, 0xc8, 0xda, 0x80, 0x42, 0x74, 0x97, 0x72, 0xd4, 0x34, 0x2c, 0xa8, 0xae, 0x0c, 0xf5, 0xea, 0xb7, 0xce, 0xc2, 0xfb, 0x2b, 0xff, 0x7f, 0xbe, 0xbf, 0xfe,
0xda, 0x18, 0xac, 0x92, 0x9d, 0x6a, 0xb4, 0xc3, 0x34, 0x4c, 0x4d, 0xa7, 0x61, 0x3a, 0x96, 0x86, 0x63, 0xa5, 0x15, 0x61, 0xe1, 0xab, 0x7a, 0x1a, 0x73, 0x2f, 0x90, 0x26, 0x65, 0x13, 0x1c, 0xb5,
0x2f, 0xa1, 0x34, 0x71, 0xab, 0x12, 0x64, 0xb3, 0x33, 0xae, 0x0d, 0xe1, 0x5a, 0xea, 0x6a, 0xcc, 0xd1, 0xd6, 0xa8, 0x6f, 0x86, 0x80, 0x5a, 0xce, 0x9b, 0x79, 0xde, 0x34, 0x73, 0x95, 0x04, 0xef,
0x53, 0xbf, 0x00, 0x4b, 0x36, 0xae, 0xad, 0x07, 0x50, 0x9a, 0xb8, 0xcf, 0x59, 0x6d, 0xdf, 0xba, 0x04, 0x0b, 0x31, 0x44, 0x15, 0x8a, 0x6b, 0x75, 0xbd, 0x3a, 0xe5, 0xc8, 0xd5, 0xcd, 0xc6, 0x50,
0x0f, 0xa5, 0xa6, 0x70, 0xc4, 0x80, 0x27, 0xf7, 0x85, 0xbf, 0x0c, 0x58, 0x0a, 0x31, 0xba, 0x35, 0xa8, 0xef, 0x5a, 0x77, 0x18, 0xa5, 0xef, 0x1a, 0xa7, 0xd2, 0x29, 0x57, 0xbc, 0x12, 0x02, 0x35,
0x7c, 0x0e, 0xf9, 0x21, 0x0d, 0x04, 0x3d, 0x8f, 0x46, 0x9d, 0x59, 0x95, 0x3f, 0x6d, 0xab, 0xe1, 0xa1, 0x70, 0x17, 0x72, 0x66, 0x97, 0x75, 0xaa, 0x5d, 0xc8, 0x99, 0x1a, 0x30, 0x94, 0xfb, 0x7e,
0x4f, 0x5b, 0x79, 0xb5, 0x1f, 0x10, 0x61, 0x47, 0x48, 0xf2, 0x06, 0xf2, 0x1c, 0xed, 0xd0, 0xf0, 0xd7, 0xed, 0x0d, 0xed, 0x8a, 0x9e, 0x6c, 0x11, 0xed, 0xec, 0x41, 0x25, 0x3e, 0x7a, 0x35, 0x99,
0xe1, 0x71, 0x37, 0x89, 0xa5, 0xfd, 0x45, 0x78, 0xb2, 0x06, 0x19, 0x8f, 0x75, 0x38, 0xde, 0x60, 0xda, 0x7d, 0x0c, 0x6d, 0x8d, 0xe6, 0xda, 0xfd, 0x28, 0x6b, 0x73, 0x8b, 0x59, 0x9b, 0x4f, 0x64,
0x71, 0xfd, 0xbf, 0x49, 0xbc, 0x5d, 0xd6, 0xb1, 0x11, 0x48, 0xde, 0x42, 0xfe, 0xcc, 0x09, 0x7c, 0xed, 0x4b, 0xa8, 0xa5, 0x92, 0x40, 0x81, 0x28, 0xbf, 0x16, 0x46, 0x11, 0xae, 0x15, 0xaf, 0xc5,
0xd7, 0xef, 0x84, 0x3f, 0x2d, 0xef, 0x25, 0x91, 0x3e, 0x2a, 0x9c, 0x1d, 0x11, 0xac, 0x92, 0x4c, 0x7d, 0xfd, 0x60, 0xac, 0x51, 0x5c, 0x3b, 0xcf, 0xa0, 0x96, 0x3a, 0xfe, 0x65, 0x53, 0xc2, 0x79,
0xf3, 0x13, 0xa6, 0x63, 0x62, 0x7d, 0x23, 0x9b, 0x9e, 0x14, 0xf5, 0xf1, 0x77, 0xa0, 0xa4, 0x92, 0x0a, 0xb5, 0x8e, 0x74, 0xe5, 0x64, 0xc5, 0x0b, 0xff, 0xbf, 0x16, 0x6c, 0x44, 0x18, 0xd3, 0x49,
0xf9, 0x03, 0x0d, 0xb8, 0x7c, 0xc6, 0x19, 0xf3, 0x8a, 0x6a, 0x33, 0x0e, 0xb5, 0x27, 0x99, 0xd6, 0x7e, 0x0d, 0xe5, 0x29, 0x0b, 0x25, 0xbb, 0x89, 0x27, 0xa3, 0xdd, 0x1c, 0xf1, 0xee, 0xac, 0x19,
0x27, 0x3d, 0x8f, 0x42, 0x85, 0x9c, 0x87, 0x7d, 0xa7, 0xd5, 0x75, 0x3a, 0xe1, 0x3d, 0x85, 0xa2, 0xfd, 0xc7, 0xa0, 0x4e, 0xfb, 0x3d, 0x22, 0x68, 0x8c, 0x24, 0xdf, 0x40, 0x59, 0xa0, 0x1e, 0x16,
0xfc, 0x32, 0xd4, 0xfe, 0xd4, 0x64, 0x08, 0x45, 0xf9, 0x66, 0x09, 0xe8, 0xd0, 0xe5, 0xe3, 0x17, 0xdd, 0x53, 0x3e, 0xcd, 0x92, 0x32, 0xf6, 0x62, 0x3c, 0xd9, 0x81, 0x82, 0xcf, 0x07, 0x02, 0x4f,
0x65, 0x24, 0xaf, 0xff, 0x99, 0x01, 0xa8, 0x45, 0xfb, 0x21, 0x87, 0xb0, 0x80, 0xfe, 0x88, 0x35, 0xb7, 0xba, 0xfb, 0x71, 0x96, 0xdc, 0x5b, 0x3e, 0xa0, 0x08, 0x24, 0xaf, 0xa1, 0x7c, 0xed, 0x86,
0x77, 0xba, 0xe1, 0xb9, 0xcb, 0x0f, 0xae, 0x30, 0x01, 0xc9, 0x7b, 0xc8, 0xaa, 0xdb, 0x22, 0x49, 0x81, 0x17, 0x0c, 0xa2, 0x97, 0xe8, 0x93, 0x2c, 0xa1, 0xef, 0x34, 0x8e, 0xc6, 0x02, 0x4e, 0x4d,
0x4d, 0x25, 0x9e, 0x5f, 0xe5, 0x87, 0xf3, 0x41, 0xca, 0xe8, 0x53, 0x83, 0xd8, 0xba, 0xe5, 0x24, 0x15, 0xc5, 0x25, 0x37, 0x31, 0x71, 0x7e, 0xaf, 0x72, 0x53, 0x91, 0xc6, 0xfd, 0x63, 0xa8, 0xe9,
0x6d, 0x34, 0x3e, 0x85, 0x92, 0x36, 0x3a, 0xd1, 0xbe, 0x2b, 0x06, 0xf9, 0x1a, 0xb2, 0x3b, 0xfe, 0xfc, 0x7e, 0xcf, 0x42, 0xa1, 0x6e, 0x7d, 0xd6, 0xaa, 0x1a, 0xdc, 0x4f, 0x42, 0x69, 0x5a, 0xd2,
0x90, 0x75, 0x29, 0xf9, 0xdf, 0x6c, 0x42, 0x68, 0x6f, 0xfe, 0xe7, 0x8a, 0xf1, 0xd4, 0x20, 0x7b, 0xf9, 0xde, 0x8c, 0xaf, 0x88, 0xa1, 0xc6, 0xe7, 0xd8, 0xed, 0x0d, 0xdd, 0x41, 0x74, 0x4e, 0x11,
0x90, 0x91, 0xd3, 0x92, 0x24, 0xb4, 0xfe, 0xd8, 0xa8, 0x2d, 0x5b, 0xf3, 0x20, 0x3a, 0x8a, 0x9f, 0xa9, 0xbe, 0x4c, 0x8d, 0x3d, 0x5d, 0x86, 0x11, 0xa9, 0x32, 0x30, 0x64, 0x53, 0x4f, 0xcc, 0x2f,
0x00, 0xc6, 0x33, 0x9b, 0x24, 0xfc, 0x90, 0x9f, 0x1a, 0xfe, 0xe5, 0xca, 0xe5, 0x40, 0xed, 0x60, 0xa0, 0x31, 0xbd, 0xfb, 0xcf, 0x22, 0x40, 0x2b, 0xde, 0x0f, 0x39, 0x87, 0x35, 0xb4, 0x47, 0x9c,
0x4f, 0x0e, 0xac, 0x13, 0x46, 0x12, 0x47, 0x55, 0x94, 0xee, 0x65, 0x6b, 0x1e, 0x44, 0x99, 0xdb, 0x95, 0xc3, 0x10, 0xfd, 0xae, 0x3f, 0xbb, 0xc7, 0xc0, 0x24, 0xef, 0xa0, 0xa8, 0x4f, 0x8b, 0x64,
0xcc, 0x7c, 0x9b, 0xea, 0x1f, 0x1f, 0x67, 0xf1, 0x9f, 0x54, 0xcf, 0xff, 0x0e, 0x00, 0x00, 0xff, 0xf5, 0xa0, 0x64, 0x7e, 0xd5, 0x9f, 0xaf, 0x06, 0x69, 0xa5, 0x9f, 0x5b, 0x84, 0x9a, 0x0e, 0x45,
0xff, 0xf4, 0x2e, 0xaa, 0xc4, 0x0b, 0x13, 0x00, 0x00, 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. // 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) List(ctx context.Context, in *ListRequest, opts ...grpc.CallOption) (*ListResponse, error)
Disconnect(ctx context.Context, in *DisconnectRequest, opts ...grpc.CallOption) (*DisconnectResponse, error) Disconnect(ctx context.Context, in *DisconnectRequest, opts ...grpc.CallOption) (*DisconnectResponse, error)
Info(ctx context.Context, in *InfoRequest, opts ...grpc.CallOption) (*InfoResponse, 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 { type controllerClient struct {
@ -2000,6 +2231,24 @@ func (c *controllerClient) Info(ctx context.Context, in *InfoRequest, opts ...gr
return out, nil 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. // ControllerServer is the server API for Controller service.
type ControllerServer interface { type ControllerServer interface {
Build(context.Context, *BuildRequest) (*BuildResponse, error) Build(context.Context, *BuildRequest) (*BuildResponse, error)
@ -2009,6 +2258,8 @@ type ControllerServer interface {
List(context.Context, *ListRequest) (*ListResponse, error) List(context.Context, *ListRequest) (*ListResponse, error)
Disconnect(context.Context, *DisconnectRequest) (*DisconnectResponse, error) Disconnect(context.Context, *DisconnectRequest) (*DisconnectResponse, error)
Info(context.Context, *InfoRequest) (*InfoResponse, 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. // 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) { func (*UnimplementedControllerServer) Info(ctx context.Context, req *InfoRequest) (*InfoResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Info not implemented") 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) { func RegisterControllerServer(s *grpc.Server, srv ControllerServer) {
s.RegisterService(&_Controller_serviceDesc, srv) 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) 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{ var _Controller_serviceDesc = grpc.ServiceDesc{
ServiceName: "buildx.controller.v1.Controller", ServiceName: "buildx.controller.v1.Controller",
HandlerType: (*ControllerServer)(nil), HandlerType: (*ControllerServer)(nil),
@ -2206,6 +2499,14 @@ var _Controller_serviceDesc = grpc.ServiceDesc{
MethodName: "Info", MethodName: "Info",
Handler: _Controller_Info_Handler, Handler: _Controller_Info_Handler,
}, },
{
MethodName: "ListProcesses",
Handler: _Controller_ListProcesses_Handler,
},
{
MethodName: "DisconnectProcess",
Handler: _Controller_DisconnectProcess_Handler,
},
}, },
Streams: []grpc.StreamDesc{ Streams: []grpc.StreamDesc{
{ {

View File

@ -14,6 +14,29 @@ service Controller {
rpc List(ListRequest) returns (ListResponse); rpc List(ListRequest) returns (ListResponse);
rpc Disconnect(DisconnectRequest) returns (DisconnectResponse); rpc Disconnect(DisconnectRequest) returns (DisconnectResponse);
rpc Info(InfoRequest) returns (InfoResponse); 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 { message BuildRequest {
@ -149,10 +172,14 @@ message Message {
message InitMessage { message InitMessage {
string Ref = 1; 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 Entrypoint = 1;
repeated string Cmd = 2; repeated string Cmd = 2;
repeated string Env = 3; repeated string Env = 3;
@ -161,6 +188,7 @@ message ContainerConfig {
string Cwd = 6; string Cwd = 6;
bool NoCwd = 7; // Do not set cwd but use the image's default bool NoCwd = 7; // Do not set cwd but use the image's default
bool Tty = 8; bool Tty = 8;
bool Rollback = 9; // Kill all process in the container and recreate it.
} }
message FdMessage { message FdMessage {

View File

@ -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
}

View File

@ -75,15 +75,28 @@ func (c *Client) Disconnect(ctx context.Context, key string) error {
return err return err
} }
func (c *Client) Invoke(ctx context.Context, ref string, containerConfig pb.ContainerConfig, in io.ReadCloser, stdout io.WriteCloser, stderr io.WriteCloser) error { func (c *Client) ListProcesses(ctx context.Context, ref string) (infos []*pb.ProcessInfo, retErr error) {
if ref == "" { 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") return errors.New("build reference must be specified")
} }
stream, err := c.client().Invoke(ctx) stream, err := c.client().Invoke(ctx)
if err != nil { if err != nil {
return err 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, stdin: in,
stdout: stdout, stdout: stdout,
stderr: stderr, stderr: stderr,

View File

@ -4,16 +4,17 @@ import (
"context" "context"
"io" "io"
"sync" "sync"
"sync/atomic"
"time" "time"
"github.com/docker/buildx/build" "github.com/docker/buildx/build"
"github.com/docker/buildx/controller/pb" "github.com/docker/buildx/controller/pb"
"github.com/docker/buildx/controller/processes"
"github.com/docker/buildx/util/ioset" "github.com/docker/buildx/util/ioset"
"github.com/docker/buildx/version" "github.com/docker/buildx/version"
controlapi "github.com/moby/buildkit/api/services/control" controlapi "github.com/moby/buildkit/api/services/control"
"github.com/moby/buildkit/client" "github.com/moby/buildkit/client"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/sirupsen/logrus"
"golang.org/x/sync/errgroup" "golang.org/x/sync/errgroup"
) )
@ -27,16 +28,47 @@ func NewServer(buildFunc BuildFunc) *Server {
type Server struct { type Server struct {
buildFunc BuildFunc buildFunc BuildFunc
session map[string]session session map[string]*session
sessionMu sync.Mutex sessionMu sync.Mutex
} }
type session struct { type session struct {
statusChan chan *client.SolveStatus buildOnGoing atomic.Bool
result *build.ResultContext statusChan chan *client.SolveStatus
inputPipe *io.PipeWriter cancelBuild func()
curInvokeCancel func() inputPipe *io.PipeWriter
curBuildCancel func()
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) { 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() m.sessionMu.Lock()
if s, ok := m.session[key]; ok { if s, ok := m.session[key]; ok {
if s.curBuildCancel != nil { if s.cancelBuild != nil {
s.curBuildCancel() s.cancelBuild()
}
if s.curInvokeCancel != nil {
s.curInvokeCancel()
} }
s.cancelRunningProcesses()
} }
delete(m.session, key) delete(m.session, key)
m.sessionMu.Unlock() m.sessionMu.Unlock()
@ -92,12 +122,10 @@ func (m *Server) Close() error {
m.sessionMu.Lock() m.sessionMu.Lock()
for k := range m.session { for k := range m.session {
if s, ok := m.session[k]; ok { if s, ok := m.session[k]; ok {
if s.curBuildCancel != nil { if s.cancelBuild != nil {
s.curBuildCancel() s.cancelBuild()
}
if s.curInvokeCancel != nil {
s.curInvokeCancel()
} }
s.cancelRunningProcesses()
} }
} }
m.sessionMu.Unlock() 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") return nil, errors.New("build: empty key")
} }
// Prepare status channel and session if not exists // Prepare status channel and session
m.sessionMu.Lock() m.sessionMu.Lock()
if m.session == nil { if m.session == nil {
m.session = make(map[string]session) m.session = make(map[string]*session)
} }
s, ok := m.session[ref] s, ok := m.session[ref]
if ok && m.session[ref].statusChan != nil { if ok {
m.sessionMu.Unlock() if !s.buildOnGoing.CompareAndSwap(false, true) {
return &pb.BuildResponse{}, errors.New("build or status ongoing or status didn't call") 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) statusChan := make(chan *client.SolveStatus)
s.statusChan = statusChan 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() m.sessionMu.Unlock()
defer func() { defer func() {
close(statusChan) close(statusChan)
@ -130,23 +169,11 @@ func (m *Server) Build(ctx context.Context, req *pb.BuildRequest) (*pb.BuildResp
s, ok := m.session[ref] s, ok := m.session[ref]
if ok { if ok {
s.statusChan = nil s.statusChan = nil
s.buildOnGoing.Store(false)
} }
m.sessionMu.Unlock() 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 // Build the specified request
ctx, cancel := context.WithCancel(ctx) ctx, cancel := context.WithCancel(ctx)
defer cancel() defer cancel()
@ -154,7 +181,7 @@ func (m *Server) Build(ctx context.Context, req *pb.BuildRequest) (*pb.BuildResp
m.sessionMu.Lock() m.sessionMu.Lock()
if s, ok := m.session[ref]; ok { if s, ok := m.session[ref]; ok {
s.result = res s.result = res
s.curBuildCancel = cancel s.cancelBuild = cancel
m.session[ref] = s m.session[ref] = s
} else { } else {
m.sessionMu.Unlock() 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 { func (m *Server) Invoke(srv pb.Controller_InvokeServer) error {
ctx, cancel := context.WithCancel(context.TODO())
defer cancel()
containerIn, containerOut := ioset.Pipe() containerIn, containerOut := ioset.Pipe()
waitInvokeDoneCh := make(chan struct{}) defer func() { containerOut.Close(); containerIn.Close() }()
var cancelOnce sync.Once
curInvokeCancel := func() {
cancelOnce.Do(func() { containerOut.Close(); containerIn.Close(); cancel() })
<-waitInvokeDoneCh
}
defer curInvokeCancel()
var cfg *pb.ContainerConfig initDoneCh := make(chan *processes.Process)
var resultCtx *build.ResultContext
initDoneCh := make(chan struct{})
initErrCh := make(chan error) initErrCh := make(chan error)
eg, egCtx := errgroup.WithContext(ctx) eg, egCtx := errgroup.WithContext(context.TODO())
srvIOCtx, srvIOCancel := context.WithCancel(egCtx)
eg.Go(func() error { 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() { defer func() {
if retErr != nil { if retErr != nil {
initErrCh <- retErr initErrCh <- retErr
} }
close(initDoneCh)
}() }()
ref := initMessage.Ref ref := initMessage.Ref
cfg = initMessage.ContainerConfig cfg := initMessage.InvokeConfig
// Register cancel callback
m.sessionMu.Lock() m.sessionMu.Lock()
if s, ok := m.session[ref]; ok { s, ok := m.session[ref]
if cancel := s.curInvokeCancel; cancel != nil { if !ok {
logrus.Warnf("invoke: cancelling ongoing invoke of %q", ref)
cancel()
}
s.curInvokeCancel = curInvokeCancel
m.session[ref] = s
} else {
m.sessionMu.Unlock() m.sessionMu.Unlock()
return errors.Errorf("invoke: unknown key %v", ref) return errors.Errorf("invoke: unknown key %v", ref)
} }
m.sessionMu.Unlock() m.sessionMu.Unlock()
// Get the target result to invoke a container from pid := initMessage.ProcessID
m.sessionMu.Lock() if pid == "" {
if _, ok := m.session[ref]; !ok || m.session[ref].result == nil { return errors.Errorf("invoke: specify process ID")
m.sessionMu.Unlock()
return errors.Errorf("unknown reference: %q", ref)
} }
resultCtx = m.session[ref].result proc, ok := s.processes.Get(pid)
m.sessionMu.Unlock() 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 return nil
}, &ioServerConfig{ }, &ioServerConfig{
stdin: containerOut.Stdin, stdin: containerOut.Stdin,
@ -356,43 +378,31 @@ func (m *Server) Invoke(srv pb.Controller_InvokeServer) error {
// TODO: signal, resize // TODO: signal, resize
}) })
}) })
eg.Go(func() error { eg.Go(func() (rErr error) {
defer containerIn.Close() defer srvIOCancel()
defer cancel() // Wait for init done
var proc *processes.Process
select { select {
case <-initDoneCh: case p := <-initDoneCh:
proc = p
case err := <-initErrCh: case err := <-initErrCh:
return err 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 { func toControlStatus(s *client.SolveStatus) *pb.StatusResponse {

View File

@ -108,6 +108,7 @@ func (d *Driver) create(ctx context.Context, l progress.SubLogger) error {
cfg.Cmd = d.InitConfig.BuildkitFlags 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 { if err := l.Wrap("creating container "+d.Name, func() error {
hc := &container.HostConfig{ hc := &container.HostConfig{
Privileged: true, Privileged: true,
@ -118,6 +119,7 @@ func (d *Driver) create(ctx context.Context, l progress.SubLogger) error {
Target: confutil.DefaultBuildKitStateDir, Target: confutil.DefaultBuildKitStateDir,
}, },
}, },
Init: &useInit,
} }
if d.netMode != "" { if d.netMode != "" {
hc.NetworkMode = container.NetworkMode(d.netMode) hc.NetworkMode = container.NetworkMode(d.netMode)

View File

@ -7,12 +7,15 @@ import (
"sort" "sort"
"strings" "strings"
"sync" "sync"
"sync/atomic"
"text/tabwriter" "text/tabwriter"
"github.com/containerd/console" "github.com/containerd/console"
"github.com/docker/buildx/controller/control" "github.com/docker/buildx/controller/control"
controllerapi "github.com/docker/buildx/controller/pb" controllerapi "github.com/docker/buildx/controller/pb"
"github.com/docker/buildx/util/ioset" "github.com/docker/buildx/util/ioset"
"github.com/moby/buildkit/identity"
"github.com/pkg/errors"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"golang.org/x/term" "golang.org/x/term"
) )
@ -22,7 +25,9 @@ Available commands are:
reload reloads the context and build it. reload reloads the context and build it.
rollback re-runs the interactive container with initial rootfs contents. rollback re-runs the interactive container with initial rootfs contents.
list list buildx sessions. 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. disconnect disconnect a client from a buildx server. Specific session ID can be specified an arg.
kill kill buildx server. kill kill buildx server.
exit exits monitor. exit exits monitor.
@ -30,7 +35,7 @@ Available commands are:
` `
// RunMonitor provides an interactive session for running and managing containers via specified IO. // 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() { defer func() {
if err := c.Disconnect(ctx, curRef); err != nil { if err := c.Disconnect(ctx, curRef); err != nil {
logrus.Warnf("disconnect error: %v", err) 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 { }, []ioset.MuxOut{monitorOutCtx, containerOutCtx}, 1, func(prev int, res int) string {
if prev == 0 && res == 0 { if prev == 0 && res == 0 {
// No toggle happened because container I/O isn't enabled. // 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" return "Switched IO\n"
}), }),
invokeFunc: func(ctx context.Context, ref string, in io.ReadCloser, out io.WriteCloser, err io.WriteCloser) error { invokeFunc: c.Invoke,
return c.Invoke(ctx, ref, invokeConfig, in, out, err)
},
} }
// Start container automatically // Start container automatically
fmt.Fprintf(stdout, "Launching interactive container. Press Ctrl-a-c to switch to monitor console\n") 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 // Serve monitor commands
monitorForwarder := ioset.NewForwarder() monitorForwarder := ioset.NewForwarder()
@ -127,12 +131,17 @@ func RunMonitor(ctx context.Context, curRef string, options controllerapi.BuildO
} else { } else {
curRef = ref curRef = ref
// rollback the running container with the new result // rollback the running container with the new result
m.rollback(ctx, curRef) id := m.rollback(ctx, curRef, invokeConfig)
fmt.Fprint(stdout, "Interactive container was restarted. Press Ctrl-a-c to switch to the new container\n") fmt.Fprintf(stdout, "Interactive container was restarted with process %q. Press Ctrl-a-c to switch to the new container\n", id)
} }
case "rollback": case "rollback":
m.rollback(ctx, curRef) cfg := invokeConfig
fmt.Fprint(stdout, "Interactive container was restarted. Press Ctrl-a-c to switch to the new container\n") 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": case "list":
refs, err := c.List(ctx) refs, err := c.List(ctx)
if err != nil { if err != nil {
@ -150,6 +159,14 @@ func RunMonitor(ctx context.Context, curRef string, options controllerapi.BuildO
if len(args) >= 2 { if len(args) >= 2 {
target = args[1] 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 { if err := c.Disconnect(ctx, target); err != nil {
fmt.Println("disconnect error", err) fmt.Println("disconnect error", err)
} }
@ -163,8 +180,68 @@ func RunMonitor(ctx context.Context, curRef string, options controllerapi.BuildO
continue continue
} }
ref := args[1] ref := args[1]
m.rollback(ctx, ref) var id string
curRef = ref
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": case "exit":
return return
case "help": case "help":
@ -177,14 +254,10 @@ func RunMonitor(ctx context.Context, curRef string, options controllerapi.BuildO
}() }()
select { select {
case <-doneCh: case <-doneCh:
if m.curInvokeCancel != nil { m.invokeCancel()
m.curInvokeCancel()
}
return nil return nil
case err := <-errCh: case err := <-errCh:
if m.curInvokeCancel != nil { m.invokeCancel()
m.curInvokeCancel()
}
return err return err
case <-monitorDisableCh: case <-monitorDisableCh:
} }
@ -198,34 +271,58 @@ type readWriter struct {
} }
type monitor struct { type monitor struct {
muxIO *ioset.MuxIO muxIO *ioset.MuxIO
invokeIO *ioset.Forwarder invokeIO *ioset.Forwarder
invokeFunc func(context.Context, string, io.ReadCloser, io.WriteCloser, io.WriteCloser) error invokeFunc func(ctx context.Context, ref, pid string, cfg controllerapi.InvokeConfig, in io.ReadCloser, out io.WriteCloser, err io.WriteCloser) error
curInvokeCancel func() invokeCancel func()
attachedPid atomic.Value
} }
func (m *monitor) rollback(ctx context.Context, ref string) { func (m *monitor) rollback(ctx context.Context, ref string, cfg controllerapi.InvokeConfig) string {
if m.curInvokeCancel != nil { pid := identity.NewID()
m.curInvokeCancel() // Finish the running container if exists 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() { go func() {
// Start a new container // Start a new invoke
if err := m.invoke(ctx, ref); err != nil { if err := m.invoke(ctx, ref, pid, cfg); err != nil {
logrus.Debugf("invoke error: %v", err) 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) m.muxIO.Enable(1)
defer m.muxIO.Disable(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) invokeCtx, invokeCancel := context.WithCancel(ctx)
containerIn, containerOut := ioset.Pipe() containerIn, containerOut := ioset.Pipe()
m.invokeIO.SetOut(&containerOut) m.invokeIO.SetOut(&containerOut)
waitInvokeDoneCh := make(chan struct{}) waitInvokeDoneCh := make(chan struct{})
var cancelOnce sync.Once var cancelOnce sync.Once
curInvokeCancel := func() { invokeCancelAndDetachFn := func() {
cancelOnce.Do(func() { cancelOnce.Do(func() {
containerIn.Close() containerIn.Close()
m.invokeIO.SetOut(nil) m.invokeIO.SetOut(nil)
@ -233,10 +330,10 @@ func (m *monitor) invoke(ctx context.Context, ref string) error {
}) })
<-waitInvokeDoneCh <-waitInvokeDoneCh
} }
defer curInvokeCancel() defer invokeCancelAndDetachFn()
m.curInvokeCancel = curInvokeCancel 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) close(waitInvokeDoneCh)
return err return err
@ -247,3 +344,16 @@ type nopCloser struct {
} }
func (c nopCloser) Close() error { return nil } 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
}

View File

@ -159,15 +159,27 @@ func (f *SingleForwarder) doForward() {
var r io.ReadCloser var r io.ReadCloser
for { for {
readerInvalid := false readerInvalid := false
var readerInvalidMu sync.Mutex
copyReaderToWriter := false
if r != nil { if r != nil {
copyReaderToWriter = true
}
if copyReaderToWriter {
srcR := r
go func() { go func() {
buf := make([]byte, 4096) buf := make([]byte, 4096)
readerClosed := false
for { for {
n, readErr := r.Read(buf) n, readErr := srcR.Read(buf)
if readErr != nil && !errors.Is(readErr, io.EOF) && !errors.Is(readErr, io.ErrClosedPipe) { if readErr != nil {
logrus.Debugf("single forwarder: reader error: %v", readErr) srcR.Close()
return 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() f.curWMu.Lock()
w := f.curW w := f.curW
f.curWMu.Unlock() f.curWMu.Unlock()
@ -176,10 +188,14 @@ func (f *SingleForwarder) doForward() {
logrus.Debugf("single forwarder: writer error: %v", err) logrus.Debugf("single forwarder: writer error: %v", err)
} }
} }
if readerInvalid { readerInvalidMu.Lock()
ri := readerInvalid
readerInvalidMu.Unlock()
if ri || readerClosed {
return return
} }
if readErr != io.EOF { if readErr != io.EOF {
logrus.Debugf("unknown error: %v\n", readErr)
continue continue
} }
@ -202,7 +218,9 @@ func (f *SingleForwarder) doForward() {
} }
f.curR = newR f.curR = newR
r = newR r = newR
readerInvalidMu.Lock()
readerInvalid = true readerInvalid = true
readerInvalidMu.Unlock()
f.curRMu.Unlock() f.curRMu.Unlock()
case <-f.doneCh: case <-f.doneCh:
return return

View File

@ -133,6 +133,27 @@ func (m *MuxIO) Enable(i int) {
m.enabled[i] = struct{}{} 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 { func (m *MuxIO) Disable(i int) error {
m.mu.Lock() m.mu.Lock()
defer m.mu.Unlock() defer m.mu.Unlock()