build: buildkit now also uses systemd's resolv.conf

Signed-off-by: Tibor Vass <tibor@docker.com>
This commit is contained in:
Tibor Vass 2019-05-31 21:09:22 +00:00
parent 2e58093662
commit 8ff4ec98cf
8 changed files with 55 additions and 182 deletions

View file

@ -9,18 +9,13 @@ import (
"strings" "strings"
"github.com/docker/docker/daemon/config" "github.com/docker/docker/daemon/config"
"github.com/docker/docker/internal/procfs"
"github.com/docker/docker/pkg/fileutils" "github.com/docker/docker/pkg/fileutils"
"github.com/docker/docker/pkg/mount" "github.com/docker/docker/pkg/mount"
"github.com/docker/libnetwork/resolvconf"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/sirupsen/logrus" "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, // On Linux, plugins use a static path for storing execution state,
// instead of deriving path from daemon's exec-root. This is because // instead of deriving path from daemon's exec-root. This is because
// plugin socket files are created here and they cannot exceed max // plugin socket files are created here and they cannot exceed max
@ -148,20 +143,5 @@ func setupResolvConf(config *config.Config) {
if config.ResolvConf != "" { if config.ResolvConf != "" {
return return
} }
config.ResolvConf = resolvconf.Path()
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)
}
} }

View file

@ -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
}

View file

@ -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()
}
}

View file

@ -27,7 +27,7 @@ github.com/imdario/mergo 7c29201646fa3de8506f70121347
golang.org/x/sync e225da77a7e68af35c70ccbf71af2b83e6acac3c golang.org/x/sync e225da77a7e68af35c70ccbf71af2b83e6acac3c
# buildkit # buildkit
github.com/moby/buildkit 37d53758a68d9f5cede1806dbb2da7c3caa8d5bc github.com/moby/buildkit 1f89ec125f84c097bdf3a063be622c4238dba5f8
github.com/tonistiigi/fsutil 3bbb99cdbd76619ab717299830c60f6f2a533a6b github.com/tonistiigi/fsutil 3bbb99cdbd76619ab717299830c60f6f2a533a6b
github.com/grpc-ecosystem/grpc-opentracing 8e809c8a86450a29b90dcc9efbf062d0fe6d9746 github.com/grpc-ecosystem/grpc-opentracing 8e809c8a86450a29b90dcc9efbf062d0fe6d9746
github.com/opentracing/opentracing-go 1361b9cd60be79c4c3a7fa9841b3c132e40066a7 github.com/opentracing/opentracing-go 1361b9cd60be79c4c3a7fa9841b3c132e40066a7
@ -39,7 +39,7 @@ github.com/gofrs/flock 7f43ea2e6a643ad441fc12d0ecc0
# libnetwork # libnetwork
# When updating, also update LIBNETWORK_COMMIT in hack/dockerfile/install/proxy.installer accordingly # 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/docker/go-events 9461782956ad83b30282bf90e31fa6a70c255ba9
github.com/armon/go-radix e39d623f12e8e41c7b5529e9a9dd67a1e2261f80 github.com/armon/go-radix e39d623f12e8e41c7b5529e9a9dd67a1e2261f80
github.com/armon/go-metrics eb0af217e5e9747e41dd5303755356b62d28e3ec github.com/armon/go-metrics eb0af217e5e9747e41dd5303755356b62d28e3ec

View file

