Browse code

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

Signed-off-by: Tibor Vass <tibor@docker.com>

Tibor Vass authored on 2019/06/01 06:09:22
Showing 8 changed files
... ...
@@ -9,18 +9,13 @@ import (
9 9
 	"strings"
10 10
 
11 11
 	"github.com/docker/docker/daemon/config"
12
-	"github.com/docker/docker/internal/procfs"
13 12
 	"github.com/docker/docker/pkg/fileutils"
14 13
 	"github.com/docker/docker/pkg/mount"
14
+	"github.com/docker/libnetwork/resolvconf"
15 15
 	"github.com/pkg/errors"
16 16
 	"github.com/sirupsen/logrus"
17 17
 )
18 18
 
19
-const (
20
-	defaultResolvConf   = "/etc/resolv.conf"
21
-	alternateResolvConf = "/run/systemd/resolve/resolv.conf"
22
-)
23
-
24 19
 // On Linux, plugins use a static path for storing execution state,
25 20
 // instead of deriving path from daemon's exec-root. This is because
26 21
 // plugin socket files are created here and they cannot exceed max
... ...
@@ -148,20 +143,5 @@ func setupResolvConf(config *config.Config) {
148 148
 	if config.ResolvConf != "" {
149 149
 		return
150 150
 	}
151
-
152
-	config.ResolvConf = defaultResolvConf
153
-	pids, err := procfs.PidOf("systemd-resolved")
154
-	if err != nil {
155
-		logrus.Errorf("unable to check systemd-resolved status: %s", err)
156
-		return
157
-	}
158
-	if len(pids) > 0 && pids[0] > 0 {
159
-		_, err := os.Stat(alternateResolvConf)
160
-		if err == nil {
161
-			logrus.Infof("systemd-resolved is running, so using resolvconf: %s", alternateResolvConf)
162
-			config.ResolvConf = alternateResolvConf
163
-			return
164
-		}
165
-		logrus.Infof("systemd-resolved is running, but %s is not present, fallback to %s", alternateResolvConf, defaultResolvConf)
166
-	}
151
+	config.ResolvConf = resolvconf.Path()
167 152
 }
