diff --git a/daemon/daemon_linux.go b/daemon/daemon_linux.go index 6a5790b4fc..2038487415 100644 --- a/daemon/daemon_linux.go +++ b/daemon/daemon_linux.go @@ -9,18 +9,13 @@ import ( "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 @@ func setupResolvConf(config *config.Config) { 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 8a68110878..0000000000 --- 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 4c5d822f76..0000000000 --- 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 abdfb7ae69..611fb8348f 100644 --- a/vendor.conf +++ b/vendor.conf @@ -27,7 +27,7 @@ github.com/imdario/mergo 7c29201646fa3de8506f70121347 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 @@ github.com/gofrs/flock 7f43ea2e6a643ad441fc12d0ecc0 # 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 2896011dbf..3b92b695c7 100644 --- a/vendor/github.com/docker/libnetwork/controller.go +++ b/vendor/github.com/docker/libnetwork/controller.go @@ -339,7 +339,6 @@ func (c *controller) clusterAgentInit() { } } case cluster.EventNodeLeave: - keysAvailable = false c.agentOperationStart() c.Lock() c.keys = nil @@ -706,11 +705,17 @@ const overlayDSROptionString = "dsr" // 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 @@ func (c *controller) NewNetwork(networkType, name string, id string, options ... } 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 @@ func (c *controller) NewNetwork(networkType, name string, id string, options ... // 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 23caf7f120..946bb87123 100644 --- a/vendor/github.com/docker/libnetwork/resolvconf/resolvconf.go +++ b/vendor/github.com/docker/libnetwork/resolvconf/resolvconf.go @@ -15,10 +15,44 @@ import ( ) 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"} @@ -55,7 +89,7 @@ type File struct { // 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 @@ func GetIfChanged() (*File, error) { 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 db1b66b190..f43b5d6035 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 @@ func (sb *sandbox) setupDNS() error { 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 f22eceed22..a65f2ddecf 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 @@ func GetResolvConf(ctx context.Context, stateDir string) (string, error) { 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