Merge pull request #260 from thaJeztah/19.03_backport_buildkit_systemd_resolvconf

[19.03 backport] build: buildkit now also uses systemd's resolv.conf
diff --git a/daemon/daemon_linux.go b/daemon/daemon_linux.go
index 6a5790b..2038487 100644
--- a/daemon/daemon_linux.go
+++ b/daemon/daemon_linux.go
@@ -9,18 +9,13 @@
 	"strings"
 
 	"github.com/docker/docker/daemon/config"
-	"github.com/docker/docker/internal/procfs"
 	"github.com/docker/docker/pkg/fileutils"
 	"github.com/docker/docker/pkg/mount"
+	"github.com/docker/libnetwork/resolvconf"
 	"github.com/pkg/errors"
 	"github.com/sirupsen/logrus"
 )
 
-const (
-	defaultResolvConf   = "/etc/resolv.conf"
-	alternateResolvConf = "/run/systemd/resolve/resolv.conf"
-)
-
 // On Linux, plugins use a static path for storing execution state,
 // instead of deriving path from daemon's exec-root. This is because
 // plugin socket files are created here and they cannot exceed max
@@ -148,20 +143,5 @@
 	if config.ResolvConf != "" {
 		return
 	}
-
-	config.ResolvConf = defaultResolvConf
-	pids, err := procfs.PidOf("systemd-resolved")
-	if err != nil {
-		logrus.Errorf("unable to check systemd-resolved status: %s", err)
-		return
-	}
-	if len(pids) > 0 && pids[0] > 0 {
-		_, err := os.Stat(alternateResolvConf)
-		if err == nil {
-			logrus.Infof("systemd-resolved is running, so using resolvconf: %s", alternateResolvConf)
-			config.ResolvConf = alternateResolvConf
-			return
-		}
-		logrus.Infof("systemd-resolved is running, but %s is not present, fallback to %s", alternateResolvConf, defaultResolvConf)
-	}
+	config.ResolvConf = resolvconf.Path()
 }
