Skip to content

Commit

Permalink
Merge pull request #2294 from coryb/gateway-exec-extra-hosts
Browse files Browse the repository at this point in the history
add missing ExtraHosts to gateway exec
  • Loading branch information
tonistiigi authored Aug 16, 2021
2 parents f6ac37d + 2893203 commit 7658c78
Show file tree
Hide file tree
Showing 12 changed files with 435 additions and 156 deletions.
179 changes: 179 additions & 0 deletions client/build_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"github.com/moby/buildkit/solver/pb"
"github.com/moby/buildkit/util/entitlements"
utilsystem "github.com/moby/buildkit/util/system"
"github.com/moby/buildkit/util/testutil/echoserver"
"github.com/moby/buildkit/util/testutil/integration"
"github.com/pkg/errors"
"github.com/stretchr/testify/require"
Expand All @@ -48,6 +49,7 @@ func TestClientGatewayIntegration(t *testing.T) {
testClientGatewayExecError,
testClientGatewaySlowCacheExecError,
testClientGatewayExecFileActionError,
testClientGatewayContainerExtraHosts,
}, integration.WithMirroredImages(integration.OfficialImages("busybox:latest")))

integration.Run(t, []integration.Test{
Expand All @@ -58,6 +60,16 @@ func TestClientGatewayIntegration(t *testing.T) {
"insecure": securityInsecure,
}),
)

integration.Run(t, []integration.Test{
testClientGatewayContainerHostNetworking,
},
integration.WithMirroredImages(integration.OfficialImages("busybox:latest")),
integration.WithMatrix("netmode", map[string]interface{}{
"default": defaultNetwork,
"host": hostNetwork,
}),
)
}

func testClientGatewaySolve(t *testing.T, sb integration.Sandbox) {
Expand Down Expand Up @@ -1594,6 +1606,173 @@ func testClientGatewayContainerSecurityMode(t *testing.T, sb integration.Sandbox
checkAllReleasable(t, c, sb, true)
}

func testClientGatewayContainerExtraHosts(t *testing.T, sb integration.Sandbox) {
requiresLinux(t)

ctx := sb.Context()
product := "buildkit_test"

c, err := New(ctx, sb.Address())
require.NoError(t, err)
defer c.Close()

b := func(ctx context.Context, c client.Client) (*client.Result, error) {
st := llb.Image("busybox")

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,
}},
ExtraHosts: []*pb.HostIP{{
Host: "some.host",
IP: "169.254.11.22",
}},
})

if err != nil {
return nil, err
}

stdout := bytes.NewBuffer(nil)
stderr := bytes.NewBuffer(nil)

pid, err := ctr.Start(ctx, client.StartRequest{
Args: []string{"grep", "169.254.11.22\tsome.host", "/etc/hosts"},
Stdout: &nopCloser{stdout},
Stderr: &nopCloser{stderr},
})
if err != nil {
ctr.Release(ctx)
return nil, err
}
defer ctr.Release(ctx)

err = pid.Wait()

t.Logf("Stdout: %q", stdout.String())
t.Logf("Stderr: %q", stderr.String())

require.NoError(t, err)

return &client.Result{}, nil
}

_, err = c.Build(ctx, SolveOpt{}, product, b, nil)
require.NoError(t, err)

checkAllReleasable(t, c, sb, true)
}

func testClientGatewayContainerHostNetworking(t *testing.T, sb integration.Sandbox) {
if os.Getenv("BUILDKIT_RUN_NETWORK_INTEGRATION_TESTS") == "" {
t.SkipNow()
}

if sb.Rootless() && sb.Value("netmode") == defaultNetwork {
// skip "default" network test for rootless, it always runs with "host" network
// https://github.com/moby/buildkit/blob/v0.9.0/docs/rootless.md#known-limitations
t.SkipNow()
}

requiresLinux(t)

ctx := sb.Context()
product := "buildkit_test"

var allowedEntitlements []entitlements.Entitlement
netMode := pb.NetMode_UNSET
if sb.Value("netmode") == hostNetwork {
netMode = pb.NetMode_HOST
allowedEntitlements = []entitlements.Entitlement{entitlements.EntitlementNetworkHost}
}
c, err := New(sb.Context(), sb.Address())
require.NoError(t, err)
defer c.Close()

s, err := echoserver.NewTestServer("foo")
require.NoError(t, err)
addrParts := strings.Split(s.Addr().String(), ":")
port := addrParts[len(addrParts)-1]

b := func(ctx context.Context, c client.Client) (*client.Result, error) {
st := llb.Image("busybox")

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,
}},
NetMode: netMode,
})

if err != nil {
return nil, err
}

stdout := bytes.NewBuffer(nil)
stderr := bytes.NewBuffer(nil)

pid, err := ctr.Start(ctx, client.StartRequest{
Args: []string{"/bin/sh", "-c", fmt.Sprintf("nc 127.0.0.1 %s | grep foo", port)},
Stdout: &nopCloser{stdout},
Stderr: &nopCloser{stderr},
})
if err != nil {
ctr.Release(ctx)
return nil, err
}
defer ctr.Release(ctx)

err = pid.Wait()

