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