update gateway to add ability to run and exec into containers

Signed-off-by: Cory Bennett <cbennett@netflix.com>
v0.8
Cory Bennett 2020-08-05 01:11:32 +00:00
parent a57f8a2dcc
commit 355e937e15
16 changed files with 4975 additions and 258 deletions

View File

@ -108,3 +108,18 @@ func (g *gatewayClientForBuild) Inputs(ctx context.Context, in *gatewayapi.Input
ctx = buildid.AppendToOutgoingContext(ctx, g.buildID)
return g.gateway.Inputs(ctx, in, opts...)
}
func (g *gatewayClientForBuild) NewContainer(ctx context.Context, in *gatewayapi.NewContainerRequest, opts ...grpc.CallOption) (*gatewayapi.NewContainerResponse, error) {
ctx = buildid.AppendToOutgoingContext(ctx, g.buildID)
return g.gateway.NewContainer(ctx, in, opts...)
}
func (g *gatewayClientForBuild) ReleaseContainer(ctx context.Context, in *gatewayapi.ReleaseContainerRequest, opts ...grpc.CallOption) (*gatewayapi.ReleaseContainerResponse, error) {
ctx = buildid.AppendToOutgoingContext(ctx, g.buildID)
return g.gateway.ReleaseContainer(ctx, in, opts...)
}
func (g *gatewayClientForBuild) ExecProcess(ctx context.Context, opts ...grpc.CallOption) (gatewayapi.LLBBridge_ExecProcessClient, error) {
ctx = buildid.AppendToOutgoingContext(ctx, g.buildID)
return g.gateway.ExecProcess(ctx, opts...)
}

View File

@ -1,16 +1,21 @@
package client
import (
"bytes"
"context"
"io"
"io/ioutil"
"os"
"path/filepath"
"testing"
"time"
"github.com/moby/buildkit/client/llb"
"github.com/moby/buildkit/frontend/gateway/client"
gatewayapi "github.com/moby/buildkit/frontend/gateway/pb"
"github.com/moby/buildkit/identity"
"github.com/moby/buildkit/solver/errdefs"
"github.com/moby/buildkit/solver/pb"
"github.com/moby/buildkit/util/testutil/integration"
"github.com/pkg/errors"
"github.com/stretchr/testify/require"
@ -23,6 +28,10 @@ func TestClientGatewayIntegration(t *testing.T) {
testClientGatewayEmptySolve,
testNoBuildID,
testUnknownBuildID,
testClientGatewayContainerExecPipe,
testClientGatewayContainerCancelOnRelease,
testClientGatewayContainerPID1Fail,
testClientGatewayContainerPID1Exit,
}, integration.WithMirroredImages(integration.OfficialImages("busybox:latest")))
}
@ -175,3 +184,367 @@ func testUnknownBuildID(t *testing.T, sb integration.Sandbox) {
require.Error(t, err)
require.Contains(t, err.Error(), "no such job")
}
// testClientGatewayContainerCancelOnRelease is testing that all running
// processes are terminated when the container is released.
func testClientGatewayContainerCancelOnRelease(t *testing.T, sb integration.Sandbox) {
requiresLinux(t)
ctx := context.TODO()
c, err := New(ctx, sb.Address())
require.NoError(t, err)
defer c.Close()
product := "buildkit_test"
b := func(ctx context.Context, c client.Client) (*client.Result, error) {
st := llb.Image("busybox:latest")
def, err := st.Marshal(ctx)
if err != nil {
return nil, errors.Wrap(err, "failed to marshal state")
}
r, err := c.Solve(ctx, client.SolveRequest{
Definition: def.ToPB(),
})
if err != nil {
return nil, errors.Wrap(err, "failed to solve")
}
ctr, err := c.NewContainer(ctx, client.NewContainerRequest{
Mounts: []client.Mount{{
Dest: "/",
MountType: pb.MountType_BIND,
Ref: r.Ref,
}},
})
if err != nil {
return nil, err
}
start := time.Now()
defer func() {
// ensure pid1 and pid2 exit from cancel before the 10s sleep
// exits naturally
require.WithinDuration(t, start, time.Now(), 10*time.Second)
}()
// background pid1 process that starts container
pid1, err := ctr.Start(ctx, client.StartRequest{
Args: []string{"sleep", "10"},
Cwd: "/",
})
require.NoError(t, err)
pid2, err := ctr.Start(ctx, client.StartRequest{
Args: []string{"sleep", "10"},
Cwd: "/",
})
require.NoError(t, err)
ctr.Release(ctx)
err = pid1.Wait()
require.Contains(t, err.Error(), context.Canceled.Error())
err = pid2.Wait()
require.Contains(t, err.Error(), context.Canceled.Error())
return &client.Result{}, nil
}
c.Build(ctx, SolveOpt{}, product, b, nil)
checkAllReleasable(t, c, sb, true)
}
// testClientGatewayContainerExecPipe is testing the ability to pipe multiple
// process together all started via `Exec` into the same container.
// We are mimicing: `echo testing | cat | cat > /tmp/foo && cat /tmp/foo`
func testClientGatewayContainerExecPipe(t *testing.T, sb integration.Sandbox) {
if sb.Rootless() {
// TODO fix this
// We get `panic: cannot statfs cgroup root` from runc when when running
// this test with runc-rootless, no idea why.
t.Skip("Skipping oci-rootless for cgroup error")
}
requiresLinux(t)
ctx := context.TODO()
c, err := New(ctx, sb.Address())
require.NoError(t, err)
defer c.Close()
product := "buildkit_test"
output := bytes.NewBuffer([]byte{})
b := func(ctx context.Context, c client.Client) (*client.Result, error) {
st := llb.Image("busybox:latest")
def, err := st.Marshal(ctx)
if err != nil {
return nil, errors.Wrap(err, "failed to marshal state")
}
r, err := c.Solve(ctx, client.SolveRequest{
Definition: def.ToPB(),
})
if err != nil {
return nil, errors.Wrap(err, "failed to solve")
}
ctr, err := c.NewContainer(ctx, client.NewContainerRequest{
Mounts: []client.Mount{{
Dest: "/",
MountType: pb.MountType_BIND,
Ref: r.Ref,
}},
})
if err != nil {
return nil, err
}
// background pid1 process that starts container
pid1, err := ctr.Start(ctx, client.StartRequest{
Args: []string{"sleep", "10"},
Cwd: "/",
})
if err != nil {
ctr.Release(ctx)
return nil, err
}
defer func() {
// cancel pid1
ctr.Release(ctx)
pid1.Wait()
}()
// first part is `echo testing | cat`
stdin2 := bytes.NewBuffer([]byte("testing"))
stdin3, stdout2 := io.Pipe()
pid2, err := ctr.Start(ctx, client.StartRequest{
Args: []string{"cat"},
Cwd: "/",
Tty: false,
Stdin: ioutil.NopCloser(stdin2),
Stdout: stdout2,
})
if err != nil {
return nil, err
}
// next part is: `| cat > /tmp/test`
pid3, err := ctr.Start(ctx, client.StartRequest{
Args: []string{"sh", "-c", "cat > /tmp/test"},
Cwd: "/",
Stdin: stdin3,
})
if err != nil {
return nil, err
}
err = pid2.Wait()
if err != nil {
stdout2.Close()
return nil, err
}
err = stdout2.Close()
if err != nil {
return nil, err
}
err = pid3.Wait()
if err != nil {
return nil, err
}
err = stdin3.Close()
if err != nil {
return nil, err
}
pid4, err := ctr.Start(ctx, client.StartRequest{
Args: []string{"cat", "/tmp/test"},
Cwd: "/",
Stdout: &nopCloser{output},
})
if err != nil {
return nil, err
}
err = pid4.Wait()
if err != nil {
return nil, err
}
return &client.Result{}, nil
}
_, err = c.Build(ctx, SolveOpt{}, product, b, nil)
require.NoError(t, err)
require.Equal(t, "testing", output.String())
checkAllReleasable(t, c, sb, true)
}
// testClientGatewayContainerPID1Fail is testing clean shutdown and release
// of resources when the primary pid1 exits with non-zero exit status
func testClientGatewayContainerPID1Fail(t *testing.T, sb integration.Sandbox) {
requiresLinux(t)
ctx := context.TODO()
c, err := New(ctx, sb.Address())
require.NoError(t, err)
defer c.Close()
product := "buildkit_test"
b := func(ctx context.Context, c client.Client) (*client.Result, error) {
st := llb.Image("busybox:latest")
def, err := st.Marshal(ctx)
if err != nil {
return nil, errors.Wrap(err, "failed to marshal state")
}
r, err := c.Solve(ctx, client.SolveRequest{
Definition: def.ToPB(),
})
if err != nil {
return nil, errors.Wrap(err, "failed to solve")
}
ctr, err := c.NewContainer(ctx, client.NewContainerRequest{
Mounts: []client.Mount{{
Dest: "/",
MountType: pb.MountType_BIND,
Ref: r.Ref,
}},
})
if err != nil {
return nil, err
}
pid1, err := ctr.Start(ctx, client.StartRequest{
Args: []string{"false"},
Cwd: "/",
})
if err != nil {
ctr.Release(ctx)
return nil, err
}
defer ctr.Release(ctx)
err = pid1.Wait()
var exitError *errdefs.ExitError
require.True(t, errors.As(err, &exitError))
require.Equal(t, uint32(1), exitError.ExitCode)
return nil, err
}
_, err = c.Build(ctx, SolveOpt{}, product, b, nil)
require.Error(t, err)
checkAllReleasable(t, c, sb, true)
}
// testClientGatewayContainerPID1Exit is testing that all process started
// via `Exec` are shutdown when the primary pid1 process exits
func testClientGatewayContainerPID1Exit(t *testing.T, sb integration.Sandbox) {
if sb.Rootless() {
// TODO fix this
// We get `panic: cannot statfs cgroup root` when running this test
// with runc-rootless
t.Skip("Skipping runc-rootless for cgroup error")
}
requiresLinux(t)
ctx := context.TODO()
c, err := New(ctx, sb.Address())
require.NoError(t, err)
defer c.Close()
product := "buildkit_test"
b := func(ctx context.Context, c client.Client) (*client.Result, error) {
st := llb.Image("busybox:latest")
def, err := st.Marshal(ctx)
if err != nil {
return nil, errors.Wrap(err, "failed to marshal state")
}
r, err := c.Solve(ctx, client.SolveRequest{
Definition: def.ToPB(),
})
if err != nil {
return nil, errors.Wrap(err, "failed to solve")
}
ctr, err := c.NewContainer(ctx, client.NewContainerRequest{
Mounts: []client.Mount{{
Dest: "/",
MountType: pb.MountType_BIND,
Ref: r.Ref,
}},
})
if err != nil {
return nil, err
}
defer ctr.Release(ctx)
start := time.Now()
defer func() {
// ensure pid1 and pid2 exits from cancel before the 10s sleep
// exits naturally
require.WithinDuration(t, start, time.Now(), 10*time.Second)
// assert this test ran for at least one second for pid1
lapse := time.Now().Sub(start)
require.Greater(t, lapse.Seconds(), float64(1))
}()
pid1, err := ctr.Start(ctx, client.StartRequest{
Args: []string{"sleep", "1"},
Cwd: "/",
})
require.NoError(t, err)
defer pid1.Wait()
pid2, err := ctr.Start(ctx, client.StartRequest{
Args: []string{"sleep", "10"},
Cwd: "/",
})
require.NoError(t, err)
return &client.Result{}, pid2.Wait()
}
_, err = c.Build(ctx, SolveOpt{}, product, b, nil)
// pid2 should error with `exit code: 255 on runc or
// `exit code: 137` (ie sigkill) on containerd
require.Error(t, err)
require.Regexp(t, "exit code: (255|137)", err.Error())
checkAllReleasable(t, c, sb, true)
}
type nopCloser struct {
io.Writer
}
func (n *nopCloser) Close() error {
return nil
}

View File

@ -152,3 +152,27 @@ func (gwf *GatewayForwarder) StatFile(ctx context.Context, req *gwapi.StatFileRe
}
return fwd.StatFile(ctx, req)
}
func (gwf *GatewayForwarder) NewContainer(ctx context.Context, req *gwapi.NewContainerRequest) (*gwapi.NewContainerResponse, error) {
fwd, err := gwf.lookupForwarder(ctx)
if err != nil {
return nil, errors.Wrap(err, "forwarding NewContainer")
}
return fwd.NewContainer(ctx, req)
}
func (gwf *GatewayForwarder) ReleaseContainer(ctx context.Context, req *gwapi.ReleaseContainerRequest) (*gwapi.ReleaseContainerResponse, error) {
fwd, err := gwf.lookupForwarder(ctx)
if err != nil {
return nil, errors.Wrap(err, "forwarding ReleaseContainer")
}
return fwd.ReleaseContainer(ctx, req)
}
func (gwf *GatewayForwarder) ExecProcess(srv gwapi.LLBBridge_ExecProcessServer) error {
fwd, err := gwf.lookupForwarder(srv.Context())
if err != nil {
return errors.Wrap(err, "forwarding ExecProcess")
}
return fwd.ExecProcess(srv)
}

View File

