Skip to content

Commit

Permalink
Merge pull request #37485 from fcrisciani/resolv
Browse files Browse the repository at this point in the history
Systemd-resolved proper handling
  • Loading branch information
thaJeztah committed Aug 1, 2018
2 parents 1dfcf4f + fc4ebe0 commit e158451
Show file tree
Hide file tree
Showing 57 changed files with 459 additions and 235 deletions.
4 changes: 2 additions & 2 deletions daemon/config/config.go
Expand Up @@ -75,6 +75,8 @@ type commonBridgeConfig struct {
type NetworkConfig struct {
// Default address pools for docker networks
DefaultAddressPools opts.PoolsOpt `json:"default-address-pools,omitempty"`
// NetworkControlPlaneMTU allows to specify the control plane MTU, this will allow to optimize the network use in some components
NetworkControlPlaneMTU int `json:"network-control-plane-mtu,omitempty"`
}

// CommonTLSOptions defines TLS configuration for the daemon server.
Expand Down Expand Up @@ -192,8 +194,6 @@ type CommonConfig struct {
// Exposed node Generic Resources
// e.g: ["orange=red", "orange=green", "orange=blue", "apple=3"]
NodeGenericResources []string `json:"node-generic-resources,omitempty"`
// NetworkControlPlaneMTU allows to specify the control plane MTU, this will allow to optimize the network use in some components
NetworkControlPlaneMTU int `json:"network-control-plane-mtu,omitempty"`

// ContainerAddr is the address used to connect to containerd if we're
// not starting it ourselves
Expand Down
6 changes: 6 additions & 0 deletions daemon/config/config_common_unix.go
Expand Up @@ -69,3 +69,9 @@ func (conf *Config) GetInitPath() string {
}
return DefaultInitBinary
}

// GetResolvConf returns the appropriate resolv.conf
// Check setupResolvConf on how this is selected
func (conf *Config) GetResolvConf() string {
return conf.ResolvConf
}
2 changes: 2 additions & 0 deletions daemon/config/config_unix.go
Expand Up @@ -37,6 +37,8 @@ type Config struct {
ShmSize opts.MemBytes `json:"default-shm-size,omitempty"`
NoNewPrivileges bool `json:"no-new-privileges,omitempty"`
IpcMode string `json:"default-ipc-mode,omitempty"`
// ResolvConf is the path to the configuration of the host resolver
ResolvConf string `json:"resolv-conf,omitempty"`
}

// BridgeConfig stores all the bridge driver specific
Expand Down
10 changes: 1 addition & 9 deletions daemon/container_operations.go
Expand Up @@ -63,21 +63,13 @@ func (daemon *Daemon) buildSandboxOptions(container *container.Container) ([]lib

if container.HostConfig.NetworkMode.IsHost() {
sboxOptions = append(sboxOptions, libnetwork.OptionUseDefaultSandbox())
if len(container.HostConfig.ExtraHosts) == 0 {
sboxOptions = append(sboxOptions, libnetwork.OptionOriginHostsPath("/etc/hosts"))
}
if len(container.HostConfig.DNS) == 0 && len(daemon.configStore.DNS) == 0 &&
len(container.HostConfig.DNSSearch) == 0 && len(daemon.configStore.DNSSearch) == 0 &&
len(container.HostConfig.DNSOptions) == 0 && len(daemon.configStore.DNSOptions) == 0 {
sboxOptions = append(sboxOptions, libnetwork.OptionOriginResolvConfPath("/etc/resolv.conf"))
}
} else {
// OptionUseExternalKey is mandatory for userns support.
// But optional for non-userns support
sboxOptions = append(sboxOptions, libnetwork.OptionUseExternalKey())
}

if err = setupPathsAndSandboxOptions(container, &sboxOptions); err != nil {
if err = daemon.setupPathsAndSandboxOptions(container, &sboxOptions); err != nil {
return nil, err
}

Expand Down
10 changes: 9 additions & 1 deletion daemon/container_operations_unix.go
Expand Up @@ -369,9 +369,17 @@ func (daemon *Daemon) isNetworkHotPluggable() bool {
return true
}

func setupPathsAndSandboxOptions(container *container.Container, sboxOptions *[]libnetwork.SandboxOption) error {
func (daemon *Daemon) setupPathsAndSandboxOptions(container *container.Container, sboxOptions *[]libnetwork.SandboxOption) error {
var err error

if container.HostConfig.NetworkMode.IsHost() {
// Point to the host files, so that will be copied into the container running in host mode
*sboxOptions = append(*sboxOptions, libnetwork.OptionOriginHostsPath("/etc/hosts"))
*sboxOptions = append(*sboxOptions, libnetwork.OptionOriginResolvConfPath("/etc/resolv.conf"))
} else {
*sboxOptions = append(*sboxOptions, libnetwork.OptionOriginResolvConfPath(daemon.configStore.GetResolvConf()))
}

container.HostsPath, err = container.GetRootResourcePath("hosts")
if err != nil {
return err
Expand Down
2 changes: 1 addition & 1 deletion daemon/container_operations_windows.go
Expand Up @@ -155,7 +155,7 @@ func (daemon *Daemon) isNetworkHotPluggable() bool {
return true
}

func setupPathsAndSandboxOptions(container *container.Container, sboxOptions *[]libnetwork.SandboxOption) error {
func (daemon *Daemon) setupPathsAndSandboxOptions(container *container.Container, sboxOptions *[]libnetwork.SandboxOption) error {
return nil
}

Expand Down
3 changes: 3 additions & 0 deletions daemon/daemon.go
Expand Up @@ -581,6 +581,9 @@ func NewDaemon(config *config.Config, registryService registry.Service, containe
// Do we have a disabled network?
config.DisableBridge = isBridgeNetworkDisabled(config)

// Setup the resolv.conf
setupResolvConf(config)

// Verify the platform is supported as a daemon
if !platformSupported {
return nil, errSystemNotSupported
Expand Down
34 changes: 34 additions & 0 deletions daemon/daemon_linux.go
Expand Up @@ -8,12 +8,19 @@ import (
"regexp"
"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/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
Expand Down Expand Up @@ -131,3 +138,30 @@ func shouldUnmountRoot(root string, info *mount.Info) bool {
}
return hasMountinfoOption(info.Optional, sharedPropagationOption)
}

// setupResolvConf sets the appropriate resolv.conf file if not specified
// When systemd-resolved is running the default /etc/resolv.conf points to
// localhost. In this case fetch the alternative config file that is in a
// different path so that containers can use it
// In all the other cases fallback to the default one
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)
}
}
4 changes: 4 additions & 0 deletions daemon/daemon_unsupported.go
@@ -1,5 +1,9 @@
// +build !linux,!freebsd,!windows

package daemon // import "github.com/docker/docker/daemon"
import "github.com/docker/docker/daemon/config"

const platformSupported = false

func setupResolvConf(config *config.Config) {
}
3 changes: 3 additions & 0 deletions daemon/daemon_windows.go
Expand Up @@ -653,3 +653,6 @@ func (daemon *Daemon) loadRuntimes() error {
func (daemon *Daemon) initRuntimes(_ map[string]types.Runtime) error {
return nil
}

func setupResolvConf(config *config.Config) {
}
2 changes: 1 addition & 1 deletion hack/dockerfile/install/proxy.installer
Expand Up @@ -3,7 +3,7 @@
# LIBNETWORK_COMMIT is used to build the docker-userland-proxy binary. When
# updating the binary version, consider updating github.com/docker/libnetwork
# in vendor.conf accordingly
LIBNETWORK_COMMIT=3ac297bc7fd0afec9051bbb47024c9bc1d75bf5b
LIBNETWORK_COMMIT=f30a35b091cc2a431ef9856c75c343f75bb5f2e2

install_proxy() {
case "$1" in
Expand Down
105 changes: 105 additions & 0 deletions internal/procfs/procfs_linux.go
@@ -0,0 +1,105 @@
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
}
36 changes: 36 additions & 0 deletions internal/procfs/procfs_linux_test.go
@@ -0,0 +1,36 @@
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()
}
}
2 changes: 1 addition & 1 deletion vendor.conf
Expand Up @@ -37,7 +37,7 @@ github.com/mitchellh/hashstructure 2bca23e0e452137f789efbc8610126fd8b94f73b
#get libnetwork packages

# When updating, also update LIBNETWORK_COMMIT in hack/dockerfile/install/proxy accordingly
github.com/docker/libnetwork d00ceed44cc447c77f25cdf5d59e83163bdcb4c9
github.com/docker/libnetwork f30a35b091cc2a431ef9856c75c343f75bb5f2e2
github.com/docker/go-events 9461782956ad83b30282bf90e31fa6a70c255ba9
github.com/armon/go-radix e39d623f12e8e41c7b5529e9a9dd67a1e2261f80
github.com/armon/go-metrics eb0af217e5e9747e41dd5303755356b62d28e3ec
Expand Down
11 changes: 11 additions & 0 deletions vendor/github.com/docker/libnetwork/README.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion vendor/github.com/docker/libnetwork/agent.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion vendor/github.com/docker/libnetwork/bitseq/sequence.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion vendor/github.com/docker/libnetwork/controller.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit e158451

Please sign in to comment.