168 153
deleted file mode 100644
... ...
@@ -1,105 +0,0 @@
1
-package procfs
2
-
3
-/*
4
-Copyright 2015 The Kubernetes Authors.
5
-
6
-Licensed under the Apache License, Version 2.0 (the "License");
7
-you may not use this file except in compliance with the License.
8
-You may obtain a copy of the License at
9
-
10
-http://www.apache.org/licenses/LICENSE-2.0
11
-
12
-Unless required by applicable law or agreed to in writing, software
13
-distributed under the License is distributed on an "AS IS" BASIS,
14
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
-See the License for the specific language governing permissions and
16
-limitations under the License.
17
-*/
18
-
19
-import (
20
-	"bytes"
21
-	"fmt"
22
-	"io"
23
-	"io/ioutil"
24
-	"os"
25
-	"path/filepath"
26
-	"regexp"
27
-	"strconv"
28
-	"strings"
29
-	"unicode"
30
-
31
-	"github.com/sirupsen/logrus"
32
-)
33
-
34
-// PidOf finds process(es) with a specified name (regexp match)
35
-// and return their pid(s)
36
-func PidOf(name string) ([]int, error) {
37
-	if len(name) == 0 {
38
-		return []int{}, fmt.Errorf("name should not be empty")
39
-	}
40
-	re, err := regexp.Compile("(^|/)" + name + "$")
41
-	if err != nil {
42
-		return []int{}, err
43
-	}
44
-	return getPids(re), nil
45
-}
46
-
47
-func getPids(re *regexp.Regexp) []int {
48
-	pids := []int{}
49
-
50
-	dirFD, err := os.Open("/proc")
51
-	if err != nil {
52
-		return nil
53
-	}
54
-	defer dirFD.Close()
55
-
56
-	for {
57
-		// Read a small number at a time in case there are many entries, we don't want to
58
-		// allocate a lot here.
59
-		ls, err := dirFD.Readdir(10)
60
-		if err == io.EOF {
61
-			break
62
-		}
63
-		if err != nil {
64
-			return nil
65
-		}
66
-
67
-		for _, entry := range ls {
68
-			if !entry.IsDir() {
69
-				continue
70
-			}
71
-
72
-			// If the directory is not a number (i.e. not a PID), skip it
73
-			pid, err := strconv.Atoi(entry.Name())
74
-			if err != nil {
75
-				continue
76
-			}
77
-
78
-			cmdline, err := ioutil.ReadFile(filepath.Join("/proc", entry.Name(), "cmdline"))
79
-			if err != nil {
80
-				logrus.Infof("Error reading file %s: %+v", filepath.Join("/proc", entry.Name(), "cmdline"), err)
81
-				continue
82
-			}
83
-
84
-			// The bytes we read have '\0' as a separator for the command line
85
-			parts := bytes.SplitN(cmdline, []byte{0}, 2)
86
-			if len(parts) == 0 {
87
-				continue
88
-			}
89
-			// Split the command line itself we are interested in just the first part
90
-			exe := strings.FieldsFunc(string(parts[0]), func(c rune) bool {
91
-				return unicode.IsSpace(c) || c == ':'
92
-			})
93
-			if len(exe) == 0 {
94
-				continue
95
-			}
96
-			// Check if the name of the executable is what we are looking for
97
-			if re.MatchString(exe[0]) {
98
-				// Grab the PID from the directory path
99
-				pids = append(pids, pid)
100
-			}
101
-		}
102
-	}
103
-
104
-	return pids
105
-}
106 1
deleted file mode 100644
... ...
@@ -1,36 +0,0 @@
1
-package procfs
2
-
3
-import (
4
-	"os"
5
-	"path/filepath"
6
-	"regexp"
7
-	"runtime"
8
-	"testing"
9
-
10
-	"gotest.tools/assert"
11
-)
12
-
13
-func TestPidOf(t *testing.T) {
14
-	pids, err := PidOf(filepath.Base(os.Args[0]))
15
-	assert.NilError(t, err)
16
-	assert.Check(t, len(pids) == 1)
17
-	assert.DeepEqual(t, pids[0], os.Getpid())
18
-}
19
-
20
-func BenchmarkGetPids(b *testing.B) {
21
-	if runtime.GOOS == "darwin" || runtime.GOOS == "windows" {
22
-		b.Skipf("not supported on GOOS=%s", runtime.GOOS)
23
-	}
24
-
25
-	re, err := regexp.Compile("(^|/)" + filepath.Base(os.Args[0]) + "$")
26
-	assert.Check(b, err == nil)
27
-
28
-	for i := 0; i < b.N; i++ {
29
-		pids := getPids(re)
30
-
31
-		b.StopTimer()
32
-		assert.Check(b, len(pids) > 0)
33
-		assert.Check(b, pids[0] == os.Getpid())
34
-		b.StartTimer()
35
-	}
36
-}
... ...
@@ -27,7 +27,7 @@ github.com/imdario/mergo                            7c29201646fa3de8506f70121347
27 27
 golang.org/x/sync                                   e225da77a7e68af35c70ccbf71af2b83e6acac3c
28 28
 
29 29
 # buildkit
30
-github.com/moby/buildkit                            37d53758a68d9f5cede1806dbb2da7c3caa8d5bc
30
+github.com/moby/buildkit                            1f89ec125f84c097bdf3a063be622c4238dba5f8
31 31
 github.com/tonistiigi/fsutil                        3bbb99cdbd76619ab717299830c60f6f2a533a6b
32 32
 github.com/grpc-ecosystem/grpc-opentracing          8e809c8a86450a29b90dcc9efbf062d0fe6d9746
33 33
 github.com/opentracing/opentracing-go               1361b9cd60be79c4c3a7fa9841b3c132e40066a7