@ -2,6 +2,8 @@ package containerdexecutor
import (
"context"
"io"
"io/ioutil"
"os"
"path/filepath"
"strings"
@ -19,6 +21,7 @@ import (
"github.com/moby/buildkit/executor/oci"
"github.com/moby/buildkit/identity"
"github.com/moby/buildkit/snapshot"
"github.com/moby/buildkit/solver/errdefs"
"github.com/moby/buildkit/solver/pb"
"github.com/moby/buildkit/util/network"
"github.com/opencontainers/runtime-spec/specs-go"
@ -187,6 +190,7 @@ func (w *containerdExecutor) Run(ctx context.Context, id string, root cache.Moun
}
}()
fixProcessOutput(&process)
cioOpts := []cio.Opt{cio.WithStreams(process.Stdin, process.Stdout, process.Stderr)}
if meta.Tty {
cioOpts = append(cioOpts, cio.WithTerminal)
@ -286,6 +290,7 @@ func (w *containerdExecutor) Exec(ctx context.Context, id string, process execut
spec.Process.Env = process.Meta.Env
}
fixProcessOutput(&process)
cioOpts := []cio.Opt{cio.WithStreams(process.Stdin, process.Stdout, process.Stderr)}
if meta.Tty {
cioOpts = append(cioOpts, cio.WithTerminal)
@ -300,6 +305,19 @@ func (w *containerdExecutor) Exec(ctx context.Context, id string, process execut
return err
}
func fixProcessOutput(process *executor.ProcessInfo) {
// It seems like if containerd has one of stdin, stdout or stderr then the
// others need to be present as well otherwise we get this error:
// failed to start io pipe copy: unable to copy pipes: containerd-shim: opening file "" failed: open : no such file or directory: unknown
// So just stub out any missing output
if process.Stdout == nil {
process.Stdout = &nopCloser{ioutil.Discard}
}
if process.Stderr == nil {
process.Stderr = &nopCloser{ioutil.Discard}
}
}
func (w *containerdExecutor) runProcess(ctx context.Context, p containerd.Process, resize <-chan executor.WinSize, started func()) error {
// Not using `ctx` here because the context passed only affects the statusCh which we
// don't want cancelled when ctx.Done is sent. We want to process statusCh on cancel.
@ -356,7 +374,7 @@ func (w *containerdExecutor) runProcess(ctx context.Context, p containerd.Proces
cancel()
}
if status.ExitCode() != 0 {
exitErr := &executor.ExitError{
exitErr := &errdefs.ExitError{
ExitCode: status.ExitCode(),
Err: status.Error(),
}
@ -379,3 +397,11 @@ func (w *containerdExecutor) runProcess(ctx context.Context, p containerd.Proces
}
}
}
type nopCloser struct {
io.Writer
}
func (c *nopCloser) Close() error {
return nil
}

View File

@ -2,7 +2,6 @@ package executor
import (
"context"
"fmt"
"io"
"net"
@ -55,24 +54,3 @@ type HostIP struct {
Host string
IP net.IP
}
// ExitError will be returned from Run and Exec when the container process exits with
// a non-zero exit code.
type ExitError struct {
ExitCode uint32
Err error
}
func (err *ExitError) Error() string {
if err.Err != nil {
return err.Err.Error()
}
return fmt.Sprintf("exit code: %d", err.ExitCode)
}
func (err *ExitError) Unwrap() error {
if err.Err == nil {
return fmt.Errorf("exit code: %d", err.ExitCode)
}
return err.Err
}

View File

@ -21,6 +21,7 @@ import (
"github.com/moby/buildkit/executor"
"github.com/moby/buildkit/executor/oci"
"github.com/moby/buildkit/identity"
"github.com/moby/buildkit/solver/errdefs"
"github.com/moby/buildkit/solver/pb"
"github.com/moby/buildkit/util/network"
rootlessspecconv "github.com/moby/buildkit/util/rootless/specconv"
@ -332,7 +333,7 @@ func (w *runcExecutor) Run(ctx context.Context, id string, root cache.Mountable,
close(ended)
if status != 0 || err != nil {
exitErr := &executor.ExitError{
exitErr := &errdefs.ExitError{
ExitCode: uint32(status),
Err: err,
}
@ -418,7 +419,7 @@ func (w *runcExecutor) Exec(ctx context.Context, id string, process executor.Pro
var exitError *exec.ExitError
if errors.As(err, &exitError) {
err = &executor.ExitError{
err = &errdefs.ExitError{
ExitCode: uint32(exitError.ExitCode()),
Err: err,
}

View File

@ -2,6 +2,7 @@ package client
import (
"context"
"io"
"github.com/moby/buildkit/client/llb"
"github.com/moby/buildkit/solver/pb"
@ -16,6 +17,58 @@ type Client interface {
ResolveImageConfig(ctx context.Context, ref string, opt llb.ResolveImageConfigOpt) (digest.Digest, []byte, error)
BuildOpts() BuildOpts
Inputs(ctx context.Context) (map[string]llb.State, error)
NewContainer(ctx context.Context, req NewContainerRequest) (Container, error)
}
// NewContainerRequest encapsulates the requirements for a client to define a
// new container, without defining the initial process.
type NewContainerRequest struct {
Mounts []Mount
NetMode pb.NetMode
SecurityMode pb.SecurityMode
}
// Mount allows clients to specify a filesystem mount. A Reference to a
// previously solved Result is required.
type Mount struct {
Selector string
Dest string
Ref Reference
Readonly bool
MountType pb.MountType
}
// Container is used to start new processes inside a container and release the
// container resources when done.
type Container interface {
Start(context.Context, StartRequest) (ContainerProcess, error)
Release(context.Context) error
}
// StartRequest encapsulates the arguments to define a process within a
// container.
type StartRequest struct {
Args []string
Env []string
User string
Cwd string
Tty bool
Stdin io.ReadCloser
Stdout, Stderr io.WriteCloser
}
// WinSize is same as executor.WinSize, copied here to prevent circular package
// dependencies.
type WinSize struct {
Rows uint32
Cols uint32
}
// ContainerProcess represents a process within a container.
type ContainerProcess interface {
Wait() error
Resize(ctx context.Context, size WinSize) error
// TODO Signal(ctx context.Context, sig os.Signal)
}
type Reference interface {

View File

@ -0,0 +1,242 @@
package gateway
import (
"context"
"strings"
"sync"
"github.com/moby/buildkit/cache"
"github.com/moby/buildkit/executor"
"github.com/moby/buildkit/frontend/gateway/client"
"github.com/moby/buildkit/solver"
opspb "github.com/moby/buildkit/solver/pb"
"github.com/moby/buildkit/util/stack"
utilsystem "github.com/moby/buildkit/util/system"
"github.com/moby/buildkit/worker"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"golang.org/x/sync/errgroup"
)
type Container interface {
client.Container
// OnRelease allows callbacks to free up resources when the container exits.
// The functions are called in LIFO order.
OnRelease(func() error)
}
type NewContainerRequest struct {
ContainerID string
NetMode opspb.NetMode
SecurityMode opspb.SecurityMode
Mounts []Mount
}
// Mount used for the gateway.Container is nearly identical to the client.Mount
// except is has a RefProxy instead of Ref to allow for a common abstraction
// between gateway clients.
type Mount struct {
Dest string
Selector string
Readonly bool
MountType opspb.MountType
RefProxy solver.ResultProxy
}
func NewContainer(ctx context.Context, e executor.Executor, req NewContainerRequest) (Container, error) {
ctx, cancel := context.WithCancel(ctx)
eg, ctx := errgroup.WithContext(ctx)
ctr := &gatewayContainer{
id: req.ContainerID,
netMode: req.NetMode,
securityMode: req.SecurityMode,
executor: e,
errGroup: eg,
ctx: ctx,
cancel: cancel,
}
for _, m := range req.Mounts {
res, err := m.RefProxy.Result(ctx)
if err != nil {
return nil, stack.Enable(err)
}
workerRef, ok := res.Sys().(*worker.WorkerRef)
if !ok {
return nil, stack.Enable(errors.Errorf("invalid reference for exec %T", res.Sys()))
}
execMount := executor.Mount{
Src: workerRef.ImmutableRef,
Selector: m.Selector,
Dest: m.Dest,
Readonly: m.Readonly,
}
if !m.Readonly {
ref, err := workerRef.Worker.CacheManager().New(ctx, workerRef.ImmutableRef)
if err != nil {
return nil, stack.Enable(err)
}
ctr.OnRelease(func() error {
return stack.Enable(ref.Release(context.TODO()))
})
execMount.Src = ref
}
if m.Dest == "/" {
ctr.rootFS = execMount.Src
} else {
ctr.mounts = append(ctr.mounts, execMount)
}
}
return ctr, nil
}
type gatewayContainer struct {
id string
netMode opspb.NetMode
securityMode opspb.SecurityMode
rootFS cache.Mountable
mounts []executor.Mount
executor executor.Executor
started bool
errGroup *errgroup.Group
mu sync.Mutex
cleanup []func() error
ctx context.Context
cancel func()
}
func (gwCtr *gatewayContainer) Start(ctx context.Context, req client.StartRequest) (client.ContainerProcess, error) {
resize := make(chan executor.WinSize)
procInfo := executor.ProcessInfo{
Meta: executor.Meta{
Args: req.Args,
Env: req.Env,
User: req.User,
Cwd: req.Cwd,
Tty: req.Tty,
NetMode: gwCtr.netMode,
SecurityMode: gwCtr.securityMode,
},
Stdin: req.Stdin,
Stdout: req.Stdout,
Stderr: req.Stderr,
Resize: resize,
}
procInfo.Meta.Env = addDefaultEnvvar(procInfo.Meta.Env, "PATH", utilsystem.DefaultPathEnv)
if req.Tty {
procInfo.Meta.Env = addDefaultEnvvar(procInfo.Meta.Env, "TERM", "xterm")
}
// mark that we have started on the first call to execProcess for this
// container, so that future calls will call Exec rather than Run
gwCtr.mu.Lock()
started := gwCtr.started
gwCtr.started = true
gwCtr.mu.Unlock()
eg, ctx := errgroup.WithContext(gwCtr.ctx)
gwProc := &gatewayContainerProcess{
resize: resize,
errGroup: eg,
groupCtx: ctx,
}
if !started {
startedCh := make(chan struct{})
gwProc.errGroup.Go(func() error {
logrus.Debugf("Starting new container for %s with args: %q", gwCtr.id, procInfo.Meta.Args)
err := gwCtr.executor.Run(ctx, gwCtr.id, gwCtr.rootFS, gwCtr.mounts, procInfo, startedCh)
return stack.Enable(err)
})
select {
case <-ctx.Done():
case <-startedCh:
}
} else {
gwProc.errGroup.Go(func() error {
logrus.Debugf("Execing into container %s with args: %q", gwCtr.id, procInfo.Meta.Args)
err := gwCtr.executor.Exec(ctx, gwCtr.id, procInfo)
return stack.Enable(err)
})
}
gwCtr.errGroup.Go(gwProc.errGroup.Wait)
return gwProc, nil
}
func (gwCtr *gatewayContainer) Release(ctx context.Context) error {
gwCtr.cancel()
err1 := gwCtr.errGroup.Wait()
var err2 error
for i := len(gwCtr.cleanup) - 1; i >= 0; i-- { // call in LIFO order
err := gwCtr.cleanup[i]()
if err2 == nil {
err2 = err
}
}
if err1 != nil {
return stack.Enable(err1)
}
return stack.Enable(err2)
}
// OnRelease will call the provided function when the Container has been
// released. The functions are called in LIFO order.
func (gwCtr *gatewayContainer) OnRelease(f func() error) {
gwCtr.cleanup = append(gwCtr.cleanup, f)
}
type gatewayContainerProcess struct {
errGroup *errgroup.Group
groupCtx context.Context
resize chan<- executor.WinSize
mu sync.Mutex
}
func (gwProc *gatewayContainerProcess) Wait() error {
err := stack.Enable(gwProc.errGroup.Wait())
gwProc.mu.Lock()
defer gwProc.mu.Unlock()
close(gwProc.resize)
return err
}
func (gwProc *gatewayContainerProcess) Resize(ctx context.Context, size client.WinSize) error {
gwProc.mu.Lock()
defer gwProc.mu.Unlock()
// is the container done or should we proceed with sending event?
select {
case <-gwProc.groupCtx.Done():
return nil
case <-ctx.Done():
return nil
default:
}
// now we select on contexts again in case p.resize blocks b/c
// container no longer reading from it. In that case when
// the errgroup finishes we want to unblock on the write
// and exit
select {
case <-gwProc.groupCtx.Done():
case <-ctx.Done():
case gwProc.resize <- executor.WinSize{Cols: size.Cols, Rows: size.Rows}:
}
return nil
}
func addDefaultEnvvar(env []string, k, v string) []string {
for _, e := range env {
if strings.HasPrefix(e, k+"=") {
return env
}
}
return append(env, k+"="+v)
}

View File

@ -9,8 +9,10 @@ import (
clienttypes "github.com/moby/buildkit/client"
"github.com/moby/buildkit/client/llb"
"github.com/moby/buildkit/frontend"
"github.com/moby/buildkit/frontend/gateway"
"github.com/moby/buildkit/frontend/gateway/client"
gwpb "github.com/moby/buildkit/frontend/gateway/pb"
"github.com/moby/buildkit/identity"
"github.com/moby/buildkit/solver"
opspb "github.com/moby/buildkit/solver/pb"
"github.com/moby/buildkit/util/apicaps"
@ -149,6 +151,34 @@ func (c *bridgeClient) discard(err error) {
}
}
func (c *bridgeClient) NewContainer(ctx context.Context, req client.NewContainerRequest) (client.Container, error) {
ctrReq := gateway.NewContainerRequest{
ContainerID: identity.NewID(),
NetMode: req.NetMode,
SecurityMode: req.SecurityMode,
}
for _, m := range req.Mounts {
refProxy, ok := m.Ref.(*ref)
if !ok {
return nil, errors.Errorf("Unexpected Ref type: %T", m.Ref)
}
ctrReq.Mounts = append(ctrReq.Mounts, gateway.Mount{
Dest: m.Dest,
Selector: m.Selector,
Readonly: m.Readonly,
MountType: m.MountType,
RefProxy: refProxy,
})
}
ctr, err := gateway.NewContainer(ctx, c, ctrReq)
if err != nil {
return nil, err
}
return ctr, nil
}
type ref struct {
solver.ResultProxy
}

View File

@ -12,6 +12,7 @@ import (
"sync"
"time"
"github.com/containerd/containerd"
"github.com/docker/distribution/reference"
gogotypes "github.com/gogo/protobuf/types"
"github.com/golang/protobuf/ptypes/any"
@ -23,20 +24,25 @@ import (
"github.com/moby/buildkit/executor"
"github.com/moby/buildkit/exporter/containerimage/exptypes"
"github.com/moby/buildkit/frontend"
gwclient "github.com/moby/buildkit/frontend/gateway/client"
pb "github.com/moby/buildkit/frontend/gateway/pb"
"github.com/moby/buildkit/identity"
"github.com/moby/buildkit/solver"
"github.com/moby/buildkit/solver/errdefs"
opspb "github.com/moby/buildkit/solver/pb"
"github.com/moby/buildkit/util/apicaps"
"github.com/moby/buildkit/util/grpcerrors"
"github.com/moby/buildkit/util/stack"
"github.com/moby/buildkit/util/tracing"
"github.com/moby/buildkit/worker"
specs "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"golang.org/x/net/http2"
"golang.org/x/sync/errgroup"
spb "google.golang.org/genproto/googleapis/rpc/status"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/health"
"google.golang.org/grpc/health/grpc_health_v1"
"google.golang.org/grpc/status"
@ -316,6 +322,7 @@ func newBridgeForwarder(ctx context.Context, llbBridge frontend.FrontendLLBBridg
workers: workers,
inputs: inputs,
sid: sid,
ctrs: map[string]Container{},
}
return lbf
}
@ -416,6 +423,8 @@ type llbBridgeForwarder struct {
isErrServerClosed bool
sid string
*pipe
ctrs map[string]Container
ctrsMu sync.Mutex
}
func (lbf *llbBridgeForwarder) ResolveImageConfig(ctx context.Context, req *pb.ResolveImageConfigRequest) (*pb.ResolveImageConfigResponse, error) {
@ -738,6 +747,406 @@ func (lbf *llbBridgeForwarder) Inputs(ctx context.Context, in *pb.InputsRequest)
}, nil
}
func (lbf *llbBridgeForwarder) NewContainer(ctx context.Context, in *pb.NewContainerRequest) (_ *pb.NewContainerResponse, err error) {
logrus.Debugf("|<--- NewContainer %s", in.ContainerID)
ctrReq := NewContainerRequest{
ContainerID: in.ContainerID,
NetMode: in.Network,
SecurityMode: in.Security,
}
for _, m := range in.Mounts {
refProxy, err := lbf.convertRef(m.ResultID)
if err != nil {
return nil, errors.Wrapf(err, "Failed to find ref %s for %q mount", m.ResultID, m.Dest)
}
ctrReq.Mounts = append(ctrReq.Mounts, Mount{
Dest: m.Dest,
Selector: m.Selector,
Readonly: m.Readonly,
MountType: m.MountType,
RefProxy: refProxy,
})
}
// Not using `ctx` here because it will get cancelled as soon as NewContainer returns
// and we want the context to live for the duration of the container.
ctr, err := NewContainer(context.Background(), lbf.llbBridge, ctrReq)
if err != nil {
return nil, stack.Enable(err)
}
lbf.ctrsMu.Lock()
// ensure we are not clobbering a dup container id request
if _, ok := lbf.ctrs[in.ContainerID]; ok {
lbf.ctrsMu.Unlock()
ctr.Release(ctx)
return nil, stack.Enable(status.Errorf(codes.AlreadyExists, "Container %s already exists", in.ContainerID))
}
lbf.ctrs[in.ContainerID] = ctr
lbf.ctrsMu.Unlock()
return &pb.NewContainerResponse{}, nil
}
func (lbf *llbBridgeForwarder) ReleaseContainer(ctx context.Context, in *pb.ReleaseContainerRequest) (*pb.ReleaseContainerResponse, error) {
logrus.Debugf("|<--- ReleaseContainer %s", in.ContainerID)
lbf.ctrsMu.Lock()
ctr, ok := lbf.ctrs[in.ContainerID]
delete(lbf.ctrs, in.ContainerID)
lbf.ctrsMu.Unlock()
if !ok {
return nil, errors.Errorf("Container details for %s not found", in.ContainerID)
}
err := ctr.Release(ctx)
return &pb.ReleaseContainerResponse{}, stack.Enable(err)
}
type processIO struct {
id string
mu sync.Mutex
resize func(context.Context, gwclient.WinSize) error
done chan struct{}
doneOnce sync.Once
// these track the process side of the io pipe for
// read (fd=0) and write (fd=1, fd=2)
processReaders map[uint32]io.ReadCloser
processWriters map[uint32]io.WriteCloser
// these track the server side of the io pipe, so
// when we receive an EOF over grpc, we will close
// this end
serverWriters map[uint32]io.WriteCloser
serverReaders map[uint32]io.ReadCloser
}
func newProcessIO(id string, openFds []uint32) *processIO {
pio := &processIO{
id: id,
processReaders: map[uint32]io.ReadCloser{},
processWriters: map[uint32]io.WriteCloser{},
serverReaders: map[uint32]io.ReadCloser{},
serverWriters: map[uint32]io.WriteCloser{},
done: make(chan struct{}),
}
for _, fd := range openFds {
// TODO do we know which way to pipe each fd? For now assume fd0 is for
// reading, and the rest are for writing
r, w := io.Pipe()
if fd == 0 {
pio.processReaders[fd] = r
pio.serverWriters[fd] = w
} else {
pio.processWriters[fd] = w
pio.serverReaders[fd] = r
}
}
return pio
}
func (pio *processIO) Close() (err error) {
pio.mu.Lock()
defer pio.mu.Unlock()
for fd, r := range pio.processReaders {
delete(pio.processReaders, fd)
err1 := r.Close()
if err1 != nil && err == nil {
err = stack.Enable(err1)
}
}
for fd, w := range pio.serverReaders {
delete(pio.serverReaders, fd)
err1 := w.Close()
if err1 != nil && err == nil {
err = stack.Enable(err1)
}
}
pio.Done()
return err
}
func (pio *processIO) Done() {
stillOpen := len(pio.processReaders) + len(pio.processWriters) + len(pio.serverReaders) + len(pio.serverWriters)
if stillOpen == 0 {
pio.doneOnce.Do(func() {
close(pio.done)
})
}
}
func (pio *processIO) Write(f *pb.FdMessage) (err error) {
pio.mu.Lock()
writer := pio.serverWriters[f.Fd]
pio.mu.Unlock()
if writer == nil {
return status.Errorf(codes.OutOfRange, "fd %d unavailable to write", f.Fd)
}
defer func() {
if err != nil || f.EOF {
writer.Close()
pio.mu.Lock()
defer pio.mu.Unlock()
delete(pio.serverWriters, f.Fd)
pio.Done()
}
}()
if len(f.Data) > 0 {
_, err = writer.Write(f.Data)
return stack.Enable(err)
}
return nil
}
type outputWriter struct {
stream pb.LLBBridge_ExecProcessServer
fd uint32
processID string
}
func (w *outputWriter) Write(msg []byte) (int, error) {
logrus.Debugf("|---> File Message %s, fd=%d, %d bytes", w.processID, w.fd, len(msg))
err := w.stream.Send(&pb.ExecMessage{
ProcessID: w.processID,
Input: &pb.ExecMessage_File{
File: &pb.FdMessage{
Fd: w.fd,
Data: msg,
},
},
})
return len(msg), stack.Enable(err)
}
func (lbf *llbBridgeForwarder) ExecProcess(srv pb.LLBBridge_ExecProcessServer) error {
eg, ctx := errgroup.WithContext(srv.Context())
msgs := make(chan *pb.ExecMessage)
eg.Go(func() error {
defer close(msgs)
for {
execMsg, err := srv.Recv()
if err != nil {
if errors.Is(err, io.EOF) {
return nil
}
return stack.Enable(err)
}
switch m := execMsg.GetInput().(type) {
case *pb.ExecMessage_Init:
logrus.Debugf("|<--- Init Message %s", execMsg.ProcessID)
case *pb.ExecMessage_File:
if m.File.EOF {
logrus.Debugf("|<--- File Message %s, fd=%d, EOF", execMsg.ProcessID, m.File.Fd)
} else {
logrus.Debugf("|<--- File Message %s, fd=%d, %d bytes", execMsg.ProcessID, m.File.Fd, len(m.File.Data))
}
case *pb.ExecMessage_Resize:
logrus.Debugf("|<--- Resize Message %s", execMsg.ProcessID)
}
select {
case <-ctx.Done():
case msgs <- execMsg:
}
}
})
eg.Go(func() error {
pios := make(map[string]*processIO)
// close any stray pios on exit to make sure
// all the associated resources get cleaned up
defer func() {
for _, pio := range pios {
pio.Close()
}
}()
for {
var execMsg *pb.ExecMessage
select {
case <-ctx.Done():
return nil
case execMsg = <-msgs:
}
if execMsg == nil {
return nil
}
pid := execMsg.ProcessID
if pid == "" {
return stack.Enable(status.Errorf(codes.InvalidArgument, "ProcessID required"))
}
pio, pioFound := pios[pid]
if data := execMsg.GetFile(); data != nil {
if !pioFound {
return stack.Enable(status.Errorf(codes.NotFound, "IO for process %q not found", pid))
}
err := pio.Write(data)
if err != nil {
return stack.Enable(err)
}
} else if resize := execMsg.GetResize(); resize != nil {
if !pioFound {
return stack.Enable(status.Errorf(codes.NotFound, "IO for process %q not found", pid))
}
pio.resize(ctx, gwclient.WinSize{
Cols: resize.Cols,
Rows: resize.Rows,
})
} else if init := execMsg.GetInit(); init != nil {
if pioFound {
return stack.Enable(status.Errorf(codes.AlreadyExists, "Process %s already exists", pid))
}
id := init.ContainerID
lbf.ctrsMu.Lock()
ctr, ok := lbf.ctrs[id]
lbf.ctrsMu.Unlock()
if !ok {
return stack.Enable(status.Errorf(codes.NotFound, "container %q previously released or not created", id))
}
initCtx, initCancel := context.WithCancel(context.Background())
defer initCancel()
pio := newProcessIO(pid, init.Fds)
pios[pid] = pio
proc, err := ctr.Start(initCtx, gwclient.StartRequest{
Args: init.Meta.Args,
Env: init.Meta.Env,
User: init.Meta.User,
Cwd: init.Meta.Cwd,
Tty: init.Tty,
Stdin: pio.processReaders[0],
Stdout: pio.processWriters[1],
Stderr: pio.processWriters[2],
})
if err != nil {
return stack.Enable(err)
}
pio.resize = proc.Resize
// ensure process has been canceled if the container is released
ctr.OnRelease(func() error {
initCancel()
return nil
})
eg.Go(func() error {
<-pio.done
logrus.Debugf("|---> Done Message %s", pid)
err := srv.Send(&pb.ExecMessage{
ProcessID: pid,
Input: &pb.ExecMessage_Done{
Done: &pb.DoneMessage{},
},
})
return stack.Enable(err)
})
eg.Go(func() error {
defer func() {
pio.Close()
}()
err := proc.Wait()
var status uint32
var exitError *errdefs.ExitError
var errMsg string
if err != nil {
status = containerd.UnknownExitStatus
errMsg = err.Error()
}
if errors.As(err, &exitError) {
status = exitError.ExitCode
}
logrus.Debugf("|---> Exit Message %s, code=%d, error=%s", pid, status, errMsg)
sendErr := srv.Send(&pb.ExecMessage{
ProcessID: pid,
Input: &pb.ExecMessage_Exit{
Exit: &pb.ExitMessage{
Code: status,
Error: errMsg,
},
},
})
if sendErr != nil && err != nil {
return errors.Wrap(sendErr, err.Error())
} else if sendErr != nil {
return stack.Enable(sendErr)
}
if err != nil && status != 0 {
// this was a container exit error which is "normal" so
// don't return this error from the errgroup
return nil
}
return stack.Enable(err)
})
logrus.Debugf("|---> Started Message %s", pid)
err = srv.Send(&pb.ExecMessage{
ProcessID: pid,
Input: &pb.ExecMessage_Started{
Started: &pb.StartedMessage{},
},
})
if err != nil {
return stack.Enable(err)
}
// start sending Fd output back to client, this is done after
// StartedMessage so that Fd output will not potentially arrive
// to the client before "Started" as the container starts up.
for fd, file := range pio.serverReaders {
fd, file := fd, file
eg.Go(func() error {
defer func() {
file.Close()
pio.mu.Lock()
defer pio.mu.Unlock()
w := pio.processWriters[fd]
if w != nil {
w.Close()
}
delete(pio.processWriters, fd)
pio.Done()
}()
dest := &outputWriter{
stream: srv,
fd: uint32(fd),
processID: pid,
}
_, err := io.Copy(dest, file)
// ignore ErrClosedPipe, it is EOF for our usage.
if err != nil && !errors.Is(err, io.ErrClosedPipe) {
return stack.Enable(err)
}
// no error so must be EOF
logrus.Debugf("|---> File Message %s, fd=%d, EOF", pid, fd)
err = srv.Send(&pb.ExecMessage{
ProcessID: pid,
Input: &pb.ExecMessage_File{
File: &pb.FdMessage{
Fd: uint32(fd),
EOF: true,
},
},
})
return stack.Enable(err)
})
}
}
}
})
err := eg.Wait()
return stack.Enable(err)
}
func (lbf *llbBridgeForwarder) convertRef(id string) (solver.ResultProxy, error) {
if id == "" {
return nil, nil

View File

@ -3,10 +3,12 @@ package grpcclient
import (
"context"
"encoding/json"
"fmt"
"io"
"net"
"os"
"strings"
"sync"
"time"
"github.com/gogo/googleapis/google/rpc"
@ -15,13 +17,18 @@ import (
"github.com/moby/buildkit/client/llb"
"github.com/moby/buildkit/frontend/gateway/client"
pb "github.com/moby/buildkit/frontend/gateway/pb"
"github.com/moby/buildkit/identity"
"github.com/moby/buildkit/solver/errdefs"
opspb "github.com/moby/buildkit/solver/pb"
"github.com/moby/buildkit/util/apicaps"
"github.com/moby/buildkit/util/grpcerrors"
digest "github.com/opencontainers/go-digest"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
fstypes "github.com/tonistiigi/fsutil/types"
"golang.org/x/sync/errgroup"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
@ -32,9 +39,9 @@ type GrpcClient interface {
}
func New(ctx context.Context, opts map[string]string, session, product string, c pb.LLBBridgeClient, w []client.WorkerInfo) (GrpcClient, error) {
ctx, cancel := context.WithTimeout(ctx, 15*time.Second)
defer cancel()
resp, err := c.Ping(ctx, &pb.PingRequest{})
pingCtx, pingCancel := context.WithTimeout(ctx, 15*time.Second)
defer pingCancel()
resp, err := c.Ping(pingCtx, &pb.PingRequest{})
if err != nil {
return nil, err
}
@ -56,6 +63,7 @@ func New(ctx context.Context, opts map[string]string, session, product string, c
caps: pb.Caps.CapSet(resp.FrontendAPICaps),
llbCaps: opspb.Caps.CapSet(resp.LLBCaps),
requests: map[string]*pb.SolveRequest{},
execMsgs: newMessageForwarder(ctx, c),
}, nil
}
@ -167,6 +175,13 @@ func (c *grpcClient) Run(ctx context.Context, f client.BuildFunc) (retError erro
}()
}
defer func() {
err = c.execMsgs.Release()
if err != nil && retError != nil {
retError = err
}
}()
if res, err = f(ctx, c); err != nil {
return err
}
@ -253,6 +268,7 @@ type grpcClient struct {
caps apicaps.CapSet
llbCaps apicaps.CapSet
requests map[string]*pb.SolveRequest
execMsgs *messageForwarder
}
func (c *grpcClient) requestForRef(ref client.Reference) (*pb.SolveRequest, error) {
@ -423,7 +439,424 @@ func (c *grpcClient) Inputs(ctx context.Context) (map[string]llb.State, error) {
inputs[key] = llb.NewState(op)
}
return inputs, nil
}
// procMessageForwarder is created per container process to act as the
// communication channel between the process and the ExecProcess message
// stream.
type procMessageForwarder struct {
done chan struct{}
closeOnce sync.Once
msgs chan *pb.ExecMessage
}
func newProcMessageForwarder() *procMessageForwarder {
return &procMessageForwarder{
done: make(chan struct{}),
msgs: make(chan *pb.ExecMessage),
}
}
func (b *procMessageForwarder) Send(ctx context.Context, m *pb.ExecMessage) {
select {
case <-ctx.Done():
case <-b.done:
b.closeOnce.Do(func() {
close(b.msgs)
})
case b.msgs <- m:
}
}
func (b *procMessageForwarder) Recv(ctx context.Context) *pb.ExecMessage {
select {
case <-ctx.Done():
case <-b.done:
case m := <-b.msgs:
return m
}
return nil
}
func (b *procMessageForwarder) Close() {
close(b.done)
b.Recv(context.Background()) // flush any messages in queue
b.Send(context.Background(), nil) // ensure channel is closed
}
// messageForwarder manages a single grpc stream for ExecProcess to facilitate
// a pub/sub message channel for each new process started from the client
// connection.
type messageForwarder struct {
client pb.LLBBridgeClient
ctx context.Context
cancel func()
eg *errgroup.Group
mu sync.Mutex
pids map[string]*procMessageForwarder
stream pb.LLBBridge_ExecProcessClient
// startOnce used to only start the exec message forwarder once,
// so we only have one exec stream per client
startOnce sync.Once
// startErr tracks the error when initializing the stream, it will
// be returned on subsequent calls to Start
startErr error
}
func newMessageForwarder(ctx context.Context, client pb.LLBBridgeClient) *messageForwarder {
ctx, cancel := context.WithCancel(ctx)
eg, ctx := errgroup.WithContext(ctx)
return &messageForwarder{
client: client,
pids: map[string]*procMessageForwarder{},
ctx: ctx,
cancel: cancel,
eg: eg,
}
}
func (m *messageForwarder) Start() (err error) {
defer func() {
if err != nil {
m.startErr = err
}
}()
if m.startErr != nil {
return m.startErr
}
m.startOnce.Do(func() {
m.stream, err = m.client.ExecProcess(m.ctx)
if err != nil {
return
}
m.eg.Go(func() error {
for {
msg, err := m.stream.Recv()
if errors.Is(err, io.EOF) || grpcerrors.Code(err) == codes.Canceled {
return nil
}
logrus.Debugf("|<--- %s", debugMessage(msg))
if err != nil {
return err
}
m.mu.Lock()
msgs, ok := m.pids[msg.ProcessID]
m.mu.Unlock()
if !ok {
logrus.Debugf("Received exec message for unregistered process: %s", msg.String())
continue
}
msgs.Send(m.ctx, msg)
}
})
})
return err
}
func debugMessage(msg *pb.ExecMessage) string {
switch m := msg.GetInput().(type) {
case *pb.ExecMessage_Init:
return fmt.Sprintf("Init Message %s", msg.ProcessID)
case *pb.ExecMessage_File:
if m.File.EOF {
return fmt.Sprintf("File Message %s, fd=%d, EOF", msg.ProcessID, m.File.Fd)
}
return fmt.Sprintf("File Message %s, fd=%d, %d bytes", msg.ProcessID, m.File.Fd, len(m.File.Data))
case *pb.ExecMessage_Resize:
return fmt.Sprintf("Resize Message %s", msg.ProcessID)
case *pb.ExecMessage_Started:
return fmt.Sprintf("Started Message %s", msg.ProcessID)
case *pb.ExecMessage_Exit:
return fmt.Sprintf("Exit Message %s, code=%d, err=%s", msg.ProcessID, m.Exit.Code, m.Exit.Error)
case *pb.ExecMessage_Done:
return fmt.Sprintf("Done Message %s", msg.ProcessID)
}
return fmt.Sprintf("Unknown Message %s", msg.String())
}
func (m *messageForwarder) Send(msg *pb.ExecMessage) error {
m.mu.Lock()
_, ok := m.pids[msg.ProcessID]
defer m.mu.Unlock()
if !ok {
return errors.Errorf("Process %s has ended, not sending message %#v", msg.ProcessID, msg.Input)
}
logrus.Debugf("|---> %s", debugMessage(msg))
return m.stream.Send(msg)
}
func (m *messageForwarder) Release() error {
m.cancel()
return m.eg.Wait()
}
func (m *messageForwarder) Register(pid string) *procMessageForwarder {
m.mu.Lock()
defer m.mu.Unlock()
sender := newProcMessageForwarder()
m.pids[pid] = sender
return sender
}
func (m *messageForwarder) Deregister(pid string) {
m.mu.Lock()
defer m.mu.Unlock()
sender, ok := m.pids[pid]
if !ok {
return
}
delete(m.pids, pid)
sender.Close()
}
type msgWriter struct {
mux *messageForwarder
fd uint32
processID string
}
func (w *msgWriter) Write(msg []byte) (int, error) {
err := w.mux.Send(&pb.ExecMessage{
ProcessID: w.processID,
Input: &pb.ExecMessage_File{
File: &pb.FdMessage{
Fd: w.fd,
Data: msg,
},
},
})
if err != nil {
return 0, err
}
return len(msg), nil
}
func (c *grpcClient) NewContainer(ctx context.Context, req client.NewContainerRequest) (client.Container, error) {
id := identity.NewID()
var mounts []*opspb.Mount
for _, m := range req.Mounts {
ref, ok := m.Ref.(*reference)
if !ok {
return nil, errors.Errorf("Unexpected type for reference, got %T", m.Ref)
}
mounts = append(mounts, &opspb.Mount{
Dest: m.Dest,
Selector: m.Selector,
Readonly: m.Readonly,
MountType: m.MountType,
ResultID: ref.id,
})
}
logrus.Debugf("|---> NewContainer %s", id)
_, err := c.client.NewContainer(ctx, &pb.NewContainerRequest{
ContainerID: id,
Mounts: mounts,
})
if err != nil {
return nil, err
}
// ensure message forwarder is started, only sets up stream first time called
err = c.execMsgs.Start()
if err != nil {
return nil, err
}
return &container{
client: c.client,
id: id,
execMsgs: c.execMsgs,
}, nil
}
type container struct {
client pb.LLBBridgeClient
id string
execMsgs *messageForwarder
}
func (ctr *container) Start(ctx context.Context, req client.StartRequest) (client.ContainerProcess, error) {
pid := fmt.Sprintf("%s:%s", ctr.id, identity.NewID())
msgs := ctr.execMsgs.Register(pid)
init := &pb.InitMessage{
ContainerID: ctr.id,
Meta: &opspb.Meta{
Args: req.Args,
Env: req.Env,
Cwd: req.Cwd,
User: req.User,
},
Tty: req.Tty,
}
if req.Stdin != nil {
init.Fds = append(init.Fds, 0)
}
if req.Stdout != nil {
init.Fds = append(init.Fds, 1)
}
if req.Stderr != nil {
init.Fds = append(init.Fds, 2)
}
err := ctr.execMsgs.Send(&pb.ExecMessage{
ProcessID: pid,
Input: &pb.ExecMessage_Init{
Init: init,
},
})
if err != nil {
return nil, err
}
msg := msgs.Recv(ctx)
if msg == nil {
return nil, errors.Errorf("Failed to receive started message")
}
started := msg.GetStarted()
if started == nil {
return nil, errors.Errorf("Expecting started message, got %T", msg.GetInput())
}
eg, ctx := errgroup.WithContext(ctx)
done := make(chan struct{})
ctrProc := &containerProcess{
execMsgs: ctr.execMsgs,
id: pid,
eg: eg,
}
var stdinReader *io.PipeReader
ctrProc.eg.Go(func() error {
<-done
if stdinReader != nil {
return stdinReader.Close()
}
return nil
})
if req.Stdin != nil {
var stdinWriter io.WriteCloser
stdinReader, stdinWriter = io.Pipe()
// This go routine is intentionally not part of the errgroup because
// if os.Stdin is used for req.Stdin then this will block until
// the user closes the input, which will likely be after we are done
// with the container, so we can't Wait on it.
go func() {
io.Copy(stdinWriter, req.Stdin)
stdinWriter.Close()
}()
ctrProc.eg.Go(func() error {
m := &msgWriter{
mux: ctr.execMsgs,
processID: pid,
fd: 0,
}
_, err := io.Copy(m, stdinReader)
// ignore ErrClosedPipe, it is EOF for our usage.
if err != nil && !errors.Is(err, io.ErrClosedPipe) {
return err
}
// not an error so must be eof
return ctr.execMsgs.Send(&pb.ExecMessage{
ProcessID: pid,
Input: &pb.ExecMessage_File{
File: &pb.FdMessage{
Fd: 0,
EOF: true,
},
},
})
})
}
ctrProc.eg.Go(func() error {
var exitError error
for {
msg := msgs.Recv(ctx)
if msg == nil {
return exitError
}
if file := msg.GetFile(); file != nil {
var out io.WriteCloser
switch file.Fd {
case 1:
out = req.Stdout
case 2:
out = req.Stderr
}
if out == nil {
// if things are plumbed correctly this should never happen
return errors.Errorf("Missing writer for output fd %d", file.Fd)
}
if len(file.Data) > 0 {
_, err := out.Write(file.Data)
if err != nil {
return err
}
}
} else if exit := msg.GetExit(); exit != nil {
// capture exit message to exitError so we can return it after
// the server sends the Done message
close(done)
if exit.Code == 0 {
continue
}
exitError = errors.Wrap(
&errdefs.ExitError{
ExitCode: exit.Code,
},
exit.Error,
)
} else if serverDone := msg.GetDone(); serverDone != nil {
return exitError
} else {
return errors.Errorf("Unexpected Exec Message for pid %s: %T", pid, msg.GetInput())
}
}
})
return ctrProc, nil
}
func (ctr *container) Release(ctx context.Context) error {
logrus.Debugf("|---> ReleaseContainer %s", ctr.id)
_, err := ctr.client.ReleaseContainer(ctx, &pb.ReleaseContainerRequest{
ContainerID: ctr.id,
})
return err
}
type containerProcess struct {
execMsgs *messageForwarder
id string
eg *errgroup.Group
}
func (ctrProc *containerProcess) Wait() error {
defer ctrProc.execMsgs.Deregister(ctrProc.id)
return ctrProc.eg.Wait()
}
func (ctrProc *containerProcess) Resize(_ context.Context, size client.WinSize) error {
return ctrProc.execMsgs.Send(&pb.ExecMessage{
ProcessID: ctrProc.id,
Input: &pb.ExecMessage_Resize{
Resize: &pb.ResizeMessage{
Cols: size.Cols,
Rows: size.Rows,
},
},
})
}
type reference struct {

File diff suppressed because it is too large Load Diff

View File

@ -28,6 +28,10 @@ service LLBBridge {
rpc Return(ReturnRequest) returns (ReturnResponse);
// apicaps:CapFrontendInputs
rpc Inputs(InputsRequest) returns (InputsResponse);
rpc NewContainer(NewContainerRequest) returns (NewContainerResponse);
rpc ReleaseContainer(ReleaseContainerRequest) returns (ReleaseContainerResponse);
rpc ExecProcess(stream ExecMessage) returns (stream ExecMessage);
}
message Result {
@ -162,3 +166,69 @@ message PongResponse{
repeated moby.buildkit.v1.apicaps.APICap LLBCaps = 2 [(gogoproto.nullable) = false];
repeated moby.buildkit.v1.types.WorkerRecord Workers = 3;
}
message NewContainerRequest {
string ContainerID = 1;
// For mount input values we can use random identifiers passed with ref
repeated pb.Mount Mounts = 2;
pb.NetMode Network = 3;
pb.SecurityMode Security = 4;
}
message NewContainerResponse{}
message ReleaseContainerRequest {
string ContainerID = 1;
}
message ReleaseContainerResponse{}
message ExecMessage {
string ProcessID = 1;
oneof Input {
// InitMessage sent from client to server will start a new process in a
// container
InitMessage Init = 2;
// FdMessage used from client to server for input (stdin) and
// from server to client for output (stdout, stderr)
FdMessage File = 3;
// ResizeMessage used from client to server for terminal resize events
ResizeMessage Resize = 4;
// StartedMessage sent from server to client after InitMessage to
// indicate the process has started.
StartedMessage Started = 5;
// ExitMessage sent from server to client will contain the exit code
// when the process ends.
ExitMessage Exit = 6;
// DoneMessage from server to client will be the last message for any
// process. Note that FdMessage might be sent after ExitMessage.
DoneMessage Done = 7;
}
}
message InitMessage{
string ContainerID = 1;
pb.Meta Meta = 2;
repeated uint32 Fds = 3;
bool Tty = 4;
}
message ExitMessage {
uint32 Code = 1;
string Error = 2;
}
message StartedMessage{}
message DoneMessage{}
message FdMessage{
uint32 Fd = 1; // what fd the data was from
bool EOF = 2; // true if eof was reached
bytes Data = 3;
}
message ResizeMessage{
uint32 Rows = 1;
uint32 Cols = 2;
}

24
solver/errdefs/exec.go Normal file
View File

@ -0,0 +1,24 @@
package errdefs
import fmt "fmt"
// ExitError will be returned when the container process exits with a non-zero
// exit code.
type ExitError struct {
ExitCode uint32
Err error
}
func (err *ExitError) Error() string {
if err.Err != nil {
return err.Err.Error()
}
return fmt.Sprintf("exit code: %d", err.ExitCode)
}
func (err *ExitError) Unwrap() error {
if err.Err == nil {
return fmt.Errorf("exit code: %d", err.ExitCode)
}
return err.Err
}

View File

@ -551,6 +551,7 @@ type Mount struct {
CacheOpt *CacheOpt `protobuf:"bytes,20,opt,name=cacheOpt,proto3" json:"cacheOpt,omitempty"`
SecretOpt *SecretOpt `protobuf:"bytes,21,opt,name=secretOpt,proto3" json:"secretOpt,omitempty"`
SSHOpt *SSHOpt `protobuf:"bytes,22,opt,name=SSHOpt,proto3" json:"SSHOpt,omitempty"`
ResultID string `protobuf:"bytes,23,opt,name=resultID,proto3" json:"resultID,omitempty"`
}
func (m *Mount) Reset() { *m = Mount{} }
@ -631,6 +632,13 @@ func (m *Mount) GetSSHOpt() *SSHOpt {
return nil
}
func (m *Mount) GetResultID() string {
if m != nil {
return m.ResultID
}
return ""
}
// CacheOpt defines options specific to cache mounts
type CacheOpt struct {
// ID is an optional namespace for the mount
@ -2316,144 +2324,145 @@ func init() {
func init() { proto.RegisterFile("ops.proto", fileDescriptor_8de16154b2733812) }
var fileDescriptor_8de16154b2733812 = []byte{
// 2189 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x58, 0x4b, 0x6f, 0x1b, 0xc9,
0xf1, 0x17, 0xdf, 0x64, 0x51, 0x92, 0xf9, 0xef, 0xf5, 0xee, 0x9f, 0xab, 0x38, 0x92, 0x76, 0xec,
0x2c, 0x64, 0xd9, 0xa6, 0x00, 0x2d, 0xb0, 0x5e, 0x2c, 0x82, 0x20, 0xe2, 0xc3, 0x10, 0xd7, 0xb6,
0x28, 0x34, 0xfd, 0xc8, 0xcd, 0x18, 0x0d, 0x9b, 0xd4, 0x40, 0xe4, 0xf4, 0xa0, 0xa7, 0x69, 0x8b,
0x97, 0x1c, 0xfc, 0x09, 0x16, 0x08, 0x90, 0x5b, 0x02, 0xe4, 0x12, 0x20, 0xf7, 0x5c, 0x73, 0xdf,
0xe3, 0x22, 0xc8, 0x61, 0x91, 0xc3, 0x26, 0xb0, 0x3f, 0x47, 0x80, 0xa0, 0xaa, 0x7b, 0x1e, 0x94,
0x65, 0xd8, 0x46, 0x82, 0x9c, 0xd8, 0xfd, 0xab, 0x5f, 0x57, 0x57, 0x57, 0x55, 0xd7, 0x54, 0x13,
0x6a, 0x32, 0x8c, 0x5a, 0xa1, 0x92, 0x5a, 0xb2, 0x7c, 0x78, 0xb2, 0x71, 0x67, 0xe2, 0xeb, 0xd3,
0xf9, 0x49, 0xcb, 0x93, 0xb3, 0xbd, 0x89, 0x9c, 0xc8, 0x3d, 0x12, 0x9d, 0xcc, 0xc7, 0x34, 0xa3,
0x09, 0x8d, 0xcc, 0x12, 0xe7, 0x0f, 0x79, 0xc8, 0x0f, 0x42, 0xf6, 0x19, 0x94, 0xfd, 0x20, 0x9c,
0xeb, 0xa8, 0x99, 0xdb, 0x2e, 0xec, 0xd4, 0xf7, 0x6b, 0xad, 0xf0, 0xa4, 0xd5, 0x47, 0x84, 0x5b,
0x01, 0xdb, 0x86, 0xa2, 0x38, 0x17, 0x5e, 0x33, 0xbf, 0x9d, 0xdb, 0xa9, 0xef, 0x03, 0x12, 0x7a,
0xe7, 0xc2, 0x1b, 0x84, 0x87, 0x2b, 0x9c, 0x24, 0xec, 0x73, 0x28, 0x47, 0x72, 0xae, 0x3c, 0xd1,
0x2c, 0x10, 0x67, 0x15, 0x39, 0x43, 0x42, 0x88, 0x65, 0xa5, 0xa8, 0x69, 0xec, 0x4f, 0x45, 0xb3,
0x98, 0x6a, 0xba, 0xe7, 0x4f, 0x0d, 0x87, 0x24, 0xec, 0x3a, 0x94, 0x4e, 0xe6, 0xfe, 0x74, 0xd4,
0x2c, 0x11, 0xa5, 0x8e, 0x94, 0x36, 0x02, 0xc4, 0x31, 0x32, 0xb6, 0x03, 0xd5, 0x70, 0xea, 0xea,
0xb1, 0x54, 0xb3, 0x26, 0xa4, 0x1b, 0x1e, 0x5b, 0x8c, 0x27, 0x52, 0x76, 0x17, 0xea, 0x9e, 0x0c,
0x22, 0xad, 0x5c, 0x3f, 0xd0, 0x51, 0xb3, 0x4e, 0xe4, 0x8f, 0x91, 0xfc, 0x54, 0xaa, 0x33, 0xa1,
0x3a, 0xa9, 0x90, 0x67, 0x99, 0xed, 0x22, 0xe4, 0x65, 0xe8, 0xfc, 0x36, 0x07, 0xd5, 0x58, 0x2b,
0x73, 0x60, 0xf5, 0x40, 0x79, 0xa7, 0xbe, 0x16, 0x9e, 0x9e, 0x2b, 0xd1, 0xcc, 0x6d, 0xe7, 0x76,
0x6a, 0x7c, 0x09, 0x63, 0xeb, 0x90, 0x1f, 0x0c, 0xc9, 0x51, 0x35, 0x9e, 0x1f, 0x0c, 0x59, 0x13,
0x2a, 0x4f, 0x5c, 0xe5, 0xbb, 0x81, 0x26, 0xcf, 0xd4, 0x78, 0x3c, 0x65, 0xd7, 0xa0, 0x36, 0x18,
0x3e, 0x11, 0x2a, 0xf2, 0x65, 0x40, 0xfe, 0xa8, 0xf1, 0x14, 0x60, 0x9b, 0x00, 0x83, 0xe1, 0x3d,
0xe1, 0xa2, 0xd2, 0xa8, 0x59, 0xda, 0x2e, 0xec, 0xd4, 0x78, 0x06, 0x71, 0x7e, 0x0d, 0x25, 0x8a,
0x11, 0xfb, 0x06, 0xca, 0x23, 0x7f, 0x22, 0x22, 0x6d, 0xcc, 0x69, 0xef, 0x7f, 0xf7, 0xe3, 0xd6,
0xca, 0xdf, 0x7f, 0xdc, 0xda, 0xcd, 0x24, 0x83, 0x0c, 0x45, 0xe0, 0xc9, 0x40, 0xbb, 0x7e, 0x20,
0x54, 0xb4, 0x37, 0x91, 0x77, 0xcc, 0x92, 0x56, 0x97, 0x7e, 0xb8, 0xd5, 0xc0, 0x6e, 0x42, 0xc9,
0x0f, 0x46, 0xe2, 0x9c, 0xec, 0x2f, 0xb4, 0x3f, 0xb2, 0xaa, 0xea, 0x83, 0xb9, 0x0e, 0xe7, 0xba,
0x8f, 0x22, 0x6e, 0x18, 0xce, 0xef, 0x73, 0x50, 0x36, 0x39, 0xc0, 0xae, 0x41, 0x71, 0x26, 0xb4,
0x4b, 0xfb, 0xd7, 0xf7, 0xab, 0xe8, 0xdb, 0x87, 0x42, 0xbb, 0x9c, 0x50, 0x4c, 0xaf, 0x99, 0x9c,
0xa3, 0xef, 0xf3, 0x69, 0x7a, 0x3d, 0x44, 0x84, 0x5b, 0x01, 0xfb, 0x19, 0x54, 0x02, 0xa1, 0x5f,
0x48, 0x75, 0x46, 0x3e, 0x5a, 0x37, 0x41, 0x3f, 0x12, 0xfa, 0xa1, 0x1c, 0x09, 0x1e, 0xcb, 0xd8,
0x6d, 0xa8, 0x46, 0xc2, 0x9b, 0x2b, 0x5f, 0x2f, 0xc8, 0x5f, 0xeb, 0xfb, 0x0d, 0xca, 0x32, 0x8b,
0x11, 0x39, 0x61, 0x38, 0x7f, 0xca, 0x41, 0x11, 0xcd, 0x60, 0x0c, 0x8a, 0xae, 0x9a, 0x98, 0xec,
0xae, 0x71, 0x1a, 0xb3, 0x06, 0x14, 0x44, 0xf0, 0x9c, 0x2c, 0xaa, 0x71, 0x1c, 0x22, 0xe2, 0xbd,
0x18, 0xd9, 0x18, 0xe1, 0x10, 0xd7, 0xcd, 0x23, 0xa1, 0x6c, 0x68, 0x68, 0xcc, 0x6e, 0x42, 0x2d,
0x54, 0xf2, 0x7c, 0xf1, 0x0c, 0x57, 0x97, 0x32, 0x89, 0x87, 0x60, 0x2f, 0x78, 0xce, 0xab, 0xa1,
0x1d, 0xb1, 0x5d, 0x00, 0x71, 0xae, 0x95, 0x7b, 0x28, 0x23, 0x1d, 0x35, 0xcb, 0x74, 0x76, 0xca,
0x77, 0x04, 0xfa, 0xc7, 0x3c, 0x23, 0x75, 0xfe, 0x9a, 0x87, 0x12, 0xb9, 0x84, 0xed, 0x60, 0x04,
0xc2, 0xb9, 0x09, 0x66, 0xa1, 0xcd, 0x6c, 0x04, 0x80, 0x62, 0x9d, 0x04, 0x00, 0xe3, 0xbe, 0x81,
0xde, 0x98, 0x0a, 0x4f, 0x4b, 0x65, 0xd3, 0x2d, 0x99, 0xa3, 0xe9, 0x23, 0xcc, 0x08, 0x73, 0x1a,
0x1a, 0xb3, 0x5b, 0x50, 0x96, 0x14, 0x46, 0x3a, 0xd0, 0x5b, 0x82, 0x6b, 0x29, 0xa8, 0x5c, 0x09,
0x77, 0x24, 0x83, 0xe9, 0x82, 0x8e, 0x59, 0xe5, 0xc9, 0x9c, 0xdd, 0x82, 0x1a, 0xc5, 0xed, 0xd1,
0x22, 0x14, 0xcd, 0x32, 0xc5, 0x61, 0x2d, 0x89, 0x29, 0x82, 0x3c, 0x95, 0xe3, 0x45, 0xf5, 0x5c,
0xef, 0x54, 0x0c, 0x42, 0xdd, 0xbc, 0x9a, 0xfa, 0xab, 0x63, 0x31, 0x9e, 0x48, 0x51, 0x6d, 0x24,
0x3c, 0x25, 0x34, 0x52, 0x3f, 0x26, 0xea, 0x9a, 0x0d, 0xaf, 0x01, 0x79, 0x2a, 0x67, 0x0e, 0x94,
0x87, 0xc3, 0x43, 0x64, 0x7e, 0x92, 0x16, 0x12, 0x83, 0x70, 0x2b, 0x71, 0xfa, 0x50, 0x8d, 0xb7,
0xc1, 0x5b, 0xd9, 0xef, 0xda, 0xfb, 0x9a, 0xef, 0x77, 0xd9, 0x1d, 0xa8, 0x44, 0xa7, 0xae, 0xf2,
0x83, 0x09, 0xf9, 0x6e, 0x7d, 0xff, 0xa3, 0xc4, 0xaa, 0xa1, 0xc1, 0x51, 0x53, 0xcc, 0x71, 0x24,
0xd4, 0x12, 0x33, 0xde, 0xd0, 0xd5, 0x80, 0xc2, 0xdc, 0x1f, 0x91, 0x9e, 0x35, 0x8e, 0x43, 0x44,
0x26, 0xbe, 0xc9, 0xa5, 0x35, 0x8e, 0x43, 0x0c, 0xc8, 0x4c, 0x8e, 0x4c, 0xd9, 0x5b, 0xe3, 0x34,
0x46, 0x1f, 0xcb, 0x50, 0xfb, 0x32, 0x70, 0xa7, 0xb1, 0x8f, 0xe3, 0xb9, 0x33, 0x8d, 0xcf, 0xf7,
0x3f, 0xd9, 0xed, 0x37, 0x39, 0xa8, 0xc6, 0xb5, 0x1a, 0x0b, 0x8f, 0x3f, 0x12, 0x81, 0xf6, 0xc7,
0xbe, 0x50, 0x76, 0xe3, 0x0c, 0xc2, 0xee, 0x40, 0xc9, 0xd5, 0x5a, 0xc5, 0xd7, 0xf9, 0xff, 0xb3,
0x85, 0xbe, 0x75, 0x80, 0x92, 0x5e, 0xa0, 0xd5, 0x82, 0x1b, 0xd6, 0xc6, 0x57, 0x00, 0x29, 0x88,
0xb6, 0x9e, 0x89, 0x85, 0xd5, 0x8a, 0x43, 0x76, 0x15, 0x4a, 0xcf, 0xdd, 0xe9, 0x5c, 0xd8, 0x1c,
0x36, 0x93, 0xaf, 0xf3, 0x5f, 0xe5, 0x9c, 0xbf, 0xe4, 0xa1, 0x62, 0x0b, 0x3f, 0xbb, 0x0d, 0x15,
0x2a, 0xfc, 0xd6, 0xa2, 0xcb, 0x2f, 0x46, 0x4c, 0x61, 0x7b, 0xc9, 0x17, 0x2d, 0x63, 0xa3, 0x55,
0x65, 0xbe, 0x6c, 0xd6, 0xc6, 0xf4, 0xfb, 0x56, 0x18, 0x89, 0xb1, 0xfd, 0x74, 0xad, 0x23, 0xbb,
0x2b, 0xc6, 0x7e, 0xe0, 0xa3, 0x7f, 0x38, 0x8a, 0xd8, 0xed, 0xf8, 0xd4, 0x45, 0xd2, 0xf8, 0x49,
0x56, 0xe3, 0x9b, 0x87, 0xee, 0x43, 0x3d, 0xb3, 0xcd, 0x25, 0xa7, 0xbe, 0x91, 0x3d, 0xb5, 0xdd,
0x92, 0xd4, 0x99, 0xef, 0x6e, 0xea, 0x85, 0xff, 0xc0, 0x7f, 0x5f, 0x02, 0xa4, 0x2a, 0xdf, 0xbf,
0xb0, 0x38, 0x2f, 0x0b, 0x00, 0x83, 0x10, 0x4b, 0xe7, 0xc8, 0xa5, 0xfa, 0xbd, 0xea, 0x4f, 0x02,
0xa9, 0xc4, 0x33, 0xba, 0xaa, 0xb4, 0xbe, 0xca, 0xeb, 0x06, 0xa3, 0x1b, 0xc3, 0x0e, 0xa0, 0x3e,
0x12, 0x91, 0xa7, 0x7c, 0x4a, 0x28, 0xeb, 0xf4, 0x2d, 0x3c, 0x53, 0xaa, 0xa7, 0xd5, 0x4d, 0x19,
0xc6, 0x57, 0xd9, 0x35, 0x6c, 0x1f, 0x56, 0xc5, 0x79, 0x28, 0x95, 0xb6, 0xbb, 0x98, 0xfe, 0xe0,
0x8a, 0xe9, 0x34, 0x10, 0xa7, 0x9d, 0x78, 0x5d, 0xa4, 0x13, 0xe6, 0x42, 0xd1, 0x73, 0x43, 0xf3,
0x71, 0xac, 0xef, 0x37, 0x2f, 0xec, 0xd7, 0x71, 0x43, 0xe3, 0xb4, 0xf6, 0x17, 0x78, 0xd6, 0x97,
0xff, 0xd8, 0xba, 0x95, 0xf9, 0x22, 0xce, 0xe4, 0xc9, 0x62, 0x8f, 0xf2, 0xe5, 0xcc, 0xd7, 0x7b,
0x73, 0xed, 0x4f, 0xf7, 0xdc, 0xd0, 0x47, 0x75, 0xb8, 0xb0, 0xdf, 0xe5, 0xa4, 0x7a, 0xe3, 0x17,
0xd0, 0xb8, 0x68, 0xf7, 0x87, 0xc4, 0x60, 0xe3, 0x2e, 0xd4, 0x12, 0x3b, 0xde, 0xb5, 0xb0, 0x9a,
0x0d, 0xde, 0x9f, 0x73, 0x50, 0x36, 0xb7, 0x8a, 0xdd, 0x85, 0xda, 0x54, 0x7a, 0x2e, 0x1a, 0x10,
0xb7, 0x68, 0x9f, 0xa6, 0x97, 0xae, 0xf5, 0x20, 0x96, 0x19, 0xaf, 0xa6, 0x5c, 0x4c, 0x32, 0x3f,
0x18, 0xcb, 0xf8, 0x16, 0xac, 0xa7, 0x8b, 0xfa, 0xc1, 0x58, 0x72, 0x23, 0xdc, 0xb8, 0x0f, 0xeb,
0xcb, 0x2a, 0x2e, 0xb1, 0xf3, 0xfa, 0x72, 0xba, 0x52, 0x5d, 0x4e, 0x16, 0x65, 0xcd, 0xbe, 0x0b,
0xb5, 0x04, 0x67, 0xbb, 0x6f, 0x1a, 0xbe, 0x9a, 0x5d, 0x99, 0xb1, 0xd5, 0x99, 0x02, 0xa4, 0xa6,
0x61, 0xb1, 0xc2, 0x5e, 0x30, 0x70, 0x67, 0x71, 0x93, 0x95, 0xcc, 0xe9, 0xdb, 0xe6, 0x6a, 0x97,
0x4c, 0x59, 0xe5, 0x34, 0x66, 0x2d, 0x80, 0x51, 0x72, 0x61, 0xdf, 0x72, 0x8d, 0x33, 0x0c, 0x67,
0x00, 0xd5, 0xd8, 0x08, 0xb6, 0x0d, 0xf5, 0xc8, 0xee, 0x8c, 0x9d, 0x0f, 0x6e, 0x57, 0xe2, 0x59,
0x08, 0x3b, 0x18, 0xe5, 0x06, 0x13, 0xb1, 0xd4, 0xc1, 0x70, 0x44, 0xb8, 0x15, 0x38, 0x4f, 0xa1,
0x44, 0x00, 0x5e, 0xb3, 0x48, 0xbb, 0x4a, 0xdb, 0x66, 0xc8, 0x34, 0x07, 0x32, 0xa2, 0x6d, 0xdb,
0x45, 0x4c, 0x44, 0x6e, 0x08, 0xec, 0x06, 0xb6, 0x20, 0x23, 0xeb, 0xd1, 0xcb, 0x78, 0x28, 0x76,
0x7e, 0x0e, 0xd5, 0x18, 0xc6, 0x93, 0x3f, 0xf0, 0x03, 0x61, 0x4d, 0xa4, 0x31, 0x36, 0x91, 0x9d,
0x53, 0x57, 0xb9, 0x9e, 0x16, 0xa6, 0x0d, 0x28, 0xf1, 0x14, 0x70, 0xae, 0x43, 0x3d, 0x73, 0x7b,
0x30, 0xdd, 0x9e, 0x50, 0x18, 0xcd, 0x1d, 0x36, 0x13, 0xe7, 0x25, 0xb6, 0xb8, 0x71, 0xd7, 0xf2,
0x53, 0x80, 0x53, 0xad, 0xc3, 0x67, 0xd4, 0xc6, 0x58, 0xdf, 0xd7, 0x10, 0x21, 0x06, 0xdb, 0x82,
0x3a, 0x4e, 0x22, 0x2b, 0x37, 0xf9, 0x4e, 0x2b, 0x22, 0x43, 0xf8, 0x09, 0xd4, 0xc6, 0xc9, 0xf2,
0x82, 0x0d, 0x5d, 0xbc, 0xfa, 0x53, 0xa8, 0x06, 0xd2, 0xca, 0x4c, 0x57, 0x55, 0x09, 0x24, 0x89,
0x9c, 0x5b, 0xf0, 0x7f, 0x6f, 0xf4, 0xe3, 0xec, 0x13, 0x28, 0x8f, 0xfd, 0xa9, 0xa6, 0xa2, 0x8f,
0x8d, 0x9a, 0x9d, 0x39, 0xff, 0xca, 0x01, 0xa4, 0x91, 0xc5, 0x7c, 0xc5, 0xea, 0x8d, 0x9c, 0x55,
0x53, 0xad, 0xa7, 0x50, 0x9d, 0xd9, 0x3a, 0x60, 0x63, 0x76, 0x6d, 0x39, 0x1b, 0x5a, 0x71, 0x99,
0x30, 0x15, 0x62, 0xdf, 0x56, 0x88, 0x0f, 0xe9, 0x99, 0x93, 0x1d, 0xa8, 0x19, 0xc9, 0xbe, 0x7d,
0x20, 0xbd, 0x68, 0xdc, 0x4a, 0x36, 0xee, 0xc3, 0xda, 0xd2, 0x96, 0xef, 0xf9, 0x4d, 0x48, 0xeb,
0x59, 0xf6, 0x96, 0xdd, 0x86, 0xb2, 0x69, 0x22, 0x31, 0x25, 0x70, 0x64, 0xd5, 0xd0, 0x98, 0x3a,
0x86, 0xe3, 0xf8, 0x05, 0xd2, 0x3f, 0x76, 0xf6, 0xa1, 0x6c, 0x9e, 0x58, 0x6c, 0x07, 0x2a, 0xae,
0x67, 0xae, 0x63, 0xa6, 0x24, 0xa0, 0xf0, 0x80, 0x60, 0x1e, 0x8b, 0x9d, 0xbf, 0xe5, 0x01, 0x52,
0xfc, 0x03, 0xba, 0xd2, 0xaf, 0x61, 0x3d, 0x12, 0x9e, 0x0c, 0x46, 0xae, 0x5a, 0x90, 0xd4, 0x3e,
0x25, 0x2e, 0x5b, 0x72, 0x81, 0x99, 0xe9, 0x50, 0x0b, 0xef, 0xee, 0x50, 0x77, 0xa0, 0xe8, 0xc9,
0x70, 0x61, 0x3f, 0x14, 0x6c, 0xf9, 0x20, 0x1d, 0x19, 0x2e, 0xf0, 0x41, 0x89, 0x0c, 0xd6, 0x82,
0xf2, 0xec, 0x8c, 0x1e, 0x9d, 0xa6, 0x61, 0xbf, 0xba, 0xcc, 0x7d, 0x78, 0x86, 0x63, 0x7c, 0xa2,
0x1a, 0x16, 0xbb, 0x05, 0xa5, 0xd9, 0xd9, 0xc8, 0x57, 0xd4, 0xdb, 0xd6, 0x4d, 0x67, 0x98, 0xa5,
0x77, 0x7d, 0x85, 0x0f, 0x51, 0xe2, 0x30, 0x07, 0xf2, 0x6a, 0xd6, 0xac, 0x10, 0xb3, 0x71, 0xc1,
0x9b, 0xb3, 0xc3, 0x15, 0x9e, 0x57, 0xb3, 0x76, 0x15, 0xca, 0xc6, 0xaf, 0xce, 0x1f, 0x0b, 0xb0,
0xbe, 0x6c, 0x25, 0xe6, 0x41, 0xa4, 0xbc, 0x38, 0x0f, 0x22, 0xe5, 0x25, 0xcd, 0x7b, 0x3e, 0xd3,
0xbc, 0x3b, 0x50, 0x92, 0x2f, 0x02, 0xa1, 0xb2, 0xaf, 0xeb, 0xce, 0xa9, 0x7c, 0x11, 0x60, 0x9b,
0x6a, 0x44, 0x4b, 0x5d, 0x5f, 0xc9, 0x76, 0x7d, 0x37, 0x60, 0x6d, 0x2c, 0xa7, 0x53, 0xf9, 0x62,
0xb8, 0x98, 0x4d, 0xfd, 0xe0, 0xcc, 0xb6, 0x7e, 0xcb, 0x20, 0xdb, 0x81, 0x2b, 0x23, 0x5f, 0xa1,
0x39, 0x1d, 0x19, 0x68, 0x11, 0xd0, 0x7b, 0x05, 0x79, 0x17, 0x61, 0xf6, 0x0d, 0x6c, 0xbb, 0x5a,
0x8b, 0x59, 0xa8, 0x1f, 0x07, 0xa1, 0xeb, 0x9d, 0x75, 0xa5, 0x47, 0x77, 0x76, 0x16, 0xba, 0xda,
0x3f, 0xf1, 0xa7, 0xf8, 0x34, 0xab, 0xd0, 0xd2, 0x77, 0xf2, 0xd8, 0xe7, 0xb0, 0xee, 0x29, 0xe1,
0x6a, 0xd1, 0x15, 0x91, 0x3e, 0x76, 0xf5, 0x69, 0xb3, 0x4a, 0x2b, 0x2f, 0xa0, 0x78, 0x06, 0x17,
0xad, 0x7d, 0xea, 0x4f, 0x47, 0x9e, 0xab, 0x46, 0xcd, 0x9a, 0x39, 0xc3, 0x12, 0xc8, 0x5a, 0xc0,
0x08, 0xe8, 0xcd, 0x42, 0xbd, 0x48, 0xa8, 0x40, 0xd4, 0x4b, 0x24, 0x58, 0x38, 0xb5, 0x3f, 0x13,
0x91, 0x76, 0x67, 0x21, 0xfd, 0x2b, 0x50, 0xe0, 0x29, 0xe0, 0x7c, 0x9b, 0x83, 0xc6, 0xc5, 0x14,
0x41, 0x07, 0x87, 0x68, 0xa6, 0xbd, 0x6c, 0x38, 0x4e, 0x9c, 0x9e, 0xcf, 0x38, 0x3d, 0xfe, 0x42,
0x15, 0x32, 0x5f, 0xa8, 0x24, 0x80, 0xc5, 0xb7, 0x07, 0x70, 0xc9, 0xa4, 0xd2, 0x45, 0x93, 0x7e,
0x97, 0x83, 0x2b, 0x17, 0xd2, 0xf0, 0xbd, 0x2d, 0xda, 0x86, 0xfa, 0xcc, 0x3d, 0x13, 0xc7, 0xae,
0xa2, 0xe0, 0x16, 0x4c, 0x0b, 0x97, 0x81, 0xfe, 0x0b, 0xf6, 0x05, 0xb0, 0x9a, 0xcd, 0xfd, 0x4b,
0x6d, 0x8b, 0x43, 0x79, 0x24, 0xf5, 0x3d, 0x39, 0xb7, 0x5f, 0xbf, 0x38, 0x94, 0x31, 0xf8, 0x66,
0xc0, 0x0b, 0x97, 0x04, 0xdc, 0x39, 0x82, 0x6a, 0x6c, 0x20, 0xdb, 0xb2, 0x4f, 0xf5, 0x5c, 0xfa,
0x97, 0xd1, 0xe3, 0x48, 0x28, 0xb4, 0xdd, 0xbc, 0xdb, 0x3f, 0x83, 0xd2, 0x44, 0xc9, 0x79, 0x68,
0x6b, 0xeb, 0x12, 0xc3, 0x48, 0x9c, 0x21, 0x54, 0x2c, 0xc2, 0x76, 0xa1, 0x7c, 0xb2, 0x38, 0x8a,
0x9b, 0x0f, 0x7b, 0xb1, 0x71, 0x3e, 0xb2, 0x0c, 0xac, 0x16, 0x86, 0xc1, 0xae, 0x42, 0xf1, 0x64,
0xd1, 0xef, 0x9a, 0x07, 0x19, 0xd6, 0x1c, 0x9c, 0xb5, 0xcb, 0xc6, 0x20, 0xe7, 0x01, 0xac, 0x66,
0xd7, 0xa1, 0x53, 0x32, 0x4d, 0x0d, 0x8d, 0xd3, 0xe2, 0x9a, 0x7f, 0x47, 0x71, 0xdd, 0xdd, 0x81,
0x8a, 0xfd, 0x53, 0x84, 0xd5, 0xa0, 0xf4, 0xf8, 0x68, 0xd8, 0x7b, 0xd4, 0x58, 0x61, 0x55, 0x28,
0x1e, 0x0e, 0x86, 0x8f, 0x1a, 0x39, 0x1c, 0x1d, 0x0d, 0x8e, 0x7a, 0x8d, 0xfc, 0xee, 0x4d, 0x58,
0xcd, 0xfe, 0x2d, 0xc2, 0xea, 0x50, 0x19, 0x1e, 0x1c, 0x75, 0xdb, 0x83, 0x5f, 0x35, 0x56, 0xd8,
0x2a, 0x54, 0xfb, 0x47, 0xc3, 0x5e, 0xe7, 0x31, 0xef, 0x35, 0x72, 0xbb, 0xbf, 0x84, 0x5a, 0xf2,
0x72, 0x47, 0x0d, 0xed, 0xfe, 0x51, 0xb7, 0xb1, 0xc2, 0x00, 0xca, 0xc3, 0x5e, 0x87, 0xf7, 0x50,
0x6f, 0x05, 0x0a, 0xc3, 0xe1, 0x61, 0x23, 0x8f, 0xbb, 0x76, 0x0e, 0x3a, 0x87, 0xbd, 0x46, 0x01,
0x87, 0x8f, 0x1e, 0x1e, 0xdf, 0x1b, 0x36, 0x8a, 0xbb, 0x5f, 0xc2, 0x95, 0x0b, 0x2f, 0x67, 0x5a,
0x7d, 0x78, 0xc0, 0x7b, 0xa8, 0xa9, 0x0e, 0x95, 0x63, 0xde, 0x7f, 0x72, 0xf0, 0xa8, 0xd7, 0xc8,
0xa1, 0xe0, 0xc1, 0xa0, 0x73, 0xbf, 0xd7, 0x6d, 0xe4, 0xdb, 0xd7, 0xbe, 0x7b, 0xb5, 0x99, 0xfb,
0xfe, 0xd5, 0x66, 0xee, 0x87, 0x57, 0x9b, 0xb9, 0x7f, 0xbe, 0xda, 0xcc, 0x7d, 0xfb, 0x7a, 0x73,
0xe5, 0xfb, 0xd7, 0x9b, 0x2b, 0x3f, 0xbc, 0xde, 0x5c, 0x39, 0x29, 0xd3, 0x9f, 0x94, 0x5f, 0xfc,
0x3b, 0x00, 0x00, 0xff, 0xff, 0x7e, 0x60, 0x46, 0x7d, 0xe4, 0x14, 0x00, 0x00,
// 2201 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x58, 0xcd, 0x6f, 0x1b, 0xc7,
0x15, 0x17, 0xbf, 0xc9, 0x47, 0x49, 0x66, 0x27, 0x4e, 0xc2, 0xa8, 0xae, 0xa4, 0x6c, 0xdc, 0x40,
0x96, 0x6d, 0x0a, 0x50, 0x80, 0x38, 0x08, 0x8a, 0xa2, 0xe2, 0x87, 0x21, 0xc6, 0xb6, 0x28, 0x0c,
0xfd, 0xd1, 0x9b, 0xb1, 0x5a, 0x0e, 0xa9, 0x85, 0xc8, 0x9d, 0xc5, 0xec, 0xd0, 0x16, 0x2f, 0x3d,
0xf8, 0x2f, 0x08, 0x50, 0xa0, 0xb7, 0x16, 0xe8, 0xa5, 0x40, 0xef, 0xbd, 0xf6, 0x9e, 0x63, 0x0e,
0x3d, 0x04, 0x3d, 0xa4, 0x85, 0x7d, 0xef, 0x7f, 0x50, 0xa0, 0x78, 0x6f, 0x66, 0x3f, 0x28, 0xcb,
0xb0, 0x8d, 0x16, 0x3d, 0xed, 0xcc, 0x7b, 0xbf, 0x79, 0xf3, 0xe6, 0x7d, 0xcd, 0x9b, 0x85, 0x9a,
0x0c, 0xa3, 0x56, 0xa8, 0xa4, 0x96, 0x2c, 0x1f, 0x9e, 0x6c, 0xdc, 0x9e, 0xf8, 0xfa, 0x74, 0x7e,
0xd2, 0xf2, 0xe4, 0x6c, 0x6f, 0x22, 0x27, 0x72, 0x8f, 0x58, 0x27, 0xf3, 0x31, 0xcd, 0x68, 0x42,
0x23, 0xb3, 0xc4, 0xf9, 0x63, 0x1e, 0xf2, 0x83, 0x90, 0x7d, 0x0a, 0x65, 0x3f, 0x08, 0xe7, 0x3a,
0x6a, 0xe6, 0xb6, 0x0b, 0x3b, 0xf5, 0xfd, 0x5a, 0x2b, 0x3c, 0x69, 0xf5, 0x91, 0xc2, 0x2d, 0x83,
0x6d, 0x43, 0x51, 0x9c, 0x0b, 0xaf, 0x99, 0xdf, 0xce, 0xed, 0xd4, 0xf7, 0x01, 0x01, 0xbd, 0x73,
0xe1, 0x0d, 0xc2, 0xc3, 0x15, 0x4e, 0x1c, 0xf6, 0x39, 0x94, 0x23, 0x39, 0x57, 0x9e, 0x68, 0x16,
0x08, 0xb3, 0x8a, 0x98, 0x21, 0x51, 0x08, 0x65, 0xb9, 0x28, 0x69, 0xec, 0x4f, 0x45, 0xb3, 0x98,
0x4a, 0xba, 0xeb, 0x4f, 0x0d, 0x86, 0x38, 0xec, 0x33, 0x28, 0x9d, 0xcc, 0xfd, 0xe9, 0xa8, 0x59,
0x22, 0x48, 0x1d, 0x21, 0x6d, 0x24, 0x10, 0xc6, 0xf0, 0xd8, 0x0e, 0x54, 0xc3, 0xa9, 0xab, 0xc7,
0x52, 0xcd, 0x9a, 0x90, 0x6e, 0x78, 0x6c, 0x69, 0x3c, 0xe1, 0xb2, 0x3b, 0x50, 0xf7, 0x64, 0x10,
0x69, 0xe5, 0xfa, 0x81, 0x8e, 0x9a, 0x75, 0x02, 0x7f, 0x88, 0xe0, 0x27, 0x52, 0x9d, 0x09, 0xd5,
0x49, 0x99, 0x3c, 0x8b, 0x6c, 0x17, 0x21, 0x2f, 0x43, 0xe7, 0x77, 0x39, 0xa8, 0xc6, 0x52, 0x99,
0x03, 0xab, 0x07, 0xca, 0x3b, 0xf5, 0xb5, 0xf0, 0xf4, 0x5c, 0x89, 0x66, 0x6e, 0x3b, 0xb7, 0x53,
0xe3, 0x4b, 0x34, 0xb6, 0x0e, 0xf9, 0xc1, 0x90, 0x0c, 0x55, 0xe3, 0xf9, 0xc1, 0x90, 0x35, 0xa1,
0xf2, 0xd8, 0x55, 0xbe, 0x1b, 0x68, 0xb2, 0x4c, 0x8d, 0xc7, 0x53, 0x76, 0x0d, 0x6a, 0x83, 0xe1,
0x63, 0xa1, 0x22, 0x5f, 0x06, 0x64, 0x8f, 0x1a, 0x4f, 0x09, 0x6c, 0x13, 0x60, 0x30, 0xbc, 0x2b,
0x5c, 0x14, 0x1a, 0x35, 0x4b, 0xdb, 0x85, 0x9d, 0x1a, 0xcf, 0x50, 0x9c, 0xdf, 0x40, 0x89, 0x7c,
0xc4, 0xbe, 0x81, 0xf2, 0xc8, 0x9f, 0x88, 0x48, 0x1b, 0x75, 0xda, 0xfb, 0xdf, 0xfd, 0xb8, 0xb5,
0xf2, 0xf7, 0x1f, 0xb7, 0x76, 0x33, 0xc1, 0x20, 0x43, 0x11, 0x78, 0x32, 0xd0, 0xae, 0x1f, 0x08,
0x15, 0xed, 0x4d, 0xe4, 0x6d, 0xb3, 0xa4, 0xd5, 0xa5, 0x0f, 0xb7, 0x12, 0xd8, 0x0d, 0x28, 0xf9,
0xc1, 0x48, 0x9c, 0x93, 0xfe, 0x85, 0xf6, 0x07, 0x56, 0x54, 0x7d, 0x30, 0xd7, 0xe1, 0x5c, 0xf7,
0x91, 0xc5, 0x0d, 0xc2, 0xf9, 0x43, 0x0e, 0xca, 0x26, 0x06, 0xd8, 0x35, 0x28, 0xce, 0x84, 0x76,
0x69, 0xff, 0xfa, 0x7e, 0x15, 0x6d, 0xfb, 0x40, 0x68, 0x97, 0x13, 0x15, 0xc3, 0x6b, 0x26, 0xe7,
0x68, 0xfb, 0x7c, 0x1a, 0x5e, 0x0f, 0x90, 0xc2, 0x2d, 0x83, 0xfd, 0x1c, 0x2a, 0x81, 0xd0, 0xcf,
0xa5, 0x3a, 0x23, 0x1b, 0xad, 0x1b, 0xa7, 0x1f, 0x09, 0xfd, 0x40, 0x8e, 0x04, 0x8f, 0x79, 0xec,
0x16, 0x54, 0x23, 0xe1, 0xcd, 0x95, 0xaf, 0x17, 0x64, 0xaf, 0xf5, 0xfd, 0x06, 0x45, 0x99, 0xa5,
0x11, 0x38, 0x41, 0x38, 0x7f, 0xce, 0x41, 0x11, 0xd5, 0x60, 0x0c, 0x8a, 0xae, 0x9a, 0x98, 0xe8,
0xae, 0x71, 0x1a, 0xb3, 0x06, 0x14, 0x44, 0xf0, 0x8c, 0x34, 0xaa, 0x71, 0x1c, 0x22, 0xc5, 0x7b,
0x3e, 0xb2, 0x3e, 0xc2, 0x21, 0xae, 0x9b, 0x47, 0x42, 0x59, 0xd7, 0xd0, 0x98, 0xdd, 0x80, 0x5a,
0xa8, 0xe4, 0xf9, 0xe2, 0x29, 0xae, 0x2e, 0x65, 0x02, 0x0f, 0x89, 0xbd, 0xe0, 0x19, 0xaf, 0x86,
0x76, 0xc4, 0x76, 0x01, 0xc4, 0xb9, 0x56, 0xee, 0xa1, 0x8c, 0x74, 0xd4, 0x2c, 0xd3, 0xd9, 0x29,
0xde, 0x91, 0xd0, 0x3f, 0xe6, 0x19, 0xae, 0xf3, 0xaf, 0x3c, 0x94, 0xc8, 0x24, 0x6c, 0x07, 0x3d,
0x10, 0xce, 0x8d, 0x33, 0x0b, 0x6d, 0x66, 0x3d, 0x00, 0xe4, 0xeb, 0xc4, 0x01, 0xe8, 0xf7, 0x0d,
0xb4, 0xc6, 0x54, 0x78, 0x5a, 0x2a, 0x1b, 0x6e, 0xc9, 0x1c, 0x55, 0x1f, 0x61, 0x44, 0x98, 0xd3,
0xd0, 0x98, 0xdd, 0x84, 0xb2, 0x24, 0x37, 0xd2, 0x81, 0xde, 0xe0, 0x5c, 0x0b, 0x41, 0xe1, 0x4a,
0xb8, 0x23, 0x19, 0x4c, 0x17, 0x74, 0xcc, 0x2a, 0x4f, 0xe6, 0xec, 0x26, 0xd4, 0xc8, 0x6f, 0x0f,
0x17, 0xa1, 0x68, 0x96, 0xc9, 0x0f, 0x6b, 0x89, 0x4f, 0x91, 0xc8, 0x53, 0x3e, 0x26, 0xaa, 0xe7,
0x7a, 0xa7, 0x62, 0x10, 0xea, 0xe6, 0xd5, 0xd4, 0x5e, 0x1d, 0x4b, 0xe3, 0x09, 0x17, 0xc5, 0x46,
0xc2, 0x53, 0x42, 0x23, 0xf4, 0x43, 0x82, 0xae, 0x59, 0xf7, 0x1a, 0x22, 0x4f, 0xf9, 0xcc, 0x81,
0xf2, 0x70, 0x78, 0x88, 0xc8, 0x8f, 0xd2, 0x42, 0x62, 0x28, 0xdc, 0x72, 0xcc, 0x19, 0xa2, 0xf9,
0x54, 0xf7, 0xbb, 0xcd, 0x8f, 0x8d, 0x81, 0xe2, 0xb9, 0xd3, 0x87, 0x6a, 0xac, 0x02, 0x66, 0x6c,
0xbf, 0x6b, 0x73, 0x39, 0xdf, 0xef, 0xb2, 0xdb, 0x50, 0x89, 0x4e, 0x5d, 0xe5, 0x07, 0x13, 0xb2,
0xeb, 0xfa, 0xfe, 0x07, 0x89, 0xc6, 0x43, 0x43, 0xc7, 0x5d, 0x62, 0x8c, 0x23, 0xa1, 0x96, 0xa8,
0xf8, 0x9a, 0xac, 0x06, 0x14, 0xe6, 0xfe, 0x88, 0xe4, 0xac, 0x71, 0x1c, 0x22, 0x65, 0xe2, 0x9b,
0x38, 0x5b, 0xe3, 0x38, 0x44, 0x67, 0xcd, 0xe4, 0xc8, 0x94, 0xc4, 0x35, 0x4e, 0x63, 0xd4, 0x5d,
0x86, 0xda, 0x97, 0x81, 0x3b, 0x8d, 0xed, 0x1f, 0xcf, 0x9d, 0x69, 0x7c, 0xf6, 0xff, 0xcb, 0x6e,
0xbf, 0xcd, 0x41, 0x35, 0xae, 0xe3, 0x58, 0x94, 0xfc, 0x91, 0x08, 0xb4, 0x3f, 0xf6, 0x85, 0xb2,
0x1b, 0x67, 0x28, 0xec, 0x36, 0x94, 0x5c, 0xad, 0x55, 0x9c, 0xea, 0x1f, 0x67, 0x2f, 0x81, 0xd6,
0x01, 0x72, 0x7a, 0x81, 0x56, 0x0b, 0x6e, 0x50, 0x1b, 0x5f, 0x01, 0xa4, 0x44, 0xd4, 0xf5, 0x4c,
0x2c, 0xac, 0x54, 0x1c, 0xb2, 0xab, 0x50, 0x7a, 0xe6, 0x4e, 0xe7, 0xc2, 0xc6, 0xb7, 0x99, 0x7c,
0x9d, 0xff, 0x2a, 0xe7, 0xfc, 0x35, 0x0f, 0x15, 0x7b, 0x29, 0xb0, 0x5b, 0x50, 0xa1, 0x4b, 0xc1,
0x6a, 0x74, 0x79, 0xd2, 0xc4, 0x10, 0xb6, 0x97, 0xdc, 0x76, 0x19, 0x1d, 0xad, 0x28, 0x73, 0xeb,
0x59, 0x1d, 0xd3, 0xbb, 0xaf, 0x30, 0x12, 0x63, 0x7b, 0xad, 0xad, 0x23, 0xba, 0x2b, 0xc6, 0x7e,
0xe0, 0xa3, 0x7d, 0x38, 0xb2, 0xd8, 0xad, 0xf8, 0xd4, 0x45, 0x92, 0xf8, 0x51, 0x56, 0xe2, 0xeb,
0x87, 0xee, 0x43, 0x3d, 0xb3, 0xcd, 0x25, 0xa7, 0xbe, 0x9e, 0x3d, 0xb5, 0xdd, 0x92, 0xc4, 0x99,
0x3b, 0x39, 0xb5, 0xc2, 0x7f, 0x61, 0xbf, 0x2f, 0x01, 0x52, 0x91, 0xef, 0x5e, 0x74, 0x9c, 0x17,
0x05, 0x80, 0x41, 0x88, 0x65, 0x75, 0xe4, 0x52, 0x6d, 0x5f, 0xf5, 0x27, 0x81, 0x54, 0xe2, 0x29,
0xa5, 0x31, 0xad, 0xaf, 0xf2, 0xba, 0xa1, 0x51, 0xc6, 0xb0, 0x03, 0xa8, 0x8f, 0x44, 0xe4, 0x29,
0x9f, 0x02, 0xca, 0x1a, 0x7d, 0x0b, 0xcf, 0x94, 0xca, 0x69, 0x75, 0x53, 0x84, 0xb1, 0x55, 0x76,
0x0d, 0xdb, 0x87, 0x55, 0x71, 0x1e, 0x4a, 0xa5, 0xed, 0x2e, 0xa6, 0x77, 0xb8, 0x62, 0xba, 0x10,
0xa4, 0xd3, 0x4e, 0xbc, 0x2e, 0xd2, 0x09, 0x73, 0xa1, 0xe8, 0xb9, 0xa1, 0xb9, 0x38, 0xeb, 0xfb,
0xcd, 0x0b, 0xfb, 0x75, 0xdc, 0xd0, 0x18, 0xad, 0xfd, 0x05, 0x9e, 0xf5, 0xc5, 0x3f, 0xb6, 0x6e,
0x66, 0x6e, 0xcb, 0x99, 0x3c, 0x59, 0xec, 0x51, 0xbc, 0x9c, 0xf9, 0x7a, 0x6f, 0xae, 0xfd, 0xe9,
0x9e, 0x1b, 0xfa, 0x28, 0x0e, 0x17, 0xf6, 0xbb, 0x9c, 0x44, 0x6f, 0xfc, 0x12, 0x1a, 0x17, 0xf5,
0x7e, 0x1f, 0x1f, 0x6c, 0xdc, 0x81, 0x5a, 0xa2, 0xc7, 0xdb, 0x16, 0x56, 0xb3, 0xce, 0xfb, 0x4b,
0x0e, 0xca, 0x26, 0xab, 0xd8, 0x1d, 0xa8, 0x4d, 0xa5, 0xe7, 0xa2, 0x02, 0x71, 0xfb, 0xf6, 0x49,
0x9a, 0x74, 0xad, 0xfb, 0x31, 0xcf, 0x58, 0x35, 0xc5, 0x62, 0x90, 0xf9, 0xc1, 0x58, 0xc6, 0x59,
0xb0, 0x9e, 0x2e, 0xea, 0x07, 0x63, 0xc9, 0x0d, 0x73, 0xe3, 0x1e, 0xac, 0x2f, 0x8b, 0xb8, 0x44,
0xcf, 0xcf, 0x96, 0xc3, 0x95, 0x6a, 0x76, 0xb2, 0x28, 0xab, 0xf6, 0x1d, 0xa8, 0x25, 0x74, 0xb6,
0xfb, 0xba, 0xe2, 0xab, 0xd9, 0x95, 0x19, 0x5d, 0x9d, 0x29, 0x40, 0xaa, 0x1a, 0x16, 0x2b, 0xec,
0x13, 0x03, 0x77, 0x16, 0x37, 0x60, 0xc9, 0x9c, 0xee, 0x3d, 0x57, 0xbb, 0xa4, 0xca, 0x2a, 0xa7,
0x31, 0x6b, 0x01, 0x8c, 0x92, 0x84, 0x7d, 0x43, 0x1a, 0x67, 0x10, 0xce, 0x00, 0xaa, 0xb1, 0x12,
0x6c, 0x1b, 0xea, 0x91, 0xdd, 0x19, 0xbb, 0x22, 0xdc, 0xae, 0xc4, 0xb3, 0x24, 0xec, 0x6e, 0x94,
0x1b, 0x4c, 0xc4, 0x52, 0x77, 0xc3, 0x91, 0xc2, 0x2d, 0xc3, 0x79, 0x02, 0x25, 0x22, 0x60, 0x9a,
0x45, 0xda, 0x55, 0xda, 0x36, 0x4a, 0xa6, 0x71, 0x90, 0x11, 0x6d, 0xdb, 0x2e, 0x62, 0x20, 0x72,
0x03, 0x60, 0xd7, 0xb1, 0x3d, 0x19, 0x59, 0x8b, 0x5e, 0x86, 0x43, 0xb6, 0xf3, 0x0b, 0xa8, 0xc6,
0x64, 0x3c, 0xf9, 0x7d, 0x3f, 0x10, 0x56, 0x45, 0x1a, 0x63, 0x83, 0xd9, 0x39, 0x75, 0x95, 0xeb,
0x69, 0x61, 0x5a, 0x84, 0x12, 0x4f, 0x09, 0xce, 0x67, 0x50, 0xcf, 0x64, 0x0f, 0x86, 0xdb, 0x63,
0x72, 0xa3, 0xc9, 0x61, 0x33, 0x71, 0x5e, 0x60, 0xfb, 0x1b, 0x77, 0x34, 0x3f, 0x03, 0x38, 0xd5,
0x3a, 0x7c, 0x4a, 0x2d, 0x8e, 0xb5, 0x7d, 0x0d, 0x29, 0x84, 0x60, 0x5b, 0x50, 0xc7, 0x49, 0x64,
0xf9, 0x26, 0xde, 0x69, 0x45, 0x64, 0x00, 0x3f, 0x85, 0xda, 0x38, 0x59, 0x5e, 0xb0, 0xae, 0x8b,
0x57, 0x7f, 0x02, 0xd5, 0x40, 0x5a, 0x9e, 0xe9, 0xb8, 0x2a, 0x81, 0x24, 0x96, 0x73, 0x13, 0x7e,
0xf2, 0x5a, 0xaf, 0xce, 0x3e, 0x82, 0xf2, 0xd8, 0x9f, 0x6a, 0x2a, 0xfa, 0xd8, 0xc4, 0xd9, 0x99,
0xf3, 0xef, 0x1c, 0x40, 0xea, 0x59, 0x8c, 0x57, 0xac, 0xde, 0x88, 0x59, 0x35, 0xd5, 0x7a, 0x0a,
0xd5, 0x99, 0xad, 0x03, 0xd6, 0x67, 0xd7, 0x96, 0xa3, 0xa1, 0x15, 0x97, 0x09, 0x53, 0x21, 0xf6,
0x6d, 0x85, 0x78, 0x9f, 0x7e, 0x3a, 0xd9, 0x81, 0x1a, 0x95, 0xec, 0xbb, 0x08, 0xd2, 0x44, 0xe3,
0x96, 0xb3, 0x71, 0x0f, 0xd6, 0x96, 0xb6, 0x7c, 0xc7, 0x3b, 0x21, 0xad, 0x67, 0xd9, 0x2c, 0xbb,
0x05, 0x65, 0xd3, 0x60, 0x62, 0x48, 0xe0, 0xc8, 0x8a, 0xa1, 0x31, 0x75, 0x0c, 0xc7, 0xf1, 0xeb,
0xa4, 0x7f, 0xec, 0xec, 0x43, 0xd9, 0x3c, 0xbf, 0xd8, 0x0e, 0x54, 0x5c, 0xcf, 0xa4, 0x63, 0xa6,
0x24, 0x20, 0xf3, 0x80, 0xc8, 0x3c, 0x66, 0x3b, 0x7f, 0xcb, 0x03, 0xa4, 0xf4, 0xf7, 0xe8, 0x58,
0xbf, 0x86, 0xf5, 0x48, 0x78, 0x32, 0x18, 0xb9, 0x6a, 0x41, 0x5c, 0xfb, 0xcc, 0xb8, 0x6c, 0xc9,
0x05, 0x64, 0xa6, 0x7b, 0x2d, 0xbc, 0xbd, 0x7b, 0xdd, 0x81, 0xa2, 0x27, 0xc3, 0x85, 0xbd, 0x28,
0xd8, 0xf2, 0x41, 0x3a, 0x32, 0x5c, 0xe0, 0x63, 0x13, 0x11, 0xac, 0x05, 0xe5, 0xd9, 0x19, 0x3d,
0x48, 0x4d, 0x33, 0x7f, 0x75, 0x19, 0xfb, 0xe0, 0x0c, 0xc7, 0xf8, 0x7c, 0x35, 0x28, 0x76, 0x13,
0x4a, 0xb3, 0xb3, 0x91, 0xaf, 0xa8, 0xef, 0xad, 0x9b, 0xce, 0x30, 0x0b, 0xef, 0xfa, 0x0a, 0x1f,
0xa9, 0x84, 0x61, 0x0e, 0xe4, 0xd5, 0xac, 0x59, 0x21, 0x64, 0xe3, 0x82, 0x35, 0x67, 0x87, 0x2b,
0x3c, 0xaf, 0x66, 0xed, 0x2a, 0x94, 0x8d, 0x5d, 0x9d, 0x3f, 0x15, 0x60, 0x7d, 0x59, 0x4b, 0x8c,
0x83, 0x48, 0x79, 0x71, 0x1c, 0x44, 0xca, 0x4b, 0x1a, 0xfb, 0x7c, 0xa6, 0xb1, 0x77, 0xa0, 0x24,
0x9f, 0x07, 0x42, 0x65, 0x5f, 0xde, 0x9d, 0x53, 0xf9, 0x3c, 0xc0, 0x36, 0xd5, 0xb0, 0x96, 0xba,
0xbe, 0x92, 0xed, 0xfa, 0xae, 0xc3, 0xda, 0x58, 0x4e, 0xa7, 0xf2, 0xf9, 0x70, 0x31, 0x9b, 0xfa,
0xc1, 0x99, 0x6d, 0xfd, 0x96, 0x89, 0x6c, 0x07, 0xae, 0x8c, 0x7c, 0x85, 0xea, 0x74, 0x64, 0xa0,
0x45, 0x40, 0x6f, 0x19, 0xc4, 0x5d, 0x24, 0xb3, 0x6f, 0x60, 0xdb, 0xd5, 0x5a, 0xcc, 0x42, 0xfd,
0x28, 0x08, 0x5d, 0xef, 0xac, 0x2b, 0x3d, 0xca, 0xd9, 0x59, 0xe8, 0x6a, 0xff, 0xc4, 0x9f, 0xe2,
0xb3, 0xad, 0x42, 0x4b, 0xdf, 0x8a, 0x63, 0x9f, 0xc3, 0xba, 0xa7, 0x84, 0xab, 0x45, 0x57, 0x44,
0xfa, 0xd8, 0xd5, 0xa7, 0xcd, 0x2a, 0xad, 0xbc, 0x40, 0xc5, 0x33, 0xb8, 0xa8, 0xed, 0x13, 0x7f,
0x3a, 0xf2, 0x5c, 0x35, 0x6a, 0xd6, 0xcc, 0x19, 0x96, 0x88, 0xac, 0x05, 0x8c, 0x08, 0xbd, 0x59,
0xa8, 0x17, 0x09, 0x14, 0x08, 0x7a, 0x09, 0x07, 0x0b, 0xa7, 0xf6, 0x67, 0x22, 0xd2, 0xee, 0x2c,
0xa4, 0x3f, 0x06, 0x05, 0x9e, 0x12, 0x9c, 0x6f, 0x73, 0xd0, 0xb8, 0x18, 0x22, 0x68, 0xe0, 0x10,
0xd5, 0xb4, 0xc9, 0x86, 0xe3, 0xc4, 0xe8, 0xf9, 0x8c, 0xd1, 0xe3, 0x1b, 0xaa, 0x90, 0xb9, 0xa1,
0x12, 0x07, 0x16, 0xdf, 0xec, 0xc0, 0x25, 0x95, 0x4a, 0x17, 0x55, 0xfa, 0x7d, 0x0e, 0xae, 0x5c,
0x08, 0xc3, 0x77, 0xd6, 0x68, 0x1b, 0xea, 0x33, 0xf7, 0x4c, 0x1c, 0xbb, 0x8a, 0x9c, 0x5b, 0x30,
0x2d, 0x5c, 0x86, 0xf4, 0x3f, 0xd0, 0x2f, 0x80, 0xd5, 0x6c, 0xec, 0x5f, 0xaa, 0x5b, 0xec, 0xca,
0x23, 0xa9, 0xef, 0xca, 0xb9, 0xbd, 0xfd, 0x62, 0x57, 0xc6, 0xc4, 0xd7, 0x1d, 0x5e, 0xb8, 0xc4,
0xe1, 0xce, 0x11, 0x54, 0x63, 0x05, 0xd9, 0x96, 0x7d, 0xc6, 0xe7, 0xd2, 0xdf, 0x49, 0x8f, 0x22,
0xa1, 0x50, 0x77, 0xf3, 0xa6, 0xff, 0x14, 0x4a, 0x13, 0x25, 0xe7, 0xa1, 0xad, 0xad, 0x4b, 0x08,
0xc3, 0x71, 0x86, 0x50, 0xb1, 0x14, 0xb6, 0x0b, 0xe5, 0x93, 0xc5, 0x51, 0xdc, 0x7c, 0xd8, 0xc4,
0xc6, 0xf9, 0xc8, 0x22, 0xb0, 0x5a, 0x18, 0x04, 0xbb, 0x0a, 0xc5, 0x93, 0x45, 0xbf, 0x6b, 0x1e,
0x64, 0x58, 0x73, 0x70, 0xd6, 0x2e, 0x1b, 0x85, 0x9c, 0xfb, 0xb0, 0x9a, 0x5d, 0x87, 0x46, 0xc9,
0x34, 0x35, 0x34, 0x4e, 0x8b, 0x6b, 0xfe, 0x2d, 0xc5, 0x75, 0x77, 0x07, 0x2a, 0xf6, 0x87, 0x09,
0xab, 0x41, 0xe9, 0xd1, 0xd1, 0xb0, 0xf7, 0xb0, 0xb1, 0xc2, 0xaa, 0x50, 0x3c, 0x1c, 0x0c, 0x1f,
0x36, 0x72, 0x38, 0x3a, 0x1a, 0x1c, 0xf5, 0x1a, 0xf9, 0xdd, 0x1b, 0xb0, 0x9a, 0xfd, 0x65, 0xc2,
0xea, 0x50, 0x19, 0x1e, 0x1c, 0x75, 0xdb, 0x83, 0x5f, 0x37, 0x56, 0xd8, 0x2a, 0x54, 0xfb, 0x47,
0xc3, 0x5e, 0xe7, 0x11, 0xef, 0x35, 0x72, 0xbb, 0xbf, 0x82, 0x5a, 0xf2, 0xaa, 0x47, 0x09, 0xed,
0xfe, 0x51, 0xb7, 0xb1, 0xc2, 0x00, 0xca, 0xc3, 0x5e, 0x87, 0xf7, 0x50, 0x6e, 0x05, 0x0a, 0xc3,
0xe1, 0x61, 0x23, 0x8f, 0xbb, 0x76, 0x0e, 0x3a, 0x87, 0xbd, 0x46, 0x01, 0x87, 0x0f, 0x1f, 0x1c,
0xdf, 0x1d, 0x36, 0x8a, 0xbb, 0x5f, 0xc2, 0x95, 0x0b, 0x2f, 0x67, 0x5a, 0x7d, 0x78, 0xc0, 0x7b,
0x28, 0xa9, 0x0e, 0x95, 0x63, 0xde, 0x7f, 0x7c, 0xf0, 0xb0, 0xd7, 0xc8, 0x21, 0xe3, 0xfe, 0xa0,
0x73, 0xaf, 0xd7, 0x6d, 0xe4, 0xdb, 0xd7, 0xbe, 0x7b, 0xb9, 0x99, 0xfb, 0xfe, 0xe5, 0x66, 0xee,
0x87, 0x97, 0x9b, 0xb9, 0x7f, 0xbe, 0xdc, 0xcc, 0x7d, 0xfb, 0x6a, 0x73, 0xe5, 0xfb, 0x57, 0x9b,
0x2b, 0x3f, 0xbc, 0xda, 0x5c, 0x39, 0x29, 0xd3, 0x0f, 0xcc, 0x2f, 0xfe, 0x13, 0x00, 0x00, 0xff,
0xff, 0x69, 0x08, 0x3c, 0x7a, 0x00, 0x15, 0x00, 0x00,
}
func (m *Op) Marshal() (dAtA []byte, err error) {
@ -2865,6 +2874,15 @@ func (m *Mount) MarshalToSizedBuffer(dAtA []byte) (int, error) {
_ = i
var l int
_ = l
if len(m.ResultID) > 0 {
i -= len(m.ResultID)
copy(dAtA[i:], m.ResultID)
i = encodeVarintOps(dAtA, i, uint64(len(m.ResultID)))
i--
dAtA[i] = 0x1
i--
dAtA[i] = 0xba
}
if m.SSHOpt != nil {
{
size, err := m.SSHOpt.MarshalToSizedBuffer(dAtA[:i])
@ -4704,6 +4722,10 @@ func (m *Mount) Size() (n int) {
l = m.SSHOpt.Size()
n += 2 + l + sovOps(uint64(l))
}
l = len(m.ResultID)
if l > 0 {
n += 2 + l + sovOps(uint64(l))
}
return n
}
@ -6661,6 +6683,38 @@ func (m *Mount) Unmarshal(dAtA []byte) error {
return err
}
iNdEx = postIndex
case 23:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field ResultID", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowOps
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthOps
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthOps
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.ResultID = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipOps(dAtA[iNdEx:])

View File

@ -81,6 +81,7 @@ message Mount {
CacheOpt cacheOpt = 20;
SecretOpt secretOpt = 21;
SSHOpt SSHOpt = 22;
string resultID = 23;
}
// MountType defines a type of a mount from a supported set