Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add missing ExtraHosts to gateway exec #2294

Merged
merged 2 commits into from
Aug 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)},
coryb marked this conversation as resolved.
Show resolved Hide resolved
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