Merge pull request #35 from tiborvass/18.09-fix-network-buildkit

[18.09] builder: fix bridge networking when using buildkit
diff --git a/builder/builder-next/builder.go b/builder/builder-next/builder.go
index 97cdfc6..6d3098a 100644
--- a/builder/builder-next/builder.go
+++ b/builder/builder-next/builder.go
@@ -37,6 +37,7 @@
 type Opt struct {
 	SessionManager    *session.Manager
 	Root              string
+	NetnsRoot         string
 	Dist              images.DistributionServices
 	NetworkController libnetwork.NetworkController
 }
diff --git a/builder/builder-next/controller.go b/builder/builder-next/controller.go
index 9794f5f..48e74cb 100644
--- a/builder/builder-next/controller.go
+++ b/builder/builder-next/controller.go
@@ -90,7 +90,7 @@
 		return nil, err
 	}
 
-	exec, err := newExecutor(root, opt.NetworkController)
+	exec, err := newExecutor(root, opt.NetnsRoot, opt.NetworkController)
 	if err != nil {
 		return nil, err
 	}
diff --git a/builder/builder-next/executor_unix.go b/builder/builder-next/executor_unix.go
index 44f2dfc..f72a27f 100644
--- a/builder/builder-next/executor_unix.go
+++ b/builder/builder-next/executor_unix.go
@@ -3,41 +3,46 @@
 package buildkit
 
 import (
-	"fmt"
+	"os"
 	"path/filepath"
+	"strconv"
 	"sync"
 
 	"github.com/docker/libnetwork"
 	"github.com/moby/buildkit/executor"
 	"github.com/moby/buildkit/executor/runcexecutor"
 	"github.com/moby/buildkit/identity"
+	"github.com/moby/buildkit/solver/pb"
 	"github.com/moby/buildkit/util/network"
-	"github.com/pkg/errors"
-	"github.com/sirupsen/logrus"
+	specs "github.com/opencontainers/runtime-spec/specs-go"
 )
 
 const networkName = "bridge"
 
-func newExecutor(root string, net libnetwork.NetworkController) (executor.Executor, error) {
-	// FIXME: fix bridge networking
-	_ = bridgeProvider{}
+func newExecutor(root, netnsRoot string, net libnetwork.NetworkController) (executor.Executor, error) {
+	networkProviders := map[pb.NetMode]network.Provider{
+		pb.NetMode_UNSET: &bridgeProvider{NetworkController: net, netnsRoot: netnsRoot},
+		pb.NetMode_HOST:  network.NewHostProvider(),
+		pb.NetMode_NONE:  network.NewNoneProvider(),
+	}
 	return runcexecutor.New(runcexecutor.Opt{
 		Root:              filepath.Join(root, "executor"),
 		CommandCandidates: []string{"docker-runc", "runc"},
-	}, nil)
+	}, networkProviders)
 }
 
 type bridgeProvider struct {
 	libnetwork.NetworkController
+	netnsRoot string
 }
 