@ -339,7 +339,6 @@ func (c *controller) clusterAgentInit() {
} }
} }
case cluster.EventNodeLeave: case cluster.EventNodeLeave:
keysAvailable = false
c.agentOperationStart() c.agentOperationStart()
c.Lock() c.Lock()
c.keys = nil c.keys = nil
@ -706,11 +705,17 @@ const overlayDSROptionString = "dsr"
// NewNetwork creates a new network of the specified network type. The options // NewNetwork creates a new network of the specified network type. The options
// are network specific and modeled in a generic way. // are network specific and modeled in a generic way.
func (c *controller) NewNetwork(networkType, name string, id string, options ...NetworkOption) (Network, error) { func (c *controller) NewNetwork(networkType, name string, id string, options ...NetworkOption) (Network, error) {
var (
cap *driverapi.Capability
err error
t *network
)
if id != "" { if id != "" {
c.networkLocker.Lock(id) c.networkLocker.Lock(id)
defer c.networkLocker.Unlock(id) defer c.networkLocker.Unlock(id)
if _, err := c.NetworkByID(id); err == nil { if _, err = c.NetworkByID(id); err == nil {
return nil, NetworkNameError(id) return nil, NetworkNameError(id)
} }
} }
@ -739,15 +744,10 @@ func (c *controller) NewNetwork(networkType, name string, id string, options ...
} }
network.processOptions(options...) network.processOptions(options...)
if err := network.validateConfiguration(); err != nil { if err = network.validateConfiguration(); err != nil {
return nil, err return nil, err
} }
var (
cap *driverapi.Capability
err error
)
// Reset network types, force local scope and skip allocation and // Reset network types, force local scope and skip allocation and
// plumbing for configuration networks. Reset of the config-only // plumbing for configuration networks. Reset of the config-only
// network drivers is needed so that this special network is not // 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, // From this point on, we need the network specific configuration,
// which may come from a configuration-only network // which may come from a configuration-only network
if network.configFrom != "" { if network.configFrom != "" {
t, err := c.getConfigNetwork(network.configFrom) t, err = c.getConfigNetwork(network.configFrom)
if err != nil { if err != nil {
return nil, types.NotFoundErrorf("configuration network %q does not exist", network.configFrom) 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) return nil, types.InternalErrorf("Failed to apply configuration: %v", err)
} }
defer func() { defer func() {

View file

@ -15,10 +15,44 @@ import (
) )
const ( const (
// DefaultResolvConf points to the default file used for dns configuration on a linux machine // defaultPath is the default path to the resolv.conf that contains information to resolve DNS. See Path().
DefaultResolvConf = "/etc/resolv.conf" 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 ( var (
// Note: the default IPv4 & IPv6 resolvers are set to Google's Public DNS // 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"} 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 // Get returns the contents of /etc/resolv.conf and its hash
func Get() (*File, error) { func Get() (*File, error) {
return GetSpecific(DefaultResolvConf) return GetSpecific(Path())
} }
// GetSpecific returns the contents of the user specified resolv.conf file and its hash // GetSpecific returns the contents of the user specified resolv.conf file and its hash
@ -78,7 +112,7 @@ func GetIfChanged() (*File, error) {
lastModified.Lock() lastModified.Lock()
defer lastModified.Unlock() defer lastModified.Unlock()
resolv, err := ioutil.ReadFile("/etc/resolv.conf") resolv, err := ioutil.ReadFile(Path())
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -213,8 +213,8 @@ func (sb *sandbox) setupDNS() error {
originResolvConfPath := sb.config.originResolvConfPath originResolvConfPath := sb.config.originResolvConfPath
if originResolvConfPath == "" { if originResolvConfPath == "" {
// if not specified fallback to default /etc/resolv.conf // fallback if not specified
originResolvConfPath = resolvconf.DefaultResolvConf originResolvConfPath = resolvconf.Path()
} }
currRC, err := resolvconf.GetSpecific(originResolvConfPath) currRC, err := resolvconf.GetSpecific(originResolvConfPath)
if err != nil { if err != nil {

View file

@ -29,7 +29,7 @@ func GetResolvConf(ctx context.Context, stateDir string) (string, error) {
generate = true generate = true
} }
if !generate { if !generate {
fiMain, err := os.Stat("/etc/resolv.conf") fiMain, err := os.Stat(resolvconf.Path())
if err != nil { if err != nil {
if !os.IsNotExist(err) { if !os.IsNotExist(err) {
return nil, err return nil, err