t.Logf("Stdout: %q", stdout.String())
t.Logf("Stderr: %q", stderr.String())

if netMode == pb.NetMode_HOST {
require.NoError(t, err)
} else {
require.Error(t, err)
}

return &client.Result{}, nil
}

solveOpts := SolveOpt{
AllowedEntitlements: allowedEntitlements,
}
_, err = c.Build(ctx, solveOpts, product, b, nil)
require.NoError(t, err)

checkAllReleasable(t, c, sb, true)
}

type nopCloser struct {
io.Writer
}
Expand Down
1 change: 1 addition & 0 deletions frontend/gateway/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ type Client interface {
type NewContainerRequest struct {
Mounts []Mount
NetMode pb.NetMode
ExtraHosts []*pb.HostIP
Platform *pb.Platform
Constraints *pb.WorkerConstraints
}
Expand Down
42 changes: 23 additions & 19 deletions frontend/gateway/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
type NewContainerRequest struct {
ContainerID string
NetMode opspb.NetMode
ExtraHosts []executor.HostIP
Mounts []Mount
Platform *opspb.Platform
Constraints *opspb.WorkerConstraints
Expand All @@ -52,13 +53,14 @@ func NewContainer(ctx context.Context, w worker.Worker, sm *session.Manager, g s
platform = *req.Platform
}
ctr := &gatewayContainer{
id: req.ContainerID,
netMode: req.NetMode,
platform: platform,
executor: w.Executor(),
errGroup: eg,
ctx: ctx,
cancel: cancel,
id: req.ContainerID,
netMode: req.NetMode,
extraHosts: req.ExtraHosts,
platform: platform,
executor: w.Executor(),
errGroup: eg,
ctx: ctx,
cancel: cancel,
}

var (
Expand Down Expand Up @@ -276,18 +278,19 @@ func PrepareMounts(ctx context.Context, mm *mounts.MountManager, cm cache.Manage
}

type gatewayContainer struct {
id string
netMode opspb.NetMode
platform opspb.Platform
rootFS executor.Mount
mounts []executor.Mount
executor executor.Executor
started bool
errGroup *errgroup.Group
mu sync.Mutex
cleanup []func() error
ctx context.Context
cancel func()
id string
netMode opspb.NetMode
extraHosts []executor.HostIP
platform opspb.Platform
rootFS executor.Mount
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) {
Expand All @@ -300,6 +303,7 @@ func (gwCtr *gatewayContainer) Start(ctx context.Context, req client.StartReques
Cwd: req.Cwd,
Tty: req.Tty,
NetMode: gwCtr.netMode,
ExtraHosts: gwCtr.extraHosts,
SecurityMode: req.SecurityMode,
},
Stdin: req.Stdin,
Expand Down
5 changes: 5 additions & 0 deletions frontend/gateway/forwarder/forward.go
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,11 @@ func (c *bridgeClient) NewContainer(ctx context.Context, req client.NewContainer
return nil, err
}

ctrReq.ExtraHosts, err = gateway.ParseExtraHosts(req.ExtraHosts)
if err != nil {
return nil, err
}

w, err := c.workers.GetDefault()
if err != nil {
return nil, err
Expand Down
5 changes: 5 additions & 0 deletions frontend/gateway/gateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -888,6 +888,11 @@ func (lbf *llbBridgeForwarder) NewContainer(ctx context.Context, in *pb.NewConta
return nil, stack.Enable(err)
}

ctrReq.ExtraHosts, err = ParseExtraHosts(in.ExtraHosts)
if err != nil {
return nil, stack.Enable(err)
}

ctr, err := NewContainer(context.Background(), w, lbf.sm, group, ctrReq)
if err != nil {
return nil, stack.Enable(err)
Expand Down
2 changes: 2 additions & 0 deletions frontend/gateway/grpcclient/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -709,6 +709,8 @@ func (c *grpcClient) NewContainer(ctx context.Context, req client.NewContainerRe
Mounts: mounts,
Platform: req.Platform,
Constraints: req.Constraints,
Network: req.NetMode,
ExtraHosts: req.ExtraHosts,
})
if err != nil {
return nil, err
Expand Down
11 changes: 11 additions & 0 deletions frontend/gateway/pb/caps.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ const (
// containers directly through the gateway
CapGatewayExec apicaps.CapID = "gateway.exec"

// CapGatewayExecExtraHosts is the capability to add additional hosts to
// /etc/hosts for containers created via gateway exec.
CapGatewayExecExtraHosts apicaps.CapID = "gateway.exec.extrahosts"

// CapFrontendCaps can be used to check that frontends define support for certain capabilities
CapFrontendCaps apicaps.CapID = "frontend.caps"

Expand Down Expand Up @@ -156,6 +160,13 @@ func init() {
Status: apicaps.CapStatusExperimental,
})

Caps.Init(apicaps.Cap{
ID: CapGatewayExecExtraHosts,
Name: "gateway exec extra-hosts",
Enabled: true,
Status: apicaps.CapStatusExperimental,
})

Caps.Init(apicaps.Cap{
ID: CapFrontendCaps,
Name: "frontend capabilities",
Expand Down
Loading

0 comments on commit 7658c78

Please sign in to comment.