... ...
@@ -39,7 +39,7 @@ github.com/gofrs/flock                              7f43ea2e6a643ad441fc12d0ecc0
39 39
 # libnetwork
40 40
 
41 41
 # When updating, also update LIBNETWORK_COMMIT in hack/dockerfile/install/proxy.installer accordingly
42
-github.com/docker/libnetwork                        5ac07abef4eee176423fdc1b870d435258e2d381
42
+github.com/docker/libnetwork                        fc5a7d91d54cc98f64fc28f9e288b46a0bee756c
43 43
 github.com/docker/go-events                         9461782956ad83b30282bf90e31fa6a70c255ba9
44 44
 github.com/armon/go-radix                           e39d623f12e8e41c7b5529e9a9dd67a1e2261f80
45 45
 github.com/armon/go-metrics                         eb0af217e5e9747e41dd5303755356b62d28e3ec
... ...
@@ -339,7 +339,6 @@ func (c *controller) clusterAgentInit() {
339 339
 				}
340 340
 			}
341 341
 		case cluster.EventNodeLeave:
342
-			keysAvailable = false
343 342
 			c.agentOperationStart()
344 343
 			c.Lock()
345 344
 			c.keys = nil
... ...
@@ -706,11 +705,17 @@ const overlayDSROptionString = "dsr"
706 706
 // NewNetwork creates a new network of the specified network type. The options
707 707
 // are network specific and modeled in a generic way.