-func (p *bridgeProvider) NewInterface() (network.Interface, error) {
+func (p *bridgeProvider) New() (network.Namespace, error) {
 	n, err := p.NetworkByName(networkName)
 	if err != nil {
 		return nil, err
 	}
 
-	iface := &lnInterface{ready: make(chan struct{})}
+	iface := &lnInterface{ready: make(chan struct{}), provider: p}
 	iface.Once.Do(func() {
 		go iface.init(p.NetworkController, n)
 	})
@@ -45,33 +50,13 @@
 	return iface, nil
 }
 
-func (p *bridgeProvider) Release(iface network.Interface) error {
-	go func() {
-		if err := p.release(iface); err != nil {
-			logrus.Errorf("%s", err)
-		}
-	}()
-	return nil
-}
-
-func (p *bridgeProvider) release(iface network.Interface) error {
-	li, ok := iface.(*lnInterface)
-	if !ok {
-		return errors.Errorf("invalid interface %T", iface)
-	}
-	err := li.sbx.Delete()
-	if err1 := li.ep.Delete(true); err1 != nil && err == nil {
-		err = err1
-	}
-	return err
-}
-
 type lnInterface struct {
 	ep  libnetwork.Endpoint
 	sbx libnetwork.Sandbox
 	sync.Once
-	err   error
-	ready chan struct{}
+	err      error
+	ready    chan struct{}
+	provider *bridgeProvider
 }
 
 func (iface *lnInterface) init(c libnetwork.NetworkController, n libnetwork.Network) {
@@ -99,14 +84,26 @@
 	iface.ep = ep
 }
 
-func (iface *lnInterface) Set(pid int) error {
+func (iface *lnInterface) Set(s *specs.Spec) {
 	<-iface.ready
 	if iface.err != nil {
-		return iface.err
+		return
 	}
-	return iface.sbx.SetKey(fmt.Sprintf("/proc/%d/ns/net", pid))
+	// attach netns to bridge within the container namespace, using reexec in a prestart hook
+	s.Hooks = &specs.Hooks{
+		Prestart: []specs.Hook{{
+			Path: filepath.Join("/proc", strconv.Itoa(os.Getpid()), "exe"),
+			Args: []string{"libnetwork-setkey", iface.sbx.ContainerID(), iface.provider.NetworkController.ID()},
+		}},
+	}
 }
 
-func (iface *lnInterface) Remove(pid int) error {
-	return nil
+func (iface *lnInterface) Close() error {
+	<-iface.ready
+	err := iface.sbx.Delete()
+	if iface.err != nil {
+		// iface.err takes precedence over cleanup errors
+		return iface.err
+	}
+	return err
 }
diff --git a/builder/builder-next/executor_windows.go b/builder/builder-next/executor_windows.go
index e3b5aba..f19bf18 100644
--- a/builder/builder-next/executor_windows.go
+++ b/builder/builder-next/executor_windows.go
@@ -10,7 +10,7 @@
 	"github.com/moby/buildkit/executor"
 )
 
-func newExecutor(_ string, _ libnetwork.NetworkController) (executor.Executor, error) {
+func newExecutor(_, _ string, _ libnetwork.NetworkController) (executor.Executor, error) {
 	return &winExecutor{}, nil
 }
 
diff --git a/cmd/dockerd/daemon.go b/cmd/dockerd/daemon.go
index 199bba0..56f88c4 100644
--- a/cmd/dockerd/daemon.go
+++ b/cmd/dockerd/daemon.go
@@ -288,6 +288,7 @@
 	bk, err := buildkit.New(buildkit.Opt{
 		SessionManager:    sm,
 		Root:              filepath.Join(config.Root, "buildkit"),
+		NetnsRoot:         filepath.Join(config.ExecRoot, "netns"),
 		Dist:              daemon.DistributionServices(),
 		NetworkController: daemon.NetworkController(),
 	})
diff --git a/vendor.conf b/vendor.conf
index 4269c9f..0156abb 100644
--- a/vendor.conf
+++ b/vendor.conf
@@ -26,7 +26,7 @@
 golang.org/x/sync 1d60e4601c6fd243af51cc01ddf169918a5407ca
 
 # buildkit
-github.com/moby/buildkit 49906c62925ed429ec9174a0b6869982967f1a39
+github.com/moby/buildkit e1cd06ad6b74e4b747306c4408c451b3b6d87a89
 github.com/tonistiigi/fsutil b19464cd1b6a00773b4f2eb7acf9c30426f9df42
 github.com/grpc-ecosystem/grpc-opentracing 8e809c8a86450a29b90dcc9efbf062d0fe6d9746
 github.com/opentracing/opentracing-go 1361b9cd60be79c4c3a7fa9841b3c132e40066a7
diff --git a/vendor/github.com/moby/buildkit/executor/oci/spec_unix.go b/vendor/github.com/moby/buildkit/executor/oci/spec_unix.go
index 498f6c0..e529b96 100644
--- a/vendor/github.com/moby/buildkit/executor/oci/spec_unix.go
+++ b/vendor/github.com/moby/buildkit/executor/oci/spec_unix.go
@@ -14,6 +14,7 @@
 	"github.com/mitchellh/hashstructure"
 	"github.com/moby/buildkit/executor"
 	"github.com/moby/buildkit/snapshot"
+	"github.com/moby/buildkit/util/network"
 	specs "github.com/opencontainers/runtime-spec/specs-go"
 	"github.com/pkg/errors"
 )
@@ -21,7 +22,7 @@
 // Ideally we don't have to import whole containerd just for the default spec
 
 // GenerateSpec generates spec using containerd functionality.
-func GenerateSpec(ctx context.Context, meta executor.Meta, mounts []executor.Mount, id, resolvConf, hostsFile string, hostNetwork bool, opts ...oci.SpecOpts) (*specs.Spec, func(), error) {
+func GenerateSpec(ctx context.Context, meta executor.Meta, mounts []executor.Mount, id, resolvConf, hostsFile string, namespace network.Namespace, opts ...oci.SpecOpts) (*specs.Spec, func(), error) {
 	c := &containers.Container{
 		ID: id,
 	}
@@ -30,16 +31,15 @@
 		ctx = namespaces.WithNamespace(ctx, "buildkit")
 	}
 
-	if hostNetwork {
-		opts = append(opts, oci.WithHostNamespace(specs.NetworkNamespace))
-	}
-
 	// Note that containerd.GenerateSpec is namespaced so as to make
 	// specs.Linux.CgroupsPath namespaced
 	s, err := oci.GenerateSpec(ctx, nil, c, opts...)
 	if err != nil {
 		return nil, nil, err
 	}
+	// set the networking information on the spec
+	namespace.Set(s)
+
 	s.Process.Args = meta.Args
 	s.Process.Env = meta.Env
 	s.Process.Cwd = meta.Cwd
diff --git a/vendor/github.com/moby/buildkit/executor/runcexecutor/executor.go b/vendor/github.com/moby/buildkit/executor/runcexecutor/executor.go
index 2874314..4458a99 100644
--- a/vendor/github.com/moby/buildkit/executor/runcexecutor/executor.go
+++ b/vendor/github.com/moby/buildkit/executor/runcexecutor/executor.go
@@ -10,7 +10,6 @@
 	"path/filepath"
 	"strconv"
 	"strings"
-	"sync"
 	"syscall"
 
 	"github.com/containerd/containerd/contrib/seccomp"
@@ -26,7 +25,6 @@
 	"github.com/moby/buildkit/util/network"
 	rootlessspecconv "github.com/moby/buildkit/util/rootless/specconv"
 	"github.com/moby/buildkit/util/system"
-	runcsystem "github.com/opencontainers/runc/libcontainer/system"
 	specs "github.com/opencontainers/runtime-spec/specs-go"
 	"github.com/pkg/errors"
 	"github.com/sirupsen/logrus"
@@ -43,14 +41,14 @@
 var defaultCommandCandidates = []string{"buildkit-runc", "runc"}
 
 type runcExecutor struct {
-	runc            *runc.Runc
-	root            string
-	cmd             string
-	rootless        bool
-	networkProvider network.Provider
+	runc             *runc.Runc
+	root             string
+	cmd              string
+	rootless         bool
+	networkProviders map[pb.NetMode]network.Provider
 }
 
-func New(opt Opt, networkProvider network.Provider) (executor.Executor, error) {
+func New(opt Opt, networkProviders map[pb.NetMode]network.Provider) (executor.Executor, error) {
 	cmds := opt.CommandCandidates
 	if cmds == nil {
 		cmds = defaultCommandCandidates
@@ -70,10 +68,6 @@
 
 	root := opt.Root
 
-	if err := setSubReaper(); err != nil {
-		return nil, err
-	}
-
 	if err := os.MkdirAll(root, 0700); err != nil {
 		return nil, errors.Wrapf(err, "failed to create %s", root)
 	}
@@ -98,36 +92,28 @@
 	}
 
 	w := &runcExecutor{
-		runc:            runtime,
-		root:            root,
-		rootless:        opt.Rootless,
-		networkProvider: networkProvider,
+		runc:             runtime,
+		root:             root,
+		rootless:         opt.Rootless,
+		networkProviders: networkProviders,
 	}
 	return w, nil
 }
 
 func (w *runcExecutor) Exec(ctx context.Context, meta executor.Meta, root cache.Mountable, mounts []executor.Mount, stdin io.ReadCloser, stdout, stderr io.WriteCloser) error {
-	var iface network.Interface
-	// FIXME: still uses host if no provider configured
-	if meta.NetMode == pb.NetMode_UNSET {
-		if w.networkProvider != nil {
-			var err error
-			iface, err = w.networkProvider.NewInterface()
-			if err != nil || iface == nil {
-				meta.NetMode = pb.NetMode_HOST
-			}
-		} else {
-			meta.NetMode = pb.NetMode_HOST
-		}
+	provider, ok := w.networkProviders[meta.NetMode]
+	if !ok {
+		return errors.Errorf("unknown network mode %s", meta.NetMode)
 	}
+	namespace, err := provider.New()
+	if err != nil {
+		return err
+	}
+	defer namespace.Close()
+
 	if meta.NetMode == pb.NetMode_HOST {
 		logrus.Info("enabling HostNetworking")
 	}
-	defer func() {
-		if iface != nil {
-			w.networkProvider.Release(iface)
-		}
-	}()
 
 	resolvConf, err := oci.GetResolvConf(ctx, w.root)
 	if err != nil {
@@ -187,7 +173,7 @@
 	if meta.ReadonlyRootFS {
 		opts = append(opts, containerdoci.WithRootFSReadonly())
 	}
-	spec, cleanup, err := oci.GenerateSpec(ctx, meta, mounts, id, resolvConf, hostsFile, meta.NetMode == pb.NetMode_HOST, opts...)
+	spec, cleanup, err := oci.GenerateSpec(ctx, meta, mounts, id, resolvConf, hostsFile, namespace, opts...)
 	if err != nil {
 		return err
 	}
@@ -225,75 +211,14 @@
 	}
 	defer forwardIO.Close()
 
-	pidFilePath := filepath.Join(w.root, "runc_pid_"+identity.NewID())
-	defer os.RemoveAll(pidFilePath)
-
 	logrus.Debugf("> creating %s %v", id, meta.Args)
-	err = w.runc.Create(ctx, id, bundle, &runc.CreateOpts{
-		PidFile: pidFilePath,
-		IO:      forwardIO,
+	status, err := w.runc.Run(ctx, id, bundle, &runc.CreateOpts{
+		IO: forwardIO,
 	})
 	if err != nil {
 		return err
 	}
-	forwardIO.release()
 
-	defer func() {
-		go func() {
-			if err := w.runc.Delete(context.TODO(), id, &runc.DeleteOpts{}); err != nil {
-				logrus.Errorf("failed to delete %s: %+v", id, err)
-			}
-		}()
-	}()
-
-	dt, err := ioutil.ReadFile(pidFilePath)
-	if err != nil {
-		return err
-	}
-	pid, err := strconv.Atoi(string(dt))
-	if err != nil {
-		return err
-	}
-
-	done := make(chan struct{})
-	defer close(done)
-
-	go func() {
-		select {
-		case <-done:
-		case <-ctx.Done():
-			syscall.Kill(-pid, syscall.SIGKILL)
-		}
-	}()
-
-	if iface != nil {
-		if err := iface.Set(pid); err != nil {
-			return errors.Wrap(err, "could not set the network")
-		}
-		defer func() {
-			iface.Remove(pid)
-		}()
-	}
-
-	err = w.runc.Start(ctx, id)
-	if err != nil {
-		return err
-	}
-
-	p, err := os.FindProcess(pid)
-	if err != nil {
-		return err
-	}
-
-	status := 0
-	ps, err := p.Wait()
-	if err != nil {
-		status = 255
-	}
-
-	if ws, ok := ps.Sys().(syscall.WaitStatus); ok {
-		status = ws.ExitStatus()
-	}
 	if status != 0 {
 		return errors.Errorf("exit code: %d", status)
 	}
@@ -336,7 +261,7 @@
 }
 
 func (s *forwardIO) Close() error {
-	s.release()
+	s.CloseAfterStart()
 	var err error
 	for _, cl := range s.toClose {
 		if err1 := cl.Close(); err == nil {
@@ -348,11 +273,12 @@
 }
 
 // release releases active FDs if the process doesn't need them any more
-func (s *forwardIO) release() {
+func (s *forwardIO) CloseAfterStart() error {
 	for _, cl := range s.toRelease {
 		cl.Close()
 	}
 	s.toRelease = nil
+	return nil
 }
 
 func (s *forwardIO) Set(cmd *exec.Cmd) {
@@ -401,16 +327,6 @@
 	return pw, nil
 }
 
-var subReaperOnce sync.Once
-var subReaperError error
-
-func setSubReaper() error {
-	subReaperOnce.Do(func() {
-		subReaperError = runcsystem.SetSubreaper(1)
-	})
-	return subReaperError
-}
-
 func (s *forwardIO) Stdin() io.WriteCloser {
 	return nil
 }
diff --git a/vendor/github.com/moby/buildkit/util/network/host.go b/vendor/github.com/moby/buildkit/util/network/host.go
new file mode 100644
index 0000000..dc58b1c
--- /dev/null
+++ b/vendor/github.com/moby/buildkit/util/network/host.go
@@ -0,0 +1,28 @@
+package network
+
+import (
+	"github.com/containerd/containerd/oci"
+	specs "github.com/opencontainers/runtime-spec/specs-go"
+)
+
+func NewHostProvider() Provider {
+	return &host{}
+}
+
+type host struct {
+}
+
+func (h *host) New() (Namespace, error) {
+	return &hostNS{}, nil
+}
+
+type hostNS struct {
+}
+
+func (h *hostNS) Set(s *specs.Spec) {
+	oci.WithHostNamespace(specs.NetworkNamespace)(nil, nil, nil, s)
+}
+
+func (h *hostNS) Close() error {
+	return nil
+}
diff --git a/vendor/github.com/moby/buildkit/util/network/network.go b/vendor/github.com/moby/buildkit/util/network/network.go
index 2f66652..055a52d 100644
--- a/vendor/github.com/moby/buildkit/util/network/network.go
+++ b/vendor/github.com/moby/buildkit/util/network/network.go
@@ -1,17 +1,32 @@
 package network
 
-// Provider interface for Network
-type Provider interface {
-	NewInterface() (Interface, error)
-	Release(Interface) error
+import (
+	"io"
+
+	"github.com/moby/buildkit/solver/pb"
+	specs "github.com/opencontainers/runtime-spec/specs-go"
+)
+
+// Default returns the default network provider set
+func Default() map[pb.NetMode]Provider {
+	return map[pb.NetMode]Provider{
+		// FIXME: still uses host if no provider configured
+		pb.NetMode_UNSET: NewHostProvider(),
+		pb.NetMode_HOST:  NewHostProvider(),
+		pb.NetMode_NONE:  NewNoneProvider(),
+	}
 }
 
-// Interface of network for workers
-type Interface interface {
-	// Set the pid with network interace namespace
-	Set(int) error
-	// Removes the network interface
-	Remove(int) error
+// Provider interface for Network
+type Provider interface {
+	New() (Namespace, error)
+}
+
+// Namespace of network for workers
+type Namespace interface {
+	io.Closer
+	// Set the namespace on the spec
+	Set(*specs.Spec)
 }
 
 // NetworkOpts hold network options
diff --git a/vendor/github.com/moby/buildkit/util/network/none.go b/vendor/github.com/moby/buildkit/util/network/none.go
new file mode 100644
index 0000000..ebf1ebd
--- /dev/null
+++ b/vendor/github.com/moby/buildkit/util/network/none.go
@@ -0,0 +1,26 @@
+package network
+
+import (
+	specs "github.com/opencontainers/runtime-spec/specs-go"
+)
+
+func NewNoneProvider() Provider {
+	return &none{}
+}
+
+type none struct {
+}
+
+func (h *none) New() (Namespace, error) {
+	return &noneNS{}, nil
+}
+
+type noneNS struct {
+}
+
+func (h *noneNS) Set(s *specs.Spec) {
+}
+
+func (h *noneNS) Close() error {
+	return nil
+}