diff --git a/internal/procfs/procfs_linux.go b/internal/procfs/procfs_linux.go
deleted file mode 100644
index 8a68110..0000000
--- a/internal/procfs/procfs_linux.go
+++ /dev/null
@@ -1,105 +0,0 @@
-package procfs
-
-/*
-Copyright 2015 The Kubernetes Authors.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-import (
-	"bytes"
-	"fmt"
-	"io"
-	"io/ioutil"
-	"os"
-	"path/filepath"
-	"regexp"
-	"strconv"
-	"strings"
-	"unicode"
-
-	"github.com/sirupsen/logrus"
-)
-
-// PidOf finds process(es) with a specified name (regexp match)
-// and return their pid(s)
-func PidOf(name string) ([]int, error) {
-	if len(name) == 0 {
-		return []int{}, fmt.Errorf("name should not be empty")
-	}
-	re, err := regexp.Compile("(^|/)" + name + "$")
-	if err != nil {
-		return []int{}, err
-	}
-	return getPids(re), nil
-}
-
-func getPids(re *regexp.Regexp) []int {
-	pids := []int{}
-
-	dirFD, err := os.Open("/proc")
-	if err != nil {
-		return nil
-	}
-	defer dirFD.Close()
-
-	for {
-		// Read a small number at a time in case there are many entries, we don't want to
-		// allocate a lot here.
-		ls, err := dirFD.Readdir(10)
-		if err == io.EOF {
-			break
-		}
-		if err != nil {
-			return nil
-		}
-
-		for _, entry := range ls {
-			if !entry.IsDir() {
-				continue
-			}
-
-			// If the directory is not a number (i.e. not a PID), skip it
-			pid, err := strconv.Atoi(entry.Name())
-			if err != nil {
-				continue
-			}
-
-			cmdline, err := ioutil.ReadFile(filepath.Join("/proc", entry.Name(), "cmdline"))
-			if err != nil {
-				logrus.Infof("Error reading file %s: %+v", filepath.Join("/proc", entry.Name(), "cmdline"), err)
-				continue
-			}
-
-			// The bytes we read have '\0' as a separator for the command line
-			parts := bytes.SplitN(cmdline, []byte{0}, 2)
-			if len(parts) == 0 {
-				continue
-			}
-			// Split the command line itself we are interested in just the first part
-			exe := strings.FieldsFunc(string(parts[0]), func(c rune) bool {
-				return unicode.IsSpace(c) || c == ':'
-			})
-			if len(exe) == 0 {
-				continue
-			}
-			// Check if the name of the executable is what we are looking for
-			if re.MatchString(exe[0]) {
-				// Grab the PID from the directory path
-				pids = append(pids, pid)
-			}
-		}
-	}
-
-	return pids
-}
diff --git a/internal/procfs/procfs_linux_test.go b/internal/procfs/procfs_linux_test.go
deleted file mode 100644
index 4c5d822..0000000
--- a/internal/procfs/procfs_linux_test.go
+++ /dev/null
@@ -1,36 +0,0 @@
-package procfs
-
-import (
-	"os"
-	"path/filepath"
-	"regexp"
-	"runtime"
-	"testing"
-
-	"gotest.tools/assert"
-)
-
-func TestPidOf(t *testing.T) {
-	pids, err := PidOf(filepath.Base(os.Args[0]))
-	assert.NilError(t, err)
-	assert.Check(t, len(pids) == 1)
-	assert.DeepEqual(t, pids[0], os.Getpid())
-}
-
-func BenchmarkGetPids(b *testing.B) {
-	if runtime.GOOS == "darwin" || runtime.GOOS == "windows" {
-		b.Skipf("not supported on GOOS=%s", runtime.GOOS)
-	}
-
-	re, err := regexp.Compile("(^|/)" + filepath.Base(os.Args[0]) + "$")
-	assert.Check(b, err == nil)
-
-	for i := 0; i < b.N; i++ {
-		pids := getPids(re)
-
-		b.StopTimer()
-		assert.Check(b, len(pids) > 0)
-		assert.Check(b, pids[0] == os.Getpid())
-		b.StartTimer()
-	}
-}
diff --git a/vendor.conf b/vendor.conf
index c0d66d2..517512d 100644
--- a/vendor.conf
+++ b/vendor.conf
@@ -27,7 +27,7 @@
 golang.org/x/sync                                   e225da77a7e68af35c70ccbf71af2b83e6acac3c
 
 # buildkit
-github.com/moby/buildkit                            37d53758a68d9f5cede1806dbb2da7c3caa8d5bc
+github.com/moby/buildkit                            1f89ec125f84c097bdf3a063be622c4238dba5f8
 github.com/tonistiigi/fsutil                        3bbb99cdbd76619ab717299830c60f6f2a533a6b
 github.com/grpc-ecosystem/grpc-opentracing          8e809c8a86450a29b90dcc9efbf062d0fe6d9746
 github.com/opentracing/opentracing-go               1361b9cd60be79c4c3a7fa9841b3c132e40066a7
@@ -39,7 +39,7 @@
 # libnetwork
 
 # When updating, also update LIBNETWORK_COMMIT in hack/dockerfile/install/proxy.installer accordingly
-github.com/docker/libnetwork                        5ac07abef4eee176423fdc1b870d435258e2d381
+github.com/docker/libnetwork                        fc5a7d91d54cc98f64fc28f9e288b46a0bee756c
 github.com/docker/go-events                         9461782956ad83b30282bf90e31fa6a70c255ba9
 github.com/armon/go-radix                           e39d623f12e8e41c7b5529e9a9dd67a1e2261f80
 github.com/armon/go-metrics                         eb0af217e5e9747e41dd5303755356b62d28e3ec
diff --git a/vendor/github.com/docker/libnetwork/controller.go b/vendor/github.com/docker/libnetwork/controller.go
index 2896011..3b92b69 100644
--- a/vendor/github.com/docker/libnetwork/controller.go
+++ b/vendor/github.com/docker/libnetwork/controller.go
@@ -339,7 +339,6 @@
 				}
 			}
 		case cluster.EventNodeLeave:
-			keysAvailable = false
 			c.agentOperationStart()
 			c.Lock()
 			c.keys = nil
@@ -706,11 +705,17 @@
 // NewNetwork creates a new network of the specified network type. The options
 // are network specific and modeled in a generic way.
 func (c *controller) NewNetwork(networkType, name string, id string, options ...NetworkOption) (Network, error) {
+	var (
+		cap *driverapi.Capability
+		err error
+		t   *network
+	)
+
 	if id != "" {
 		c.networkLocker.Lock(id)
 		defer c.networkLocker.Unlock(id)
 
-		if _, err := c.NetworkByID(id); err == nil {
+		if _, err = c.NetworkByID(id); err == nil {
 			return nil, NetworkNameError(id)
 		}
 	}
@@ -739,15 +744,10 @@
 	}
 
 	network.processOptions(options...)
-	if err := network.validateConfiguration(); err != nil {
+	if err = network.validateConfiguration(); err != nil {
 		return nil, err
 	}
 
-	var (
-		cap *driverapi.Capability
-		err error
-	)
-
 	// Reset network types, force local scope and skip allocation and
 	// plumbing for configuration networks. Reset of the config-only
 	// network drivers is needed so that this special network is not
@@ -794,11 +794,11 @@
 	// From this point on, we need the network specific configuration,
 	// which may come from a configuration-only network
 	if network.configFrom != "" {
-		t, err := c.getConfigNetwork(network.configFrom)
+		t, err = c.getConfigNetwork(network.configFrom)
 		if err != nil {
 			return nil, types.NotFoundErrorf("configuration network %q does not exist", network.configFrom)
 		}
-		if err := t.applyConfigurationTo(network); err != nil {
+		if err = t.applyConfigurationTo(network); err != nil {
 			return nil, types.InternalErrorf("Failed to apply configuration: %v", err)
 		}
 		defer func() {
diff --git a/vendor/github.com/docker/libnetwork/resolvconf/resolvconf.go b/vendor/github.com/docker/libnetwork/resolvconf/resolvconf.go
index 23caf7f..946bb87 100644
--- a/vendor/github.com/docker/libnetwork/resolvconf/resolvconf.go
+++ b/vendor/github.com/docker/libnetwork/resolvconf/resolvconf.go
@@ -15,11 +15,45 @@
 )
 
 const (
-	// DefaultResolvConf points to the default file used for dns configuration on a linux machine
-	DefaultResolvConf = "/etc/resolv.conf"
+	// defaultPath is the default path to the resolv.conf that contains information to resolve DNS. See Path().
+	defaultPath = "/etc/resolv.conf"
+	// alternatePath is a path different from defaultPath, that may be used to resolve DNS. See Path().
+	alternatePath = "/run/systemd/resolve/resolv.conf"
 )
 
 var (
+	detectSystemdResolvConfOnce sync.Once
+	pathAfterSystemdDetection   = defaultPath
+)
+
+// Path returns the path to the resolv.conf file that libnetwork should use.
+//
+// When /etc/resolv.conf contains 127.0.0.53 as the only nameserver, then
+// it is assumed systemd-resolved manages DNS. Because inside the container 127.0.0.53
+// is not a valid DNS server, Path() returns /run/systemd/resolve/resolv.conf
+// which is the resolv.conf that systemd-resolved generates and manages.
+// Otherwise Path() returns /etc/resolv.conf.
+//
+// Errors are silenced as they will inevitably resurface at future open/read calls.
+//
+// More information at https://www.freedesktop.org/software/systemd/man/systemd-resolved.service.html#/etc/resolv.conf
+func Path() string {
+	detectSystemdResolvConfOnce.Do(func() {
+		candidateResolvConf, err := ioutil.ReadFile(defaultPath)
+		if err != nil {
+			// silencing error as it will resurface at next calls trying to read defaultPath
+			return
+		}
+		ns := GetNameservers(candidateResolvConf, types.IP)
+		if len(ns) == 1 && ns[0] == "127.0.0.53" {
+			pathAfterSystemdDetection = alternatePath
+			logrus.Infof("detected 127.0.0.53 nameserver, assuming systemd-resolved, so using resolv.conf: %s", alternatePath)
+		}
+	})
+	return pathAfterSystemdDetection
+}
+
+var (
 	// Note: the default IPv4 & IPv6 resolvers are set to Google's Public DNS
 	defaultIPv4Dns = []string{"nameserver 8.8.8.8", "nameserver 8.8.4.4"}
 	defaultIPv6Dns = []string{"nameserver 2001:4860:4860::8888", "nameserver 2001:4860:4860::8844"}
@@ -55,7 +89,7 @@
 
 // Get returns the contents of /etc/resolv.conf and its hash
 func Get() (*File, error) {
-	return GetSpecific(DefaultResolvConf)
+	return GetSpecific(Path())
 }
 
 // GetSpecific returns the contents of the user specified resolv.conf file and its hash
@@ -78,7 +112,7 @@
 	lastModified.Lock()
 	defer lastModified.Unlock()
 
-	resolv, err := ioutil.ReadFile("/etc/resolv.conf")
+	resolv, err := ioutil.ReadFile(Path())
 	if err != nil {
 		return nil, err
 	}
diff --git a/vendor/github.com/docker/libnetwork/sandbox_dns_unix.go b/vendor/github.com/docker/libnetwork/sandbox_dns_unix.go
index db1b66b..f43b5d6 100644
--- a/vendor/github.com/docker/libnetwork/sandbox_dns_unix.go
+++ b/vendor/github.com/docker/libnetwork/sandbox_dns_unix.go
@@ -213,8 +213,8 @@
 
 	originResolvConfPath := sb.config.originResolvConfPath
 	if originResolvConfPath == "" {
-		// if not specified fallback to default /etc/resolv.conf
-		originResolvConfPath = resolvconf.DefaultResolvConf
+		// fallback if not specified
+		originResolvConfPath = resolvconf.Path()
 	}
 	currRC, err := resolvconf.GetSpecific(originResolvConfPath)
 	if err != nil {
diff --git a/vendor/github.com/moby/buildkit/executor/oci/resolvconf.go b/vendor/github.com/moby/buildkit/executor/oci/resolvconf.go
index f22ecee..a65f2dd 100644
--- a/vendor/github.com/moby/buildkit/executor/oci/resolvconf.go
+++ b/vendor/github.com/moby/buildkit/executor/oci/resolvconf.go
@@ -29,7 +29,7 @@
 				generate = true
 			}
 			if !generate {
-				fiMain, err := os.Stat("/etc/resolv.conf")
+				fiMain, err := os.Stat(resolvconf.Path())
 				if err != nil {
 					if !os.IsNotExist(err) {
 						return nil, err