708 708
 func (c *controller) NewNetwork(networkType, name string, id string, options ...NetworkOption) (Network, error) {
709
+	var (
710
+		cap *driverapi.Capability
711
+		err error
712
+		t   *network
713
+	)
714
+
709 715
 	if id != "" {
710 716
 		c.networkLocker.Lock(id)
711 717
 		defer c.networkLocker.Unlock(id)
712 718
 
713
-		if _, err := c.NetworkByID(id); err == nil {
719
+		if _, err = c.NetworkByID(id); err == nil {
714 720
 			return nil, NetworkNameError(id)
715 721
 		}
716 722
 	}
... ...
@@ -739,15 +744,10 @@ func (c *controller) NewNetwork(networkType, name string, id string, options ...
739 739
 	}
740 740
 
741 741
 	network.processOptions(options...)
742
-	if err := network.validateConfiguration(); err != nil {
742
+	if err = network.validateConfiguration(); err != nil {
743 743
 		return nil, err
744 744
 	}
745 745
 
746
-	var (
747
-		cap *driverapi.Capability
748
-		err error
749
-	)
750
-
751 746
 	// Reset network types, force local scope and skip allocation and
752 747
 	// plumbing for configuration networks. Reset of the config-only
753 748
 	// 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 ...
794 794
 	// From this point on, we need the network specific configuration,
795 795
 	// which may come from a configuration-only network
796 796
 	if network.configFrom != "" {
797
-		t, err := c.getConfigNetwork(network.configFrom)
797
+		t, err = c.getConfigNetwork(network.configFrom)
798 798
 		if err != nil {
799 799
 			return nil, types.NotFoundErrorf("configuration network %q does not exist", network.configFrom)
800 800
 		}
801
-		if err := t.applyConfigurationTo(network); err != nil {
801
+		if err = t.applyConfigurationTo(network); err != nil {
802 802
 			return nil, types.InternalErrorf("Failed to apply configuration: %v", err)
803 803
 		}
804 804
 		defer func() {
... ...
@@ -15,11 +15,45 @@ import (
15 15
 )
16 16
 
17 17
 const (
18
-	// DefaultResolvConf points to the default file used for dns configuration on a linux machine
19
-	DefaultResolvConf = "/etc/resolv.conf"
18
+	// defaultPath is the default path to the resolv.conf that contains information to resolve DNS. See Path().
19
+	defaultPath = "/etc/resolv.conf"
20
+	// alternatePath is a path different from defaultPath, that may be used to resolve DNS. See Path().
21
+	alternatePath = "/run/systemd/resolve/resolv.conf"
20 22
 )
21 23
 
22 24
 var (
25
+	detectSystemdResolvConfOnce sync.Once
26
+	pathAfterSystemdDetection   = defaultPath
27
+)
28
+
29
+// Path returns the path to the resolv.conf file that libnetwork should use.
30
+//
31
+// When /etc/resolv.conf contains 127.0.0.53 as the only nameserver, then
32
+// it is assumed systemd-resolved manages DNS. Because inside the container 127.0.0.53
33
+// is not a valid DNS server, Path() returns /run/systemd/resolve/resolv.conf
34
+// which is the resolv.conf that systemd-resolved generates and manages.
35
+// Otherwise Path() returns /etc/resolv.conf.
36
+//
37
+// Errors are silenced as they will inevitably resurface at future open/read calls.
38
+//
39
+// More information at https://www.freedesktop.org/software/systemd/man/systemd-resolved.service.html#/etc/resolv.conf
40
+func Path() string {
41
+	detectSystemdResolvConfOnce.Do(func() {
42
+		candidateResolvConf, err := ioutil.ReadFile(defaultPath)
43
+		if err != nil {
44
+			// silencing error as it will resurface at next calls trying to read defaultPath
45
+			return
46
+		}
47
+		ns := GetNameservers(candidateResolvConf, types.IP)
48
+		if len(ns) == 1 && ns[0] == "127.0.0.53" {
49
+			pathAfterSystemdDetection = alternatePath
50
+			logrus.Infof("detected 127.0.0.53 nameserver, assuming systemd-resolved, so using resolv.conf: %s", alternatePath)
51
+		}
52
+	})
53
+	return pathAfterSystemdDetection
54
+}
55
+
56
+var (
23 57
 	// Note: the default IPv4 & IPv6 resolvers are set to Google's Public DNS
24 58
 	defaultIPv4Dns = []string{"nameserver 8.8.8.8", "nameserver 8.8.4.4"}
25 59
 	defaultIPv6Dns = []string{"nameserver 2001:4860:4860::8888", "nameserver 2001:4860:4860::8844"}
... ...
@@ -55,7 +89,7 @@ type File struct {
55 55
 
56 56
 // Get returns the contents of /etc/resolv.conf and its hash
57 57
 func Get() (*File, error) {
58
-	return GetSpecific(DefaultResolvConf)
58
+	return GetSpecific(Path())
59 59
 }
60 60
 
61 61
 // GetSpecific returns the contents of the user specified resolv.conf file and its hash
... ...
@@ -78,7 +112,7 @@ func GetIfChanged() (*File, error) {
78 78
 	lastModified.Lock()
79 79
 	defer lastModified.Unlock()
80 80
 
81
-	resolv, err := ioutil.ReadFile("/etc/resolv.conf")
81
+	resolv, err := ioutil.ReadFile(Path())
82 82
 	if err != nil {
83 83
 		return nil, err
84 84
 	}
... ...
@@ -213,8 +213,8 @@ func (sb *sandbox) setupDNS() error {
213 213
 
214 214
 	originResolvConfPath := sb.config.originResolvConfPath
215 215
 	if originResolvConfPath == "" {
216
-		// if not specified fallback to default /etc/resolv.conf
217
-		originResolvConfPath = resolvconf.DefaultResolvConf
216
+		// fallback if not specified
217
+		originResolvConfPath = resolvconf.Path()
218 218
 	}
219 219
 	currRC, err := resolvconf.GetSpecific(originResolvConfPath)
220 220
 	if err != nil {
... ...
@@ -29,7 +29,7 @@ func GetResolvConf(ctx context.Context, stateDir string) (string, error) {
29 29
 				generate = true
30 30
 			}
31 31
 			if !generate {
32
-				fiMain, err := os.Stat("/etc/resolv.conf")
32
+				fiMain, err := os.Stat(resolvconf.Path())
33 33
 				if err != nil {
34 34
 					if !os.IsNotExist(err) {
35 35
 						return nil, err