update gateway to add ability to run and exec into containers
Signed-off-by: Cory Bennett <cbennett@netflix.com>v0.8
parent
a57f8a2dcc
commit
355e937e15
|
@ -108,3 +108,18 @@ func (g *gatewayClientForBuild) Inputs(ctx context.Context, in *gatewayapi.Input
|
||||||
ctx = buildid.AppendToOutgoingContext(ctx, g.buildID)
|
ctx = buildid.AppendToOutgoingContext(ctx, g.buildID)
|
||||||
return g.gateway.Inputs(ctx, in, opts...)
|
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...)
|
||||||
|
}
|
||||||
|
|
|
@ -1,16 +1,21 @@
|
||||||
package client
|
package client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/moby/buildkit/client/llb"
|
"github.com/moby/buildkit/client/llb"
|
||||||
"github.com/moby/buildkit/frontend/gateway/client"
|
"github.com/moby/buildkit/frontend/gateway/client"
|
||||||
gatewayapi "github.com/moby/buildkit/frontend/gateway/pb"
|
gatewayapi "github.com/moby/buildkit/frontend/gateway/pb"
|
||||||
"github.com/moby/buildkit/identity"
|
"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/moby/buildkit/util/testutil/integration"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
@ -23,6 +28,10 @@ func TestClientGatewayIntegration(t *testing.T) {
|
||||||
testClientGatewayEmptySolve,
|
testClientGatewayEmptySolve,
|
||||||
testNoBuildID,
|
testNoBuildID,
|
||||||
testUnknownBuildID,
|
testUnknownBuildID,
|
||||||
|
testClientGatewayContainerExecPipe,
|
||||||
|
testClientGatewayContainerCancelOnRelease,
|
||||||
|
testClientGatewayContainerPID1Fail,
|
||||||
|
testClientGatewayContainerPID1Exit,
|
||||||
}, integration.WithMirroredImages(integration.OfficialImages("busybox:latest")))
|
}, integration.WithMirroredImages(integration.OfficialImages("busybox:latest")))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,3 +184,367 @@ func testUnknownBuildID(t *testing.T, sb integration.Sandbox) {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
require.Contains(t, err.Error(), "no such job")
|
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
|
||||||
|
}
|
||||||
|
|
|
@ -152,3 +152,27 @@ func (gwf *GatewayForwarder) StatFile(ctx context.Context, req *gwapi.StatFileRe
|
||||||
}
|
}
|
||||||
return fwd.StatFile(ctx, req)
|
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)
|
||||||
|
}
|
||||||
|
|
|
@ -2,6 +2,8 @@ package containerdexecutor
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -19,6 +21,7 @@ import (
|
||||||
"github.com/moby/buildkit/executor/oci"
|
"github.com/moby/buildkit/executor/oci"
|
||||||
"github.com/moby/buildkit/identity"
|
"github.com/moby/buildkit/identity"
|
||||||
"github.com/moby/buildkit/snapshot"
|
"github.com/moby/buildkit/snapshot"
|
||||||
|
"github.com/moby/buildkit/solver/errdefs"
|
||||||
"github.com/moby/buildkit/solver/pb"
|
"github.com/moby/buildkit/solver/pb"
|
||||||
"github.com/moby/buildkit/util/network"
|
"github.com/moby/buildkit/util/network"
|
||||||
"github.com/opencontainers/runtime-spec/specs-go"
|
"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)}
|
cioOpts := []cio.Opt{cio.WithStreams(process.Stdin, process.Stdout, process.Stderr)}
|
||||||
if meta.Tty {
|
if meta.Tty {
|
||||||
cioOpts = append(cioOpts, cio.WithTerminal)
|
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
|
spec.Process.Env = process.Meta.Env
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fixProcessOutput(&process)
|
||||||
cioOpts := []cio.Opt{cio.WithStreams(process.Stdin, process.Stdout, process.Stderr)}
|
cioOpts := []cio.Opt{cio.WithStreams(process.Stdin, process.Stdout, process.Stderr)}
|
||||||
if meta.Tty {
|
if meta.Tty {
|
||||||
cioOpts = append(cioOpts, cio.WithTerminal)
|
cioOpts = append(cioOpts, cio.WithTerminal)
|
||||||
|
@ -300,6 +305,19 @@ func (w *containerdExecutor) Exec(ctx context.Context, id string, process execut
|
||||||
return err
|
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 {
|
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
|
// 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.
|
// 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()
|
cancel()
|
||||||
}
|
}
|
||||||
if status.ExitCode() != 0 {
|
if status.ExitCode() != 0 {
|
||||||
exitErr := &executor.ExitError{
|
exitErr := &errdefs.ExitError{
|
||||||
ExitCode: status.ExitCode(),
|
ExitCode: status.ExitCode(),
|
||||||
Err: status.Error(),
|
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
|
||||||
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@ package executor
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
|
@ -55,24 +54,3 @@ type HostIP struct {
|
||||||
Host string
|
Host string
|
||||||
IP net.IP
|
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
|
|
||||||
}
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ import (
|
||||||
"github.com/moby/buildkit/executor"
|
"github.com/moby/buildkit/executor"
|
||||||
"github.com/moby/buildkit/executor/oci"
|
"github.com/moby/buildkit/executor/oci"
|
||||||
"github.com/moby/buildkit/identity"
|
"github.com/moby/buildkit/identity"
|
||||||
|
"github.com/moby/buildkit/solver/errdefs"
|
||||||
"github.com/moby/buildkit/solver/pb"
|
"github.com/moby/buildkit/solver/pb"
|
||||||
"github.com/moby/buildkit/util/network"
|
"github.com/moby/buildkit/util/network"
|
||||||
rootlessspecconv "github.com/moby/buildkit/util/rootless/specconv"
|
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)
|
close(ended)
|
||||||
|
|
||||||
if status != 0 || err != nil {
|
if status != 0 || err != nil {
|
||||||
exitErr := &executor.ExitError{
|
exitErr := &errdefs.ExitError{
|
||||||
ExitCode: uint32(status),
|
ExitCode: uint32(status),
|
||||||
Err: err,
|
Err: err,
|
||||||
}
|
}
|
||||||
|
@ -418,7 +419,7 @@ func (w *runcExecutor) Exec(ctx context.Context, id string, process executor.Pro
|
||||||
|
|
||||||
var exitError *exec.ExitError
|
var exitError *exec.ExitError
|
||||||
if errors.As(err, &exitError) {
|
if errors.As(err, &exitError) {
|
||||||
err = &executor.ExitError{
|
err = &errdefs.ExitError{
|
||||||
ExitCode: uint32(exitError.ExitCode()),
|
ExitCode: uint32(exitError.ExitCode()),
|
||||||
Err: err,
|
Err: err,
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"io"
|
||||||
|
|
||||||
"github.com/moby/buildkit/client/llb"
|
"github.com/moby/buildkit/client/llb"
|
||||||
"github.com/moby/buildkit/solver/pb"
|
"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)
|
ResolveImageConfig(ctx context.Context, ref string, opt llb.ResolveImageConfigOpt) (digest.Digest, []byte, error)
|
||||||
BuildOpts() BuildOpts
|
BuildOpts() BuildOpts
|
||||||
Inputs(ctx context.Context) (map[string]llb.State, error)
|
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 {
|
type Reference interface {
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
|
@ -9,8 +9,10 @@ import (
|
||||||
clienttypes "github.com/moby/buildkit/client"
|
clienttypes "github.com/moby/buildkit/client"
|
||||||
"github.com/moby/buildkit/client/llb"
|
"github.com/moby/buildkit/client/llb"
|
||||||
"github.com/moby/buildkit/frontend"
|
"github.com/moby/buildkit/frontend"
|
||||||
|
"github.com/moby/buildkit/frontend/gateway"
|
||||||
"github.com/moby/buildkit/frontend/gateway/client"
|
"github.com/moby/buildkit/frontend/gateway/client"
|
||||||
gwpb "github.com/moby/buildkit/frontend/gateway/pb"
|
gwpb "github.com/moby/buildkit/frontend/gateway/pb"
|
||||||
|
"github.com/moby/buildkit/identity"
|
||||||
"github.com/moby/buildkit/solver"
|
"github.com/moby/buildkit/solver"
|
||||||
opspb "github.com/moby/buildkit/solver/pb"
|
opspb "github.com/moby/buildkit/solver/pb"
|
||||||
"github.com/moby/buildkit/util/apicaps"
|
"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 {
|
type ref struct {
|
||||||
solver.ResultProxy
|
solver.ResultProxy
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/containerd/containerd"
|
||||||
"github.com/docker/distribution/reference"
|
"github.com/docker/distribution/reference"
|
||||||
gogotypes "github.com/gogo/protobuf/types"
|
gogotypes "github.com/gogo/protobuf/types"
|
||||||
"github.com/golang/protobuf/ptypes/any"
|
"github.com/golang/protobuf/ptypes/any"
|
||||||
|
@ -23,20 +24,25 @@ import (
|
||||||
"github.com/moby/buildkit/executor"
|
"github.com/moby/buildkit/executor"
|
||||||
"github.com/moby/buildkit/exporter/containerimage/exptypes"
|
"github.com/moby/buildkit/exporter/containerimage/exptypes"
|
||||||
"github.com/moby/buildkit/frontend"
|
"github.com/moby/buildkit/frontend"
|
||||||
|
gwclient "github.com/moby/buildkit/frontend/gateway/client"
|
||||||
pb "github.com/moby/buildkit/frontend/gateway/pb"
|
pb "github.com/moby/buildkit/frontend/gateway/pb"
|
||||||
"github.com/moby/buildkit/identity"
|
"github.com/moby/buildkit/identity"
|
||||||
"github.com/moby/buildkit/solver"
|
"github.com/moby/buildkit/solver"
|
||||||
|
"github.com/moby/buildkit/solver/errdefs"
|
||||||
opspb "github.com/moby/buildkit/solver/pb"
|
opspb "github.com/moby/buildkit/solver/pb"
|
||||||
"github.com/moby/buildkit/util/apicaps"
|
"github.com/moby/buildkit/util/apicaps"
|
||||||
"github.com/moby/buildkit/util/grpcerrors"
|
"github.com/moby/buildkit/util/grpcerrors"
|
||||||
|
"github.com/moby/buildkit/util/stack"
|
||||||
"github.com/moby/buildkit/util/tracing"
|
"github.com/moby/buildkit/util/tracing"
|
||||||
"github.com/moby/buildkit/worker"
|
"github.com/moby/buildkit/worker"
|
||||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"golang.org/x/net/http2"
|
"golang.org/x/net/http2"
|
||||||
|
"golang.org/x/sync/errgroup"
|
||||||
spb "google.golang.org/genproto/googleapis/rpc/status"
|
spb "google.golang.org/genproto/googleapis/rpc/status"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/health"
|
"google.golang.org/grpc/health"
|
||||||
"google.golang.org/grpc/health/grpc_health_v1"
|
"google.golang.org/grpc/health/grpc_health_v1"
|
||||||
"google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
|
@ -316,6 +322,7 @@ func newBridgeForwarder(ctx context.Context, llbBridge frontend.FrontendLLBBridg
|
||||||
workers: workers,
|
workers: workers,
|
||||||
inputs: inputs,
|
inputs: inputs,
|
||||||
sid: sid,
|
sid: sid,
|
||||||
|
ctrs: map[string]Container{},
|
||||||
}
|
}
|
||||||
return lbf
|
return lbf
|
||||||
}
|
}
|
||||||
|
@ -416,6 +423,8 @@ type llbBridgeForwarder struct {
|
||||||
isErrServerClosed bool
|
isErrServerClosed bool
|
||||||
sid string
|
sid string
|
||||||
*pipe
|
*pipe
|
||||||
|
ctrs map[string]Container
|
||||||
|
ctrsMu sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func (lbf *llbBridgeForwarder) ResolveImageConfig(ctx context.Context, req *pb.ResolveImageConfigRequest) (*pb.ResolveImageConfigResponse, error) {
|
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
|
}, 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) {
|
func (lbf *llbBridgeForwarder) convertRef(id string) (solver.ResultProxy, error) {
|
||||||
if id == "" {
|
if id == "" {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
|
|
@ -3,10 +3,12 @@ package grpcclient
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gogo/googleapis/google/rpc"
|
"github.com/gogo/googleapis/google/rpc"
|
||||||
|
@ -15,13 +17,18 @@ import (
|
||||||
"github.com/moby/buildkit/client/llb"
|
"github.com/moby/buildkit/client/llb"
|
||||||
"github.com/moby/buildkit/frontend/gateway/client"
|
"github.com/moby/buildkit/frontend/gateway/client"
|
||||||
pb "github.com/moby/buildkit/frontend/gateway/pb"
|
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"
|
opspb "github.com/moby/buildkit/solver/pb"
|
||||||
"github.com/moby/buildkit/util/apicaps"
|
"github.com/moby/buildkit/util/apicaps"
|
||||||
"github.com/moby/buildkit/util/grpcerrors"
|
"github.com/moby/buildkit/util/grpcerrors"
|
||||||
digest "github.com/opencontainers/go-digest"
|
digest "github.com/opencontainers/go-digest"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
fstypes "github.com/tonistiigi/fsutil/types"
|
fstypes "github.com/tonistiigi/fsutil/types"
|
||||||
|
"golang.org/x/sync/errgroup"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/status"
|
"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) {
|
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)
|
pingCtx, pingCancel := context.WithTimeout(ctx, 15*time.Second)
|
||||||
defer cancel()
|
defer pingCancel()
|
||||||
resp, err := c.Ping(ctx, &pb.PingRequest{})
|
resp, err := c.Ping(pingCtx, &pb.PingRequest{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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),
|
caps: pb.Caps.CapSet(resp.FrontendAPICaps),
|
||||||
llbCaps: opspb.Caps.CapSet(resp.LLBCaps),
|
llbCaps: opspb.Caps.CapSet(resp.LLBCaps),
|
||||||
requests: map[string]*pb.SolveRequest{},
|
requests: map[string]*pb.SolveRequest{},
|
||||||
|
execMsgs: newMessageForwarder(ctx, c),
|
||||||
}, nil
|
}, 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 {
|
if res, err = f(ctx, c); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -253,6 +268,7 @@ type grpcClient struct {
|
||||||
caps apicaps.CapSet
|
caps apicaps.CapSet
|
||||||
llbCaps apicaps.CapSet
|
llbCaps apicaps.CapSet
|
||||||
requests map[string]*pb.SolveRequest
|
requests map[string]*pb.SolveRequest
|
||||||
|
execMsgs *messageForwarder
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *grpcClient) requestForRef(ref client.Reference) (*pb.SolveRequest, error) {
|
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)
|
inputs[key] = llb.NewState(op)
|
||||||
}
|
}
|
||||||
return inputs, nil
|
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 {
|
type reference struct {
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -28,6 +28,10 @@ service LLBBridge {
|
||||||
rpc Return(ReturnRequest) returns (ReturnResponse);
|
rpc Return(ReturnRequest) returns (ReturnResponse);
|
||||||
// apicaps:CapFrontendInputs
|
// apicaps:CapFrontendInputs
|
||||||
rpc Inputs(InputsRequest) returns (InputsResponse);
|
rpc Inputs(InputsRequest) returns (InputsResponse);
|
||||||
|
|
||||||
|
rpc NewContainer(NewContainerRequest) returns (NewContainerResponse);
|
||||||
|
rpc ReleaseContainer(ReleaseContainerRequest) returns (ReleaseContainerResponse);
|
||||||
|
rpc ExecProcess(stream ExecMessage) returns (stream ExecMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
message Result {
|
message Result {
|
||||||
|
@ -162,3 +166,69 @@ message PongResponse{
|
||||||
repeated moby.buildkit.v1.apicaps.APICap LLBCaps = 2 [(gogoproto.nullable) = false];
|
repeated moby.buildkit.v1.apicaps.APICap LLBCaps = 2 [(gogoproto.nullable) = false];
|
||||||
repeated moby.buildkit.v1.types.WorkerRecord Workers = 3;
|
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;
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -551,6 +551,7 @@ type Mount struct {
|
||||||
CacheOpt *CacheOpt `protobuf:"bytes,20,opt,name=cacheOpt,proto3" json:"cacheOpt,omitempty"`
|
CacheOpt *CacheOpt `protobuf:"bytes,20,opt,name=cacheOpt,proto3" json:"cacheOpt,omitempty"`
|
||||||
SecretOpt *SecretOpt `protobuf:"bytes,21,opt,name=secretOpt,proto3" json:"secretOpt,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"`
|
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{} }
|
func (m *Mount) Reset() { *m = Mount{} }
|
||||||
|
@ -631,6 +632,13 @@ func (m *Mount) GetSSHOpt() *SSHOpt {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Mount) GetResultID() string {
|
||||||
|
if m != nil {
|
||||||
|
return m.ResultID
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
// CacheOpt defines options specific to cache mounts
|
// CacheOpt defines options specific to cache mounts
|
||||||
type CacheOpt struct {
|
type CacheOpt struct {
|
||||||
// ID is an optional namespace for the mount
|
// ID is an optional namespace for the mount
|
||||||
|
@ -2316,144 +2324,145 @@ func init() {
|
||||||
func init() { proto.RegisterFile("ops.proto", fileDescriptor_8de16154b2733812) }
|
func init() { proto.RegisterFile("ops.proto", fileDescriptor_8de16154b2733812) }
|
||||||
|
|
||||||
var fileDescriptor_8de16154b2733812 = []byte{
|
var fileDescriptor_8de16154b2733812 = []byte{
|
||||||
// 2189 bytes of a gzipped FileDescriptorProto
|
// 2201 bytes of a gzipped FileDescriptorProto
|
||||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x58, 0x4b, 0x6f, 0x1b, 0xc9,
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x58, 0xcd, 0x6f, 0x1b, 0xc7,
|
||||||
0xf1, 0x17, 0xdf, 0x64, 0x51, 0x92, 0xf9, 0xef, 0xf5, 0xee, 0x9f, 0xab, 0x38, 0x92, 0x76, 0xec,
|
0x15, 0x17, 0xbf, 0xc9, 0x47, 0x49, 0x66, 0x27, 0x4e, 0xc2, 0xa8, 0xae, 0xa4, 0x6c, 0xdc, 0x40,
|
||||||
0x2c, 0x64, 0xd9, 0xa6, 0x00, 0x2d, 0xb0, 0x5e, 0x2c, 0x82, 0x20, 0xe2, 0xc3, 0x10, 0xd7, 0xb6,
|
0x96, 0x6d, 0x0a, 0x50, 0x80, 0x38, 0x08, 0x8a, 0xa2, 0xe2, 0x87, 0x21, 0xc6, 0xb6, 0x28, 0x0c,
|
||||||
0x28, 0x34, 0xfd, 0xc8, 0xcd, 0x18, 0x0d, 0x9b, 0xd4, 0x40, 0xe4, 0xf4, 0xa0, 0xa7, 0x69, 0x8b,
|
0xfd, 0xd1, 0x9b, 0xb1, 0x5a, 0x0e, 0xa9, 0x85, 0xc8, 0x9d, 0xc5, 0xec, 0xd0, 0x16, 0x2f, 0x3d,
|
||||||
0x97, 0x1c, 0xfc, 0x09, 0x16, 0x08, 0x90, 0x5b, 0x02, 0xe4, 0x12, 0x20, 0xf7, 0x5c, 0x73, 0xdf,
|
0xf8, 0x2f, 0x08, 0x50, 0xa0, 0xb7, 0x16, 0xe8, 0xa5, 0x40, 0xef, 0xbd, 0xf6, 0x9e, 0x63, 0x0e,
|
||||||
0xe3, 0x22, 0xc8, 0x61, 0x91, 0xc3, 0x26, 0xb0, 0x3f, 0x47, 0x80, 0xa0, 0xaa, 0x7b, 0x1e, 0x94,
|
0x3d, 0x04, 0x3d, 0xa4, 0x85, 0x7d, 0xef, 0x7f, 0x50, 0xa0, 0x78, 0x6f, 0x66, 0x3f, 0x28, 0xcb,
|
||||||
0x65, 0xd8, 0x46, 0x82, 0x9c, 0xd8, 0xfd, 0xab, 0x5f, 0x57, 0x57, 0x57, 0x55, 0xd7, 0x54, 0x13,
|
0xb0, 0x8d, 0x16, 0x3d, 0xed, 0xcc, 0x7b, 0xbf, 0x79, 0xf3, 0xe6, 0x7d, 0xcd, 0x9b, 0x85, 0x9a,
|
||||||
0x6a, 0x32, 0x8c, 0x5a, 0xa1, 0x92, 0x5a, 0xb2, 0x7c, 0x78, 0xb2, 0x71, 0x67, 0xe2, 0xeb, 0xd3,
|
0x0c, 0xa3, 0x56, 0xa8, 0xa4, 0x96, 0x2c, 0x1f, 0x9e, 0x6c, 0xdc, 0x9e, 0xf8, 0xfa, 0x74, 0x7e,
|
||||||
0xf9, 0x49, 0xcb, 0x93, 0xb3, 0xbd, 0x89, 0x9c, 0xc8, 0x3d, 0x12, 0x9d, 0xcc, 0xc7, 0x34, 0xa3,
|
0xd2, 0xf2, 0xe4, 0x6c, 0x6f, 0x22, 0x27, 0x72, 0x8f, 0x58, 0x27, 0xf3, 0x31, 0xcd, 0x68, 0x42,
|
||||||
0x09, 0x8d, 0xcc, 0x12, 0xe7, 0x0f, 0x79, 0xc8, 0x0f, 0x42, 0xf6, 0x19, 0x94, 0xfd, 0x20, 0x9c,
|
0x23, 0xb3, 0xc4, 0xf9, 0x63, 0x1e, 0xf2, 0x83, 0x90, 0x7d, 0x0a, 0x65, 0x3f, 0x08, 0xe7, 0x3a,
|
||||||
0xeb, 0xa8, 0x99, 0xdb, 0x2e, 0xec, 0xd4, 0xf7, 0x6b, 0xad, 0xf0, 0xa4, 0xd5, 0x47, 0x84, 0x5b,
|
0x6a, 0xe6, 0xb6, 0x0b, 0x3b, 0xf5, 0xfd, 0x5a, 0x2b, 0x3c, 0x69, 0xf5, 0x91, 0xc2, 0x2d, 0x83,
|
||||||
0x01, 0xdb, 0x86, 0xa2, 0x38, 0x17, 0x5e, 0x33, 0xbf, 0x9d, 0xdb, 0xa9, 0xef, 0x03, 0x12, 0x7a,
|
0x6d, 0x43, 0x51, 0x9c, 0x0b, 0xaf, 0x99, 0xdf, 0xce, 0xed, 0xd4, 0xf7, 0x01, 0x01, 0xbd, 0x73,
|
||||||
0xe7, 0xc2, 0x1b, 0x84, 0x87, 0x2b, 0x9c, 0x24, 0xec, 0x73, 0x28, 0x47, 0x72, 0xae, 0x3c, 0xd1,
|
0xe1, 0x0d, 0xc2, 0xc3, 0x15, 0x4e, 0x1c, 0xf6, 0x39, 0x94, 0x23, 0x39, 0x57, 0x9e, 0x68, 0x16,
|
||||||
0x2c, 0x10, 0x67, 0x15, 0x39, 0x43, 0x42, 0x88, 0x65, 0xa5, 0xa8, 0x69, 0xec, 0x4f, 0x45, 0xb3,
|
0x08, 0xb3, 0x8a, 0x98, 0x21, 0x51, 0x08, 0x65, 0xb9, 0x28, 0x69, 0xec, 0x4f, 0x45, 0xb3, 0x98,
|
||||||
0x98, 0x6a, 0xba, 0xe7, 0x4f, 0x0d, 0x87, 0x24, 0xec, 0x3a, 0x94, 0x4e, 0xe6, 0xfe, 0x74, 0xd4,
|
0x4a, 0xba, 0xeb, 0x4f, 0x0d, 0x86, 0x38, 0xec, 0x33, 0x28, 0x9d, 0xcc, 0xfd, 0xe9, 0xa8, 0x59,
|
||||||
0x2c, 0x11, 0xa5, 0x8e, 0x94, 0x36, 0x02, 0xc4, 0x31, 0x32, 0xb6, 0x03, 0xd5, 0x70, 0xea, 0xea,
|
0x22, 0x48, 0x1d, 0x21, 0x6d, 0x24, 0x10, 0xc6, 0xf0, 0xd8, 0x0e, 0x54, 0xc3, 0xa9, 0xab, 0xc7,
|
||||||
0xb1, 0x54, 0xb3, 0x26, 0xa4, 0x1b, 0x1e, 0x5b, 0x8c, 0x27, 0x52, 0x76, 0x17, 0xea, 0x9e, 0x0c,
|
0x52, 0xcd, 0x9a, 0x90, 0x6e, 0x78, 0x6c, 0x69, 0x3c, 0xe1, 0xb2, 0x3b, 0x50, 0xf7, 0x64, 0x10,
|
||||||
0x22, 0xad, 0x5c, 0x3f, 0xd0, 0x51, 0xb3, 0x4e, 0xe4, 0x8f, 0x91, 0xfc, 0x54, 0xaa, 0x33, 0xa1,
|
0x69, 0xe5, 0xfa, 0x81, 0x8e, 0x9a, 0x75, 0x02, 0x7f, 0x88, 0xe0, 0x27, 0x52, 0x9d, 0x09, 0xd5,
|
||||||
0x3a, 0xa9, 0x90, 0x67, 0x99, 0xed, 0x22, 0xe4, 0x65, 0xe8, 0xfc, 0x36, 0x07, 0xd5, 0x58, 0x2b,
|
0x49, 0x99, 0x3c, 0x8b, 0x6c, 0x17, 0x21, 0x2f, 0x43, 0xe7, 0x77, 0x39, 0xa8, 0xc6, 0x52, 0x99,
|
||||||
0x73, 0x60, 0xf5, 0x40, 0x79, 0xa7, 0xbe, 0x16, 0x9e, 0x9e, 0x2b, 0xd1, 0xcc, 0x6d, 0xe7, 0x76,
|
0x03, 0xab, 0x07, 0xca, 0x3b, 0xf5, 0xb5, 0xf0, 0xf4, 0x5c, 0x89, 0x66, 0x6e, 0x3b, 0xb7, 0x53,
|
||||||
0x6a, 0x7c, 0x09, 0x63, 0xeb, 0x90, 0x1f, 0x0c, 0xc9, 0x51, 0x35, 0x9e, 0x1f, 0x0c, 0x59, 0x13,
|
0xe3, 0x4b, 0x34, 0xb6, 0x0e, 0xf9, 0xc1, 0x90, 0x0c, 0x55, 0xe3, 0xf9, 0xc1, 0x90, 0x35, 0xa1,
|
||||||
0x2a, 0x4f, 0x5c, 0xe5, 0xbb, 0x81, 0x26, 0xcf, 0xd4, 0x78, 0x3c, 0x65, 0xd7, 0xa0, 0x36, 0x18,
|
0xf2, 0xd8, 0x55, 0xbe, 0x1b, 0x68, 0xb2, 0x4c, 0x8d, 0xc7, 0x53, 0x76, 0x0d, 0x6a, 0x83, 0xe1,
|
||||||
0x3e, 0x11, 0x2a, 0xf2, 0x65, 0x40, 0xfe, 0xa8, 0xf1, 0x14, 0x60, 0x9b, 0x00, 0x83, 0xe1, 0x3d,
|
0x63, 0xa1, 0x22, 0x5f, 0x06, 0x64, 0x8f, 0x1a, 0x4f, 0x09, 0x6c, 0x13, 0x60, 0x30, 0xbc, 0x2b,
|
||||||
0xe1, 0xa2, 0xd2, 0xa8, 0x59, 0xda, 0x2e, 0xec, 0xd4, 0x78, 0x06, 0x71, 0x7e, 0x0d, 0x25, 0x8a,
|
0x5c, 0x14, 0x1a, 0x35, 0x4b, 0xdb, 0x85, 0x9d, 0x1a, 0xcf, 0x50, 0x9c, 0xdf, 0x40, 0x89, 0x7c,
|
||||||
0x11, 0xfb, 0x06, 0xca, 0x23, 0x7f, 0x22, 0x22, 0x6d, 0xcc, 0x69, 0xef, 0x7f, 0xf7, 0xe3, 0xd6,
|
0xc4, 0xbe, 0x81, 0xf2, 0xc8, 0x9f, 0x88, 0x48, 0x1b, 0x75, 0xda, 0xfb, 0xdf, 0xfd, 0xb8, 0xb5,
|
||||||
0xca, 0xdf, 0x7f, 0xdc, 0xda, 0xcd, 0x24, 0x83, 0x0c, 0x45, 0xe0, 0xc9, 0x40, 0xbb, 0x7e, 0x20,
|
0xf2, 0xf7, 0x1f, 0xb7, 0x76, 0x33, 0xc1, 0x20, 0x43, 0x11, 0x78, 0x32, 0xd0, 0xae, 0x1f, 0x08,
|
||||||
0x54, 0xb4, 0x37, 0x91, 0x77, 0xcc, 0x92, 0x56, 0x97, 0x7e, 0xb8, 0xd5, 0xc0, 0x6e, 0x42, 0xc9,
|
0x15, 0xed, 0x4d, 0xe4, 0x6d, 0xb3, 0xa4, 0xd5, 0xa5, 0x0f, 0xb7, 0x12, 0xd8, 0x0d, 0x28, 0xf9,
|
||||||
0x0f, 0x46, 0xe2, 0x9c, 0xec, 0x2f, 0xb4, 0x3f, 0xb2, 0xaa, 0xea, 0x83, 0xb9, 0x0e, 0xe7, 0xba,
|
0xc1, 0x48, 0x9c, 0x93, 0xfe, 0x85, 0xf6, 0x07, 0x56, 0x54, 0x7d, 0x30, 0xd7, 0xe1, 0x5c, 0xf7,
|
||||||
0x8f, 0x22, 0x6e, 0x18, 0xce, 0xef, 0x73, 0x50, 0x36, 0x39, 0xc0, 0xae, 0x41, 0x71, 0x26, 0xb4,
|
0x91, 0xc5, 0x0d, 0xc2, 0xf9, 0x43, 0x0e, 0xca, 0x26, 0x06, 0xd8, 0x35, 0x28, 0xce, 0x84, 0x76,
|
||||||
0x4b, 0xfb, 0xd7, 0xf7, 0xab, 0xe8, 0xdb, 0x87, 0x42, 0xbb, 0x9c, 0x50, 0x4c, 0xaf, 0x99, 0x9c,
|
0x69, 0xff, 0xfa, 0x7e, 0x15, 0x6d, 0xfb, 0x40, 0x68, 0x97, 0x13, 0x15, 0xc3, 0x6b, 0x26, 0xe7,
|
||||||
0xa3, 0xef, 0xf3, 0x69, 0x7a, 0x3d, 0x44, 0x84, 0x5b, 0x01, 0xfb, 0x19, 0x54, 0x02, 0xa1, 0x5f,
|
0x68, 0xfb, 0x7c, 0x1a, 0x5e, 0x0f, 0x90, 0xc2, 0x2d, 0x83, 0xfd, 0x1c, 0x2a, 0x81, 0xd0, 0xcf,
|
||||||
0x48, 0x75, 0x46, 0x3e, 0x5a, 0x37, 0x41, 0x3f, 0x12, 0xfa, 0xa1, 0x1c, 0x09, 0x1e, 0xcb, 0xd8,
|
0xa5, 0x3a, 0x23, 0x1b, 0xad, 0x1b, 0xa7, 0x1f, 0x09, 0xfd, 0x40, 0x8e, 0x04, 0x8f, 0x79, 0xec,
|
||||||
0x6d, 0xa8, 0x46, 0xc2, 0x9b, 0x2b, 0x5f, 0x2f, 0xc8, 0x5f, 0xeb, 0xfb, 0x0d, 0xca, 0x32, 0x8b,
|
0x16, 0x54, 0x23, 0xe1, 0xcd, 0x95, 0xaf, 0x17, 0x64, 0xaf, 0xf5, 0xfd, 0x06, 0x45, 0x99, 0xa5,
|
||||||
0x11, 0x39, 0x61, 0x38, 0x7f, 0xca, 0x41, 0x11, 0xcd, 0x60, 0x0c, 0x8a, 0xae, 0x9a, 0x98, 0xec,
|
0x11, 0x38, 0x41, 0x38, 0x7f, 0xce, 0x41, 0x11, 0xd5, 0x60, 0x0c, 0x8a, 0xae, 0x9a, 0x98, 0xe8,
|
||||||
0xae, 0x71, 0x1a, 0xb3, 0x06, 0x14, 0x44, 0xf0, 0x9c, 0x2c, 0xaa, 0x71, 0x1c, 0x22, 0xe2, 0xbd,
|
0xae, 0x71, 0x1a, 0xb3, 0x06, 0x14, 0x44, 0xf0, 0x8c, 0x34, 0xaa, 0x71, 0x1c, 0x22, 0xc5, 0x7b,
|
||||||
0x18, 0xd9, 0x18, 0xe1, 0x10, 0xd7, 0xcd, 0x23, 0xa1, 0x6c, 0x68, 0x68, 0xcc, 0x6e, 0x42, 0x2d,
|
0x3e, 0xb2, 0x3e, 0xc2, 0x21, 0xae, 0x9b, 0x47, 0x42, 0x59, 0xd7, 0xd0, 0x98, 0xdd, 0x80, 0x5a,
|
||||||
0x54, 0xf2, 0x7c, 0xf1, 0x0c, 0x57, 0x97, 0x32, 0x89, 0x87, 0x60, 0x2f, 0x78, 0xce, 0xab, 0xa1,
|
0xa8, 0xe4, 0xf9, 0xe2, 0x29, 0xae, 0x2e, 0x65, 0x02, 0x0f, 0x89, 0xbd, 0xe0, 0x19, 0xaf, 0x86,
|
||||||
0x1d, 0xb1, 0x5d, 0x00, 0x71, 0xae, 0x95, 0x7b, 0x28, 0x23, 0x1d, 0x35, 0xcb, 0x74, 0x76, 0xca,
|
0x76, 0xc4, 0x76, 0x01, 0xc4, 0xb9, 0x56, 0xee, 0xa1, 0x8c, 0x74, 0xd4, 0x2c, 0xd3, 0xd9, 0x29,
|
||||||
0x77, 0x04, 0xfa, 0xc7, 0x3c, 0x23, 0x75, 0xfe, 0x9a, 0x87, 0x12, 0xb9, 0x84, 0xed, 0x60, 0x04,
|
0xde, 0x91, 0xd0, 0x3f, 0xe6, 0x19, 0xae, 0xf3, 0xaf, 0x3c, 0x94, 0xc8, 0x24, 0x6c, 0x07, 0x3d,
|
||||||
0xc2, 0xb9, 0x09, 0x66, 0xa1, 0xcd, 0x6c, 0x04, 0x80, 0x62, 0x9d, 0x04, 0x00, 0xe3, 0xbe, 0x81,
|
0x10, 0xce, 0x8d, 0x33, 0x0b, 0x6d, 0x66, 0x3d, 0x00, 0xe4, 0xeb, 0xc4, 0x01, 0xe8, 0xf7, 0x0d,
|
||||||
0xde, 0x98, 0x0a, 0x4f, 0x4b, 0x65, 0xd3, 0x2d, 0x99, 0xa3, 0xe9, 0x23, 0xcc, 0x08, 0x73, 0x1a,
|
0xb4, 0xc6, 0x54, 0x78, 0x5a, 0x2a, 0x1b, 0x6e, 0xc9, 0x1c, 0x55, 0x1f, 0x61, 0x44, 0x98, 0xd3,
|
||||||
0x1a, 0xb3, 0x5b, 0x50, 0x96, 0x14, 0x46, 0x3a, 0xd0, 0x5b, 0x82, 0x6b, 0x29, 0xa8, 0x5c, 0x09,
|
0xd0, 0x98, 0xdd, 0x84, 0xb2, 0x24, 0x37, 0xd2, 0x81, 0xde, 0xe0, 0x5c, 0x0b, 0x41, 0xe1, 0x4a,
|
||||||
0x77, 0x24, 0x83, 0xe9, 0x82, 0x8e, 0x59, 0xe5, 0xc9, 0x9c, 0xdd, 0x82, 0x1a, 0xc5, 0xed, 0xd1,
|
0xb8, 0x23, 0x19, 0x4c, 0x17, 0x74, 0xcc, 0x2a, 0x4f, 0xe6, 0xec, 0x26, 0xd4, 0xc8, 0x6f, 0x0f,
|
||||||
0x22, 0x14, 0xcd, 0x32, 0xc5, 0x61, 0x2d, 0x89, 0x29, 0x82, 0x3c, 0x95, 0xe3, 0x45, 0xf5, 0x5c,
|
0x17, 0xa1, 0x68, 0x96, 0xc9, 0x0f, 0x6b, 0x89, 0x4f, 0x91, 0xc8, 0x53, 0x3e, 0x26, 0xaa, 0xe7,
|
||||||
0xef, 0x54, 0x0c, 0x42, 0xdd, 0xbc, 0x9a, 0xfa, 0xab, 0x63, 0x31, 0x9e, 0x48, 0x51, 0x6d, 0x24,
|
0x7a, 0xa7, 0x62, 0x10, 0xea, 0xe6, 0xd5, 0xd4, 0x5e, 0x1d, 0x4b, 0xe3, 0x09, 0x17, 0xc5, 0x46,
|
||||||
0x3c, 0x25, 0x34, 0x52, 0x3f, 0x26, 0xea, 0x9a, 0x0d, 0xaf, 0x01, 0x79, 0x2a, 0x67, 0x0e, 0x94,
|
0xc2, 0x53, 0x42, 0x23, 0xf4, 0x43, 0x82, 0xae, 0x59, 0xf7, 0x1a, 0x22, 0x4f, 0xf9, 0xcc, 0x81,
|
||||||
0x87, 0xc3, 0x43, 0x64, 0x7e, 0x92, 0x16, 0x12, 0x83, 0x70, 0x2b, 0x71, 0xfa, 0x50, 0x8d, 0xb7,
|
0xf2, 0x70, 0x78, 0x88, 0xc8, 0x8f, 0xd2, 0x42, 0x62, 0x28, 0xdc, 0x72, 0xcc, 0x19, 0xa2, 0xf9,
|
||||||
0xc1, 0x5b, 0xd9, 0xef, 0xda, 0xfb, 0x9a, 0xef, 0x77, 0xd9, 0x1d, 0xa8, 0x44, 0xa7, 0xae, 0xf2,
|
0x54, 0xf7, 0xbb, 0xcd, 0x8f, 0x8d, 0x81, 0xe2, 0xb9, 0xd3, 0x87, 0x6a, 0xac, 0x02, 0x66, 0x6c,
|
||||||
0x83, 0x09, 0xf9, 0x6e, 0x7d, 0xff, 0xa3, 0xc4, 0xaa, 0xa1, 0xc1, 0x51, 0x53, 0xcc, 0x71, 0x24,
|
0xbf, 0x6b, 0x73, 0x39, 0xdf, 0xef, 0xb2, 0xdb, 0x50, 0x89, 0x4e, 0x5d, 0xe5, 0x07, 0x13, 0xb2,
|
||||||
0xd4, 0x12, 0x33, 0xde, 0xd0, 0xd5, 0x80, 0xc2, 0xdc, 0x1f, 0x91, 0x9e, 0x35, 0x8e, 0x43, 0x44,
|
0xeb, 0xfa, 0xfe, 0x07, 0x89, 0xc6, 0x43, 0x43, 0xc7, 0x5d, 0x62, 0x8c, 0x23, 0xa1, 0x96, 0xa8,
|
||||||
0x26, 0xbe, 0xc9, 0xa5, 0x35, 0x8e, 0x43, 0x0c, 0xc8, 0x4c, 0x8e, 0x4c, 0xd9, 0x5b, 0xe3, 0x34,
|
0xf8, 0x9a, 0xac, 0x06, 0x14, 0xe6, 0xfe, 0x88, 0xe4, 0xac, 0x71, 0x1c, 0x22, 0x65, 0xe2, 0x9b,
|
||||||
0x46, 0x1f, 0xcb, 0x50, 0xfb, 0x32, 0x70, 0xa7, 0xb1, 0x8f, 0xe3, 0xb9, 0x33, 0x8d, 0xcf, 0xf7,
|
0x38, 0x5b, 0xe3, 0x38, 0x44, 0x67, 0xcd, 0xe4, 0xc8, 0x94, 0xc4, 0x35, 0x4e, 0x63, 0xd4, 0x5d,
|
||||||
0x3f, 0xd9, 0xed, 0x37, 0x39, 0xa8, 0xc6, 0xb5, 0x1a, 0x0b, 0x8f, 0x3f, 0x12, 0x81, 0xf6, 0xc7,
|
0x86, 0xda, 0x97, 0x81, 0x3b, 0x8d, 0xed, 0x1f, 0xcf, 0x9d, 0x69, 0x7c, 0xf6, 0xff, 0xcb, 0x6e,
|
||||||
0xbe, 0x50, 0x76, 0xe3, 0x0c, 0xc2, 0xee, 0x40, 0xc9, 0xd5, 0x5a, 0xc5, 0xd7, 0xf9, 0xff, 0xb3,
|
0xbf, 0xcd, 0x41, 0x35, 0xae, 0xe3, 0x58, 0x94, 0xfc, 0x91, 0x08, 0xb4, 0x3f, 0xf6, 0x85, 0xb2,
|
||||||
0x85, 0xbe, 0x75, 0x80, 0x92, 0x5e, 0xa0, 0xd5, 0x82, 0x1b, 0xd6, 0xc6, 0x57, 0x00, 0x29, 0x88,
|
0x1b, 0x67, 0x28, 0xec, 0x36, 0x94, 0x5c, 0xad, 0x55, 0x9c, 0xea, 0x1f, 0x67, 0x2f, 0x81, 0xd6,
|
||||||
0xb6, 0x9e, 0x89, 0x85, 0xd5, 0x8a, 0x43, 0x76, 0x15, 0x4a, 0xcf, 0xdd, 0xe9, 0x5c, 0xd8, 0x1c,
|
0x01, 0x72, 0x7a, 0x81, 0x56, 0x0b, 0x6e, 0x50, 0x1b, 0x5f, 0x01, 0xa4, 0x44, 0xd4, 0xf5, 0x4c,
|
||||||
0x36, 0x93, 0xaf, 0xf3, 0x5f, 0xe5, 0x9c, 0xbf, 0xe4, 0xa1, 0x62, 0x0b, 0x3f, 0xbb, 0x0d, 0x15,
|
0x2c, 0xac, 0x54, 0x1c, 0xb2, 0xab, 0x50, 0x7a, 0xe6, 0x4e, 0xe7, 0xc2, 0xc6, 0xb7, 0x99, 0x7c,
|
||||||
0x2a, 0xfc, 0xd6, 0xa2, 0xcb, 0x2f, 0x46, 0x4c, 0x61, 0x7b, 0xc9, 0x17, 0x2d, 0x63, 0xa3, 0x55,
|
0x9d, 0xff, 0x2a, 0xe7, 0xfc, 0x35, 0x0f, 0x15, 0x7b, 0x29, 0xb0, 0x5b, 0x50, 0xa1, 0x4b, 0xc1,
|
||||||
0x65, 0xbe, 0x6c, 0xd6, 0xc6, 0xf4, 0xfb, 0x56, 0x18, 0x89, 0xb1, 0xfd, 0x74, 0xad, 0x23, 0xbb,
|
0x6a, 0x74, 0x79, 0xd2, 0xc4, 0x10, 0xb6, 0x97, 0xdc, 0x76, 0x19, 0x1d, 0xad, 0x28, 0x73, 0xeb,
|
||||||
0x2b, 0xc6, 0x7e, 0xe0, 0xa3, 0x7f, 0x38, 0x8a, 0xd8, 0xed, 0xf8, 0xd4, 0x45, 0xd2, 0xf8, 0x49,
|
0x59, 0x1d, 0xd3, 0xbb, 0xaf, 0x30, 0x12, 0x63, 0x7b, 0xad, 0xad, 0x23, 0xba, 0x2b, 0xc6, 0x7e,
|
||||||
0x56, 0xe3, 0x9b, 0x87, 0xee, 0x43, 0x3d, 0xb3, 0xcd, 0x25, 0xa7, 0xbe, 0x91, 0x3d, 0xb5, 0xdd,
|
0xe0, 0xa3, 0x7d, 0x38, 0xb2, 0xd8, 0xad, 0xf8, 0xd4, 0x45, 0x92, 0xf8, 0x51, 0x56, 0xe2, 0xeb,
|
||||||
0x92, 0xd4, 0x99, 0xef, 0x6e, 0xea, 0x85, 0xff, 0xc0, 0x7f, 0x5f, 0x02, 0xa4, 0x2a, 0xdf, 0xbf,
|
0x87, 0xee, 0x43, 0x3d, 0xb3, 0xcd, 0x25, 0xa7, 0xbe, 0x9e, 0x3d, 0xb5, 0xdd, 0x92, 0xc4, 0x99,
|
||||||
0xb0, 0x38, 0x2f, 0x0b, 0x00, 0x83, 0x10, 0x4b, 0xe7, 0xc8, 0xa5, 0xfa, 0xbd, 0xea, 0x4f, 0x02,
|
0x3b, 0x39, 0xb5, 0xc2, 0x7f, 0x61, 0xbf, 0x2f, 0x01, 0x52, 0x91, 0xef, 0x5e, 0x74, 0x9c, 0x17,
|
||||||
0xa9, 0xc4, 0x33, 0xba, 0xaa, 0xb4, 0xbe, 0xca, 0xeb, 0x06, 0xa3, 0x1b, 0xc3, 0x0e, 0xa0, 0x3e,
|
0x05, 0x80, 0x41, 0x88, 0x65, 0x75, 0xe4, 0x52, 0x6d, 0x5f, 0xf5, 0x27, 0x81, 0x54, 0xe2, 0x29,
|
||||||
0x12, 0x91, 0xa7, 0x7c, 0x4a, 0x28, 0xeb, 0xf4, 0x2d, 0x3c, 0x53, 0xaa, 0xa7, 0xd5, 0x4d, 0x19,
|
0xa5, 0x31, 0xad, 0xaf, 0xf2, 0xba, 0xa1, 0x51, 0xc6, 0xb0, 0x03, 0xa8, 0x8f, 0x44, 0xe4, 0x29,
|
||||||
0xc6, 0x57, 0xd9, 0x35, 0x6c, 0x1f, 0x56, 0xc5, 0x79, 0x28, 0x95, 0xb6, 0xbb, 0x98, 0xfe, 0xe0,
|
0x9f, 0x02, 0xca, 0x1a, 0x7d, 0x0b, 0xcf, 0x94, 0xca, 0x69, 0x75, 0x53, 0x84, 0xb1, 0x55, 0x76,
|
||||||
0x8a, 0xe9, 0x34, 0x10, 0xa7, 0x9d, 0x78, 0x5d, 0xa4, 0x13, 0xe6, 0x42, 0xd1, 0x73, 0x43, 0xf3,
|
0x0d, 0xdb, 0x87, 0x55, 0x71, 0x1e, 0x4a, 0xa5, 0xed, 0x2e, 0xa6, 0x77, 0xb8, 0x62, 0xba, 0x10,
|
||||||
0x71, 0xac, 0xef, 0x37, 0x2f, 0xec, 0xd7, 0x71, 0x43, 0xe3, 0xb4, 0xf6, 0x17, 0x78, 0xd6, 0x97,
|
0xa4, 0xd3, 0x4e, 0xbc, 0x2e, 0xd2, 0x09, 0x73, 0xa1, 0xe8, 0xb9, 0xa1, 0xb9, 0x38, 0xeb, 0xfb,
|
||||||
0xff, 0xd8, 0xba, 0x95, 0xf9, 0x22, 0xce, 0xe4, 0xc9, 0x62, 0x8f, 0xf2, 0xe5, 0xcc, 0xd7, 0x7b,
|
0xcd, 0x0b, 0xfb, 0x75, 0xdc, 0xd0, 0x18, 0xad, 0xfd, 0x05, 0x9e, 0xf5, 0xc5, 0x3f, 0xb6, 0x6e,
|
||||||
0x73, 0xed, 0x4f, 0xf7, 0xdc, 0xd0, 0x47, 0x75, 0xb8, 0xb0, 0xdf, 0xe5, 0xa4, 0x7a, 0xe3, 0x17,
|
0x66, 0x6e, 0xcb, 0x99, 0x3c, 0x59, 0xec, 0x51, 0xbc, 0x9c, 0xf9, 0x7a, 0x6f, 0xae, 0xfd, 0xe9,
|
||||||
0xd0, 0xb8, 0x68, 0xf7, 0x87, 0xc4, 0x60, 0xe3, 0x2e, 0xd4, 0x12, 0x3b, 0xde, 0xb5, 0xb0, 0x9a,
|
0x9e, 0x1b, 0xfa, 0x28, 0x0e, 0x17, 0xf6, 0xbb, 0x9c, 0x44, 0x6f, 0xfc, 0x12, 0x1a, 0x17, 0xf5,
|
||||||
0x0d, 0xde, 0x9f, 0x73, 0x50, 0x36, 0xb7, 0x8a, 0xdd, 0x85, 0xda, 0x54, 0x7a, 0x2e, 0x1a, 0x10,
|
0x7e, 0x1f, 0x1f, 0x6c, 0xdc, 0x81, 0x5a, 0xa2, 0xc7, 0xdb, 0x16, 0x56, 0xb3, 0xce, 0xfb, 0x4b,
|
||||||
0xb7, 0x68, 0x9f, 0xa6, 0x97, 0xae, 0xf5, 0x20, 0x96, 0x19, 0xaf, 0xa6, 0x5c, 0x4c, 0x32, 0x3f,
|
0x0e, 0xca, 0x26, 0xab, 0xd8, 0x1d, 0xa8, 0x4d, 0xa5, 0xe7, 0xa2, 0x02, 0x71, 0xfb, 0xf6, 0x49,
|
||||||
0x18, 0xcb, 0xf8, 0x16, 0xac, 0xa7, 0x8b, 0xfa, 0xc1, 0x58, 0x72, 0x23, 0xdc, 0xb8, 0x0f, 0xeb,
|
0x9a, 0x74, 0xad, 0xfb, 0x31, 0xcf, 0x58, 0x35, 0xc5, 0x62, 0x90, 0xf9, 0xc1, 0x58, 0xc6, 0x59,
|
||||||
0xcb, 0x2a, 0x2e, 0xb1, 0xf3, 0xfa, 0x72, 0xba, 0x52, 0x5d, 0x4e, 0x16, 0x65, 0xcd, 0xbe, 0x0b,
|
0xb0, 0x9e, 0x2e, 0xea, 0x07, 0x63, 0xc9, 0x0d, 0x73, 0xe3, 0x1e, 0xac, 0x2f, 0x8b, 0xb8, 0x44,
|
||||||
0xb5, 0x04, 0x67, 0xbb, 0x6f, 0x1a, 0xbe, 0x9a, 0x5d, 0x99, 0xb1, 0xd5, 0x99, 0x02, 0xa4, 0xa6,
|
0xcf, 0xcf, 0x96, 0xc3, 0x95, 0x6a, 0x76, 0xb2, 0x28, 0xab, 0xf6, 0x1d, 0xa8, 0x25, 0x74, 0xb6,
|
||||||
0x61, 0xb1, 0xc2, 0x5e, 0x30, 0x70, 0x67, 0x71, 0x93, 0x95, 0xcc, 0xe9, 0xdb, 0xe6, 0x6a, 0x97,
|
0xfb, 0xba, 0xe2, 0xab, 0xd9, 0x95, 0x19, 0x5d, 0x9d, 0x29, 0x40, 0xaa, 0x1a, 0x16, 0x2b, 0xec,
|
||||||
0x4c, 0x59, 0xe5, 0x34, 0x66, 0x2d, 0x80, 0x51, 0x72, 0x61, 0xdf, 0x72, 0x8d, 0x33, 0x0c, 0x67,
|
0x13, 0x03, 0x77, 0x16, 0x37, 0x60, 0xc9, 0x9c, 0xee, 0x3d, 0x57, 0xbb, 0xa4, 0xca, 0x2a, 0xa7,
|
||||||
0x00, 0xd5, 0xd8, 0x08, 0xb6, 0x0d, 0xf5, 0xc8, 0xee, 0x8c, 0x9d, 0x0f, 0x6e, 0x57, 0xe2, 0x59,
|
0x31, 0x6b, 0x01, 0x8c, 0x92, 0x84, 0x7d, 0x43, 0x1a, 0x67, 0x10, 0xce, 0x00, 0xaa, 0xb1, 0x12,
|
||||||
0x08, 0x3b, 0x18, 0xe5, 0x06, 0x13, 0xb1, 0xd4, 0xc1, 0x70, 0x44, 0xb8, 0x15, 0x38, 0x4f, 0xa1,
|
0x6c, 0x1b, 0xea, 0x91, 0xdd, 0x19, 0xbb, 0x22, 0xdc, 0xae, 0xc4, 0xb3, 0x24, 0xec, 0x6e, 0x94,
|
||||||
0x44, 0x00, 0x5e, 0xb3, 0x48, 0xbb, 0x4a, 0xdb, 0x66, 0xc8, 0x34, 0x07, 0x32, 0xa2, 0x6d, 0xdb,
|
0x1b, 0x4c, 0xc4, 0x52, 0x77, 0xc3, 0x91, 0xc2, 0x2d, 0xc3, 0x79, 0x02, 0x25, 0x22, 0x60, 0x9a,
|
||||||
0x45, 0x4c, 0x44, 0x6e, 0x08, 0xec, 0x06, 0xb6, 0x20, 0x23, 0xeb, 0xd1, 0xcb, 0x78, 0x28, 0x76,
|
0x45, 0xda, 0x55, 0xda, 0x36, 0x4a, 0xa6, 0x71, 0x90, 0x11, 0x6d, 0xdb, 0x2e, 0x62, 0x20, 0x72,
|
||||||
0x7e, 0x0e, 0xd5, 0x18, 0xc6, 0x93, 0x3f, 0xf0, 0x03, 0x61, 0x4d, 0xa4, 0x31, 0x36, 0x91, 0x9d,
|
0x03, 0x60, 0xd7, 0xb1, 0x3d, 0x19, 0x59, 0x8b, 0x5e, 0x86, 0x43, 0xb6, 0xf3, 0x0b, 0xa8, 0xc6,
|
||||||
0x53, 0x57, 0xb9, 0x9e, 0x16, 0xa6, 0x0d, 0x28, 0xf1, 0x14, 0x70, 0xae, 0x43, 0x3d, 0x73, 0x7b,
|
0x64, 0x3c, 0xf9, 0x7d, 0x3f, 0x10, 0x56, 0x45, 0x1a, 0x63, 0x83, 0xd9, 0x39, 0x75, 0x95, 0xeb,
|
||||||
0x30, 0xdd, 0x9e, 0x50, 0x18, 0xcd, 0x1d, 0x36, 0x13, 0xe7, 0x25, 0xb6, 0xb8, 0x71, 0xd7, 0xf2,
|
0x69, 0x61, 0x5a, 0x84, 0x12, 0x4f, 0x09, 0xce, 0x67, 0x50, 0xcf, 0x64, 0x0f, 0x86, 0xdb, 0x63,
|
||||||
0x53, 0x80, 0x53, 0xad, 0xc3, 0x67, 0xd4, 0xc6, 0x58, 0xdf, 0xd7, 0x10, 0x21, 0x06, 0xdb, 0x82,
|
0x72, 0xa3, 0xc9, 0x61, 0x33, 0x71, 0x5e, 0x60, 0xfb, 0x1b, 0x77, 0x34, 0x3f, 0x03, 0x38, 0xd5,
|
||||||
0x3a, 0x4e, 0x22, 0x2b, 0x37, 0xf9, 0x4e, 0x2b, 0x22, 0x43, 0xf8, 0x09, 0xd4, 0xc6, 0xc9, 0xf2,
|
0x3a, 0x7c, 0x4a, 0x2d, 0x8e, 0xb5, 0x7d, 0x0d, 0x29, 0x84, 0x60, 0x5b, 0x50, 0xc7, 0x49, 0x64,
|
||||||
0x82, 0x0d, 0x5d, 0xbc, 0xfa, 0x53, 0xa8, 0x06, 0xd2, 0xca, 0x4c, 0x57, 0x55, 0x09, 0x24, 0x89,
|
0xf9, 0x26, 0xde, 0x69, 0x45, 0x64, 0x00, 0x3f, 0x85, 0xda, 0x38, 0x59, 0x5e, 0xb0, 0xae, 0x8b,
|
||||||
0x9c, 0x5b, 0xf0, 0x7f, 0x6f, 0xf4, 0xe3, 0xec, 0x13, 0x28, 0x8f, 0xfd, 0xa9, 0xa6, 0xa2, 0x8f,
|
0x57, 0x7f, 0x02, 0xd5, 0x40, 0x5a, 0x9e, 0xe9, 0xb8, 0x2a, 0x81, 0x24, 0x96, 0x73, 0x13, 0x7e,
|
||||||
0x8d, 0x9a, 0x9d, 0x39, 0xff, 0xca, 0x01, 0xa4, 0x91, 0xc5, 0x7c, 0xc5, 0xea, 0x8d, 0x9c, 0x55,
|
0xf2, 0x5a, 0xaf, 0xce, 0x3e, 0x82, 0xf2, 0xd8, 0x9f, 0x6a, 0x2a, 0xfa, 0xd8, 0xc4, 0xd9, 0x99,
|
||||||
0x53, 0xad, 0xa7, 0x50, 0x9d, 0xd9, 0x3a, 0x60, 0x63, 0x76, 0x6d, 0x39, 0x1b, 0x5a, 0x71, 0x99,
|
0xf3, 0xef, 0x1c, 0x40, 0xea, 0x59, 0x8c, 0x57, 0xac, 0xde, 0x88, 0x59, 0x35, 0xd5, 0x7a, 0x0a,
|
||||||
0x30, 0x15, 0x62, 0xdf, 0x56, 0x88, 0x0f, 0xe9, 0x99, 0x93, 0x1d, 0xa8, 0x19, 0xc9, 0xbe, 0x7d,
|
0xd5, 0x99, 0xad, 0x03, 0xd6, 0x67, 0xd7, 0x96, 0xa3, 0xa1, 0x15, 0x97, 0x09, 0x53, 0x21, 0xf6,
|
||||||
0x20, 0xbd, 0x68, 0xdc, 0x4a, 0x36, 0xee, 0xc3, 0xda, 0xd2, 0x96, 0xef, 0xf9, 0x4d, 0x48, 0xeb,
|
0x6d, 0x85, 0x78, 0x9f, 0x7e, 0x3a, 0xd9, 0x81, 0x1a, 0x95, 0xec, 0xbb, 0x08, 0xd2, 0x44, 0xe3,
|
||||||
0x59, 0xf6, 0x96, 0xdd, 0x86, 0xb2, 0x69, 0x22, 0x31, 0x25, 0x70, 0x64, 0xd5, 0xd0, 0x98, 0x3a,
|
0x96, 0xb3, 0x71, 0x0f, 0xd6, 0x96, 0xb6, 0x7c, 0xc7, 0x3b, 0x21, 0xad, 0x67, 0xd9, 0x2c, 0xbb,
|
||||||
0x86, 0xe3, 0xf8, 0x05, 0xd2, 0x3f, 0x76, 0xf6, 0xa1, 0x6c, 0x9e, 0x58, 0x6c, 0x07, 0x2a, 0xae,
|
0x05, 0x65, 0xd3, 0x60, 0x62, 0x48, 0xe0, 0xc8, 0x8a, 0xa1, 0x31, 0x75, 0x0c, 0xc7, 0xf1, 0xeb,
|
||||||
0x67, 0xae, 0x63, 0xa6, 0x24, 0xa0, 0xf0, 0x80, 0x60, 0x1e, 0x8b, 0x9d, 0xbf, 0xe5, 0x01, 0x52,
|
0xa4, 0x7f, 0xec, 0xec, 0x43, 0xd9, 0x3c, 0xbf, 0xd8, 0x0e, 0x54, 0x5c, 0xcf, 0xa4, 0x63, 0xa6,
|
||||||
0xfc, 0x03, 0xba, 0xd2, 0xaf, 0x61, 0x3d, 0x12, 0x9e, 0x0c, 0x46, 0xae, 0x5a, 0x90, 0xd4, 0x3e,
|
0x24, 0x20, 0xf3, 0x80, 0xc8, 0x3c, 0x66, 0x3b, 0x7f, 0xcb, 0x03, 0xa4, 0xf4, 0xf7, 0xe8, 0x58,
|
||||||
0x25, 0x2e, 0x5b, 0x72, 0x81, 0x99, 0xe9, 0x50, 0x0b, 0xef, 0xee, 0x50, 0x77, 0xa0, 0xe8, 0xc9,
|
0xbf, 0x86, 0xf5, 0x48, 0x78, 0x32, 0x18, 0xb9, 0x6a, 0x41, 0x5c, 0xfb, 0xcc, 0xb8, 0x6c, 0xc9,
|
||||||
0x70, 0x61, 0x3f, 0x14, 0x6c, 0xf9, 0x20, 0x1d, 0x19, 0x2e, 0xf0, 0x41, 0x89, 0x0c, 0xd6, 0x82,
|
0x05, 0x64, 0xa6, 0x7b, 0x2d, 0xbc, 0xbd, 0x7b, 0xdd, 0x81, 0xa2, 0x27, 0xc3, 0x85, 0xbd, 0x28,
|
||||||
0xf2, 0xec, 0x8c, 0x1e, 0x9d, 0xa6, 0x61, 0xbf, 0xba, 0xcc, 0x7d, 0x78, 0x86, 0x63, 0x7c, 0xa2,
|
0xd8, 0xf2, 0x41, 0x3a, 0x32, 0x5c, 0xe0, 0x63, 0x13, 0x11, 0xac, 0x05, 0xe5, 0xd9, 0x19, 0x3d,
|
||||||
0x1a, 0x16, 0xbb, 0x05, 0xa5, 0xd9, 0xd9, 0xc8, 0x57, 0xd4, 0xdb, 0xd6, 0x4d, 0x67, 0x98, 0xa5,
|
0x48, 0x4d, 0x33, 0x7f, 0x75, 0x19, 0xfb, 0xe0, 0x0c, 0xc7, 0xf8, 0x7c, 0x35, 0x28, 0x76, 0x13,
|
||||||
0x77, 0x7d, 0x85, 0x0f, 0x51, 0xe2, 0x30, 0x07, 0xf2, 0x6a, 0xd6, 0xac, 0x10, 0xb3, 0x71, 0xc1,
|
0x4a, 0xb3, 0xb3, 0x91, 0xaf, 0xa8, 0xef, 0xad, 0x9b, 0xce, 0x30, 0x0b, 0xef, 0xfa, 0x0a, 0x1f,
|
||||||
0x9b, 0xb3, 0xc3, 0x15, 0x9e, 0x57, 0xb3, 0x76, 0x15, 0xca, 0xc6, 0xaf, 0xce, 0x1f, 0x0b, 0xb0,
|
0xa9, 0x84, 0x61, 0x0e, 0xe4, 0xd5, 0xac, 0x59, 0x21, 0x64, 0xe3, 0x82, 0x35, 0x67, 0x87, 0x2b,
|
||||||
0xbe, 0x6c, 0x25, 0xe6, 0x41, 0xa4, 0xbc, 0x38, 0x0f, 0x22, 0xe5, 0x25, 0xcd, 0x7b, 0x3e, 0xd3,
|
0x3c, 0xaf, 0x66, 0xed, 0x2a, 0x94, 0x8d, 0x5d, 0x9d, 0x3f, 0x15, 0x60, 0x7d, 0x59, 0x4b, 0x8c,
|
||||||
0xbc, 0x3b, 0x50, 0x92, 0x2f, 0x02, 0xa1, 0xb2, 0xaf, 0xeb, 0xce, 0xa9, 0x7c, 0x11, 0x60, 0x9b,
|
0x83, 0x48, 0x79, 0x71, 0x1c, 0x44, 0xca, 0x4b, 0x1a, 0xfb, 0x7c, 0xa6, 0xb1, 0x77, 0xa0, 0x24,
|
||||||
0x6a, 0x44, 0x4b, 0x5d, 0x5f, 0xc9, 0x76, 0x7d, 0x37, 0x60, 0x6d, 0x2c, 0xa7, 0x53, 0xf9, 0x62,
|
0x9f, 0x07, 0x42, 0x65, 0x5f, 0xde, 0x9d, 0x53, 0xf9, 0x3c, 0xc0, 0x36, 0xd5, 0xb0, 0x96, 0xba,
|
||||||
0xb8, 0x98, 0x4d, 0xfd, 0xe0, 0xcc, 0xb6, 0x7e, 0xcb, 0x20, 0xdb, 0x81, 0x2b, 0x23, 0x5f, 0xa1,
|
0xbe, 0x92, 0xed, 0xfa, 0xae, 0xc3, 0xda, 0x58, 0x4e, 0xa7, 0xf2, 0xf9, 0x70, 0x31, 0x9b, 0xfa,
|
||||||
0x39, 0x1d, 0x19, 0x68, 0x11, 0xd0, 0x7b, 0x05, 0x79, 0x17, 0x61, 0xf6, 0x0d, 0x6c, 0xbb, 0x5a,
|
0xc1, 0x99, 0x6d, 0xfd, 0x96, 0x89, 0x6c, 0x07, 0xae, 0x8c, 0x7c, 0x85, 0xea, 0x74, 0x64, 0xa0,
|
||||||
0x8b, 0x59, 0xa8, 0x1f, 0x07, 0xa1, 0xeb, 0x9d, 0x75, 0xa5, 0x47, 0x77, 0x76, 0x16, 0xba, 0xda,
|
0x45, 0x40, 0x6f, 0x19, 0xc4, 0x5d, 0x24, 0xb3, 0x6f, 0x60, 0xdb, 0xd5, 0x5a, 0xcc, 0x42, 0xfd,
|
||||||
0x3f, 0xf1, 0xa7, 0xf8, 0x34, 0xab, 0xd0, 0xd2, 0x77, 0xf2, 0xd8, 0xe7, 0xb0, 0xee, 0x29, 0xe1,
|
0x28, 0x08, 0x5d, 0xef, 0xac, 0x2b, 0x3d, 0xca, 0xd9, 0x59, 0xe8, 0x6a, 0xff, 0xc4, 0x9f, 0xe2,
|
||||||
0x6a, 0xd1, 0x15, 0x91, 0x3e, 0x76, 0xf5, 0x69, 0xb3, 0x4a, 0x2b, 0x2f, 0xa0, 0x78, 0x06, 0x17,
|
0xb3, 0xad, 0x42, 0x4b, 0xdf, 0x8a, 0x63, 0x9f, 0xc3, 0xba, 0xa7, 0x84, 0xab, 0x45, 0x57, 0x44,
|
||||||
0xad, 0x7d, 0xea, 0x4f, 0x47, 0x9e, 0xab, 0x46, 0xcd, 0x9a, 0x39, 0xc3, 0x12, 0xc8, 0x5a, 0xc0,
|
0xfa, 0xd8, 0xd5, 0xa7, 0xcd, 0x2a, 0xad, 0xbc, 0x40, 0xc5, 0x33, 0xb8, 0xa8, 0xed, 0x13, 0x7f,
|
||||||
0x08, 0xe8, 0xcd, 0x42, 0xbd, 0x48, 0xa8, 0x40, 0xd4, 0x4b, 0x24, 0x58, 0x38, 0xb5, 0x3f, 0x13,
|
0x3a, 0xf2, 0x5c, 0x35, 0x6a, 0xd6, 0xcc, 0x19, 0x96, 0x88, 0xac, 0x05, 0x8c, 0x08, 0xbd, 0x59,
|
||||||
0x91, 0x76, 0x67, 0x21, 0xfd, 0x2b, 0x50, 0xe0, 0x29, 0xe0, 0x7c, 0x9b, 0x83, 0xc6, 0xc5, 0x14,
|
0xa8, 0x17, 0x09, 0x14, 0x08, 0x7a, 0x09, 0x07, 0x0b, 0xa7, 0xf6, 0x67, 0x22, 0xd2, 0xee, 0x2c,
|
||||||
0x41, 0x07, 0x87, 0x68, 0xa6, 0xbd, 0x6c, 0x38, 0x4e, 0x9c, 0x9e, 0xcf, 0x38, 0x3d, 0xfe, 0x42,
|
0xa4, 0x3f, 0x06, 0x05, 0x9e, 0x12, 0x9c, 0x6f, 0x73, 0xd0, 0xb8, 0x18, 0x22, 0x68, 0xe0, 0x10,
|
||||||
0x15, 0x32, 0x5f, 0xa8, 0x24, 0x80, 0xc5, 0xb7, 0x07, 0x70, 0xc9, 0xa4, 0xd2, 0x45, 0x93, 0x7e,
|
0xd5, 0xb4, 0xc9, 0x86, 0xe3, 0xc4, 0xe8, 0xf9, 0x8c, 0xd1, 0xe3, 0x1b, 0xaa, 0x90, 0xb9, 0xa1,
|
||||||
0x97, 0x83, 0x2b, 0x17, 0xd2, 0xf0, 0xbd, 0x2d, 0xda, 0x86, 0xfa, 0xcc, 0x3d, 0x13, 0xc7, 0xae,
|
0x12, 0x07, 0x16, 0xdf, 0xec, 0xc0, 0x25, 0x95, 0x4a, 0x17, 0x55, 0xfa, 0x7d, 0x0e, 0xae, 0x5c,
|
||||||
0xa2, 0xe0, 0x16, 0x4c, 0x0b, 0x97, 0x81, 0xfe, 0x0b, 0xf6, 0x05, 0xb0, 0x9a, 0xcd, 0xfd, 0x4b,
|
0x08, 0xc3, 0x77, 0xd6, 0x68, 0x1b, 0xea, 0x33, 0xf7, 0x4c, 0x1c, 0xbb, 0x8a, 0x9c, 0x5b, 0x30,
|
||||||
0x6d, 0x8b, 0x43, 0x79, 0x24, 0xf5, 0x3d, 0x39, 0xb7, 0x5f, 0xbf, 0x38, 0x94, 0x31, 0xf8, 0x66,
|
0x2d, 0x5c, 0x86, 0xf4, 0x3f, 0xd0, 0x2f, 0x80, 0xd5, 0x6c, 0xec, 0x5f, 0xaa, 0x5b, 0xec, 0xca,
|
||||||
0xc0, 0x0b, 0x97, 0x04, 0xdc, 0x39, 0x82, 0x6a, 0x6c, 0x20, 0xdb, 0xb2, 0x4f, 0xf5, 0x5c, 0xfa,
|
0x23, 0xa9, 0xef, 0xca, 0xb9, 0xbd, 0xfd, 0x62, 0x57, 0xc6, 0xc4, 0xd7, 0x1d, 0x5e, 0xb8, 0xc4,
|
||||||
0x97, 0xd1, 0xe3, 0x48, 0x28, 0xb4, 0xdd, 0xbc, 0xdb, 0x3f, 0x83, 0xd2, 0x44, 0xc9, 0x79, 0x68,
|
0xe1, 0xce, 0x11, 0x54, 0x63, 0x05, 0xd9, 0x96, 0x7d, 0xc6, 0xe7, 0xd2, 0xdf, 0x49, 0x8f, 0x22,
|
||||||
0x6b, 0xeb, 0x12, 0xc3, 0x48, 0x9c, 0x21, 0x54, 0x2c, 0xc2, 0x76, 0xa1, 0x7c, 0xb2, 0x38, 0x8a,
|
0xa1, 0x50, 0x77, 0xf3, 0xa6, 0xff, 0x14, 0x4a, 0x13, 0x25, 0xe7, 0xa1, 0xad, 0xad, 0x4b, 0x08,
|
||||||
0x9b, 0x0f, 0x7b, 0xb1, 0x71, 0x3e, 0xb2, 0x0c, 0xac, 0x16, 0x86, 0xc1, 0xae, 0x42, 0xf1, 0x64,
|
0xc3, 0x71, 0x86, 0x50, 0xb1, 0x14, 0xb6, 0x0b, 0xe5, 0x93, 0xc5, 0x51, 0xdc, 0x7c, 0xd8, 0xc4,
|
||||||
0xd1, 0xef, 0x9a, 0x07, 0x19, 0xd6, 0x1c, 0x9c, 0xb5, 0xcb, 0xc6, 0x20, 0xe7, 0x01, 0xac, 0x66,
|
0xc6, 0xf9, 0xc8, 0x22, 0xb0, 0x5a, 0x18, 0x04, 0xbb, 0x0a, 0xc5, 0x93, 0x45, 0xbf, 0x6b, 0x1e,
|
||||||
0xd7, 0xa1, 0x53, 0x32, 0x4d, 0x0d, 0x8d, 0xd3, 0xe2, 0x9a, 0x7f, 0x47, 0x71, 0xdd, 0xdd, 0x81,
|
0x64, 0x58, 0x73, 0x70, 0xd6, 0x2e, 0x1b, 0x85, 0x9c, 0xfb, 0xb0, 0x9a, 0x5d, 0x87, 0x46, 0xc9,
|
||||||
0x8a, 0xfd, 0x53, 0x84, 0xd5, 0xa0, 0xf4, 0xf8, 0x68, 0xd8, 0x7b, 0xd4, 0x58, 0x61, 0x55, 0x28,
|
0x34, 0x35, 0x34, 0x4e, 0x8b, 0x6b, 0xfe, 0x2d, 0xc5, 0x75, 0x77, 0x07, 0x2a, 0xf6, 0x87, 0x09,
|
||||||
0x1e, 0x0e, 0x86, 0x8f, 0x1a, 0x39, 0x1c, 0x1d, 0x0d, 0x8e, 0x7a, 0x8d, 0xfc, 0xee, 0x4d, 0x58,
|
0xab, 0x41, 0xe9, 0xd1, 0xd1, 0xb0, 0xf7, 0xb0, 0xb1, 0xc2, 0xaa, 0x50, 0x3c, 0x1c, 0x0c, 0x1f,
|
||||||
0xcd, 0xfe, 0x2d, 0xc2, 0xea, 0x50, 0x19, 0x1e, 0x1c, 0x75, 0xdb, 0x83, 0x5f, 0x35, 0x56, 0xd8,
|
0x36, 0x72, 0x38, 0x3a, 0x1a, 0x1c, 0xf5, 0x1a, 0xf9, 0xdd, 0x1b, 0xb0, 0x9a, 0xfd, 0x65, 0xc2,
|
||||||
0x2a, 0x54, 0xfb, 0x47, 0xc3, 0x5e, 0xe7, 0x31, 0xef, 0x35, 0x72, 0xbb, 0xbf, 0x84, 0x5a, 0xf2,
|
0xea, 0x50, 0x19, 0x1e, 0x1c, 0x75, 0xdb, 0x83, 0x5f, 0x37, 0x56, 0xd8, 0x2a, 0x54, 0xfb, 0x47,
|
||||||
0x72, 0x47, 0x0d, 0xed, 0xfe, 0x51, 0xb7, 0xb1, 0xc2, 0x00, 0xca, 0xc3, 0x5e, 0x87, 0xf7, 0x50,
|
0xc3, 0x5e, 0xe7, 0x11, 0xef, 0x35, 0x72, 0xbb, 0xbf, 0x82, 0x5a, 0xf2, 0xaa, 0x47, 0x09, 0xed,
|
||||||
0x6f, 0x05, 0x0a, 0xc3, 0xe1, 0x61, 0x23, 0x8f, 0xbb, 0x76, 0x0e, 0x3a, 0x87, 0xbd, 0x46, 0x01,
|
0xfe, 0x51, 0xb7, 0xb1, 0xc2, 0x00, 0xca, 0xc3, 0x5e, 0x87, 0xf7, 0x50, 0x6e, 0x05, 0x0a, 0xc3,
|
||||||
0x87, 0x8f, 0x1e, 0x1e, 0xdf, 0x1b, 0x36, 0x8a, 0xbb, 0x5f, 0xc2, 0x95, 0x0b, 0x2f, 0x67, 0x5a,
|
0xe1, 0x61, 0x23, 0x8f, 0xbb, 0x76, 0x0e, 0x3a, 0x87, 0xbd, 0x46, 0x01, 0x87, 0x0f, 0x1f, 0x1c,
|
||||||
0x7d, 0x78, 0xc0, 0x7b, 0xa8, 0xa9, 0x0e, 0x95, 0x63, 0xde, 0x7f, 0x72, 0xf0, 0xa8, 0xd7, 0xc8,
|
0xdf, 0x1d, 0x36, 0x8a, 0xbb, 0x5f, 0xc2, 0x95, 0x0b, 0x2f, 0x67, 0x5a, 0x7d, 0x78, 0xc0, 0x7b,
|
||||||
0xa1, 0xe0, 0xc1, 0xa0, 0x73, 0xbf, 0xd7, 0x6d, 0xe4, 0xdb, 0xd7, 0xbe, 0x7b, 0xb5, 0x99, 0xfb,
|
0x28, 0xa9, 0x0e, 0x95, 0x63, 0xde, 0x7f, 0x7c, 0xf0, 0xb0, 0xd7, 0xc8, 0x21, 0xe3, 0xfe, 0xa0,
|
||||||
0xfe, 0xd5, 0x66, 0xee, 0x87, 0x57, 0x9b, 0xb9, 0x7f, 0xbe, 0xda, 0xcc, 0x7d, 0xfb, 0x7a, 0x73,
|
0x73, 0xaf, 0xd7, 0x6d, 0xe4, 0xdb, 0xd7, 0xbe, 0x7b, 0xb9, 0x99, 0xfb, 0xfe, 0xe5, 0x66, 0xee,
|
||||||
0xe5, 0xfb, 0xd7, 0x9b, 0x2b, 0x3f, 0xbc, 0xde, 0x5c, 0x39, 0x29, 0xd3, 0x9f, 0x94, 0x5f, 0xfc,
|
0x87, 0x97, 0x9b, 0xb9, 0x7f, 0xbe, 0xdc, 0xcc, 0x7d, 0xfb, 0x6a, 0x73, 0xe5, 0xfb, 0x57, 0x9b,
|
||||||
0x3b, 0x00, 0x00, 0xff, 0xff, 0x7e, 0x60, 0x46, 0x7d, 0xe4, 0x14, 0x00, 0x00,
|
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) {
|
func (m *Op) Marshal() (dAtA []byte, err error) {
|
||||||
|
@ -2865,6 +2874,15 @@ func (m *Mount) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
||||||
_ = i
|
_ = i
|
||||||
var l int
|
var l int
|
||||||
_ = l
|
_ = 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 {
|
if m.SSHOpt != nil {
|
||||||
{
|
{
|
||||||
size, err := m.SSHOpt.MarshalToSizedBuffer(dAtA[:i])
|
size, err := m.SSHOpt.MarshalToSizedBuffer(dAtA[:i])
|
||||||
|
@ -4704,6 +4722,10 @@ func (m *Mount) Size() (n int) {
|
||||||
l = m.SSHOpt.Size()
|
l = m.SSHOpt.Size()
|
||||||
n += 2 + l + sovOps(uint64(l))
|
n += 2 + l + sovOps(uint64(l))
|
||||||
}
|
}
|
||||||
|
l = len(m.ResultID)
|
||||||
|
if l > 0 {
|
||||||
|
n += 2 + l + sovOps(uint64(l))
|
||||||
|
}
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6661,6 +6683,38 @@ func (m *Mount) Unmarshal(dAtA []byte) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
iNdEx = postIndex
|
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:
|
default:
|
||||||
iNdEx = preIndex
|
iNdEx = preIndex
|
||||||
skippy, err := skipOps(dAtA[iNdEx:])
|
skippy, err := skipOps(dAtA[iNdEx:])
|
||||||
|
|
|
@ -81,6 +81,7 @@ message Mount {
|
||||||
CacheOpt cacheOpt = 20;
|
CacheOpt cacheOpt = 20;
|
||||||
SecretOpt secretOpt = 21;
|
SecretOpt secretOpt = 21;
|
||||||
SSHOpt SSHOpt = 22;
|
SSHOpt SSHOpt = 22;
|
||||||
|
string resultID = 23;
|
||||||
}
|
}
|
||||||
|
|
||||||
// MountType defines a type of a mount from a supported set
|
// MountType defines a type of a mount from a supported set
|
||||||
|
|
Loading…
Reference in New Issue