Browse code

add resolvconf

Docker-DCO-1.1-Signed-off-by: Victor Vieux <vieux@docker.com> (github: vieux)

Victor Vieux authored on 2014/05/06 07:51:32
Showing 7 changed files
... ...
@@ -23,6 +23,7 @@ import (
23 23
 	"github.com/dotcloud/docker/nat"
24 24
 	"github.com/dotcloud/docker/pkg/label"
25 25
 	"github.com/dotcloud/docker/pkg/networkfs/etchosts"
26
+	"github.com/dotcloud/docker/pkg/networkfs/resolvconf"
26 27
 	"github.com/dotcloud/docker/runconfig"
27 28
 	"github.com/dotcloud/docker/utils"
28 29
 )
... ...
@@ -987,7 +988,7 @@ func (container *Container) setupContainerDns() error {
987 987
 		return nil
988 988
 	}
989 989
 
990
-	resolvConf, err := utils.GetResolvConf()
990
+	resolvConf, err := resolvconf.Get()
991 991
 	if err != nil {
992 992
 		return err
993 993
 	}
... ...
@@ -995,8 +996,8 @@ func (container *Container) setupContainerDns() error {
995 995
 	// If custom dns exists, then create a resolv.conf for the container
996 996
 	if len(config.Dns) > 0 || len(daemon.config.Dns) > 0 || len(config.DnsSearch) > 0 || len(daemon.config.DnsSearch) > 0 {
997 997
 		var (
998
-			dns       = utils.GetNameservers(resolvConf)
999
-			dnsSearch = utils.GetSearchDomains(resolvConf)
998
+			dns       = resolvconf.GetNameservers(resolvConf)
999
+			dnsSearch = resolvconf.GetSearchDomains(resolvConf)
1000 1000
 		)
1001 1001
 		if len(config.Dns) > 0 {
1002 1002
 			dns = config.Dns
... ...
@@ -1009,21 +1010,7 @@ func (container *Container) setupContainerDns() error {
1009 1009
 			dnsSearch = daemon.config.DnsSearch
1010 1010
 		}
1011 1011
 		container.ResolvConfPath = path.Join(container.root, "resolv.conf")
1012
-		f, err := os.Create(container.ResolvConfPath)
1013
-		if err != nil {
1014
-			return err
1015
-		}
1016
-		defer f.Close()
1017
-		for _, dns := range dns {
1018
-			if _, err := f.Write([]byte("nameserver " + dns + "\n")); err != nil {
1019
-				return err
1020
-			}
1021
-		}
1022
-		if len(dnsSearch) > 0 {
1023
-			if _, err := f.Write([]byte("search " + strings.Join(dnsSearch, " ") + "\n")); err != nil {
1024
-				return err
1025
-			}
1026
-		}
1012
+		return resolvconf.Build(container.ResolvConfPath, dns, dnsSearch)
1027 1013
 	} else {
1028 1014
 		container.ResolvConfPath = "/etc/resolv.conf"
1029 1015
 	}
... ...
@@ -29,6 +29,7 @@ import (
29 29
 	"github.com/dotcloud/docker/pkg/graphdb"
30 30
 	"github.com/dotcloud/docker/pkg/label"
31 31
 	"github.com/dotcloud/docker/pkg/mount"
32
+	"github.com/dotcloud/docker/pkg/networkfs/resolvconf"
32 33
 	"github.com/dotcloud/docker/pkg/selinux"
33 34
 	"github.com/dotcloud/docker/pkg/sysinfo"
34 35
 	"github.com/dotcloud/docker/runconfig"
... ...
@@ -981,7 +982,7 @@ func (daemon *Daemon) SetServer(server Server) {
981 981
 }
982 982
 
983 983
 func (daemon *Daemon) checkLocaldns() error {
984
-	resolvConf, err := utils.GetResolvConf()
984
+	resolvConf, err := resolvconf.Get()
985 985
 	if err != nil {
986 986
 		return err
987 987
 	}
... ...
@@ -2,6 +2,11 @@ package bridge
2 2
 
3 3
 import (
4 4
 	"fmt"
5
+	"io/ioutil"
6
+	"log"
7
+	"net"
8
+	"strings"
9
+
5 10
 	"github.com/dotcloud/docker/daemon/networkdriver"
6 11
 	"github.com/dotcloud/docker/daemon/networkdriver/ipallocator"
7 12
 	"github.com/dotcloud/docker/daemon/networkdriver/portallocator"
... ...
@@ -9,11 +14,8 @@ import (
9 9
 	"github.com/dotcloud/docker/engine"
10 10
 	"github.com/dotcloud/docker/pkg/iptables"
11 11
 	"github.com/dotcloud/docker/pkg/netlink"
12
+	"github.com/dotcloud/docker/pkg/networkfs/resolvconf"
12 13
 	"github.com/dotcloud/docker/utils"
13
-	"io/ioutil"
14
-	"log"
15
-	"net"
16
-	"strings"
17 14
 )
18 15
 
19 16
 const (
... ...
@@ -222,13 +224,13 @@ func setupIPTables(addr net.Addr, icc bool) error {
222 222
 // If it can't find an address which doesn't conflict, it will return an error.
223 223
 func createBridge(bridgeIP string) error {
224 224
 	nameservers := []string{}
225
-	resolvConf, _ := utils.GetResolvConf()
225
+	resolvConf, _ := resolvconf.Get()
226 226
 	// we don't check for an error here, because we don't really care
227 227
 	// if we can't read /etc/resolv.conf. So instead we skip the append
228 228
 	// if resolvConf is nil. It either doesn't exist, or we can't read it
229 229
 	// for some reason.
230 230
 	if resolvConf != nil {
231
-		nameservers = append(nameservers, utils.GetNameserversAsCIDR(resolvConf)...)
231
+		nameservers = append(nameservers, resolvconf.GetNameserversAsCIDR(resolvConf)...)
232 232
 	}
233 233
 
234 234
 	var ifaceAddr string
235 235
new file mode 100644
... ...
@@ -0,0 +1,87 @@
0
+package resolvconf
1
+
2
+import (
3
+	"bytes"
4
+	"io/ioutil"
5
+	"regexp"
6
+	"strings"
7
+)
8
+
9
+func Get() ([]byte, error) {
10
+	resolv, err := ioutil.ReadFile("/etc/resolv.conf")
11
+	if err != nil {
12
+		return nil, err
13
+	}
14
+	return resolv, nil
15
+}
16
+
17
+// getLines parses input into lines and strips away comments.
18
+func getLines(input []byte, commentMarker []byte) [][]byte {
19
+	lines := bytes.Split(input, []byte("\n"))
20
+	var output [][]byte
21
+	for _, currentLine := range lines {
22
+		var commentIndex = bytes.Index(currentLine, commentMarker)
23
+		if commentIndex == -1 {
24
+			output = append(output, currentLine)
25
+		} else {
26
+			output = append(output, currentLine[:commentIndex])
27
+		}
28
+	}
29
+	return output
30
+}
31
+
32
+// GetNameservers returns nameservers (if any) listed in /etc/resolv.conf
33
+func GetNameservers(resolvConf []byte) []string {
34
+	nameservers := []string{}
35
+	re := regexp.MustCompile(`^\s*nameserver\s*(([0-9]+\.){3}([0-9]+))\s*$`)
36
+	for _, line := range getLines(resolvConf, []byte("#")) {
37
+		var ns = re.FindSubmatch(line)
38
+		if len(ns) > 0 {
39
+			nameservers = append(nameservers, string(ns[1]))
40
+		}
41
+	}
42
+	return nameservers
43
+}
44
+
45
+// GetNameserversAsCIDR returns nameservers (if any) listed in
46
+// /etc/resolv.conf as CIDR blocks (e.g., "1.2.3.4/32")
47
+// This function's output is intended for net.ParseCIDR
48
+func GetNameserversAsCIDR(resolvConf []byte) []string {
49
+	nameservers := []string{}
50
+	for _, nameserver := range GetNameservers(resolvConf) {
51
+		nameservers = append(nameservers, nameserver+"/32")
52
+	}
53
+	return nameservers
54
+}
55
+
56
+// GetSearchDomains returns search domains (if any) listed in /etc/resolv.conf
57
+// If more than one search line is encountered, only the contents of the last
58
+// one is returned.
59
+func GetSearchDomains(resolvConf []byte) []string {
60
+	re := regexp.MustCompile(`^\s*search\s*(([^\s]+\s*)*)$`)
61
+	domains := []string{}
62
+	for _, line := range getLines(resolvConf, []byte("#")) {
63
+		match := re.FindSubmatch(line)
64
+		if match == nil {
65
+			continue
66
+		}
67
+		domains = strings.Fields(string(match[1]))
68
+	}
69
+	return domains
70
+}
71
+
72
+func Build(path string, dns, dnsSearch []string) error {
73
+	content := bytes.NewBuffer(nil)
74
+	for _, dns := range dns {
75
+		if _, err := content.WriteString("nameserver " + dns + "\n"); err != nil {
76
+			return err
77
+		}
78
+	}
79
+	if len(dnsSearch) > 0 {
80
+		if _, err := content.WriteString("search " + strings.Join(dnsSearch, " ") + "\n"); err != nil {
81
+			return err
82
+		}
83
+	}
84
+
85
+	return ioutil.WriteFile(path, content.Bytes(), 0644)
86
+}
0 87
new file mode 100644
... ...
@@ -0,0 +1,133 @@
0
+package resolvconf
1
+
2
+import (
3
+	"bytes"
4
+	"io/ioutil"
5
+	"os"
6
+	"testing"
7
+)
8
+
9
+func TestGet(t *testing.T) {
10
+	resolvConfUtils, err := Get()
11
+	if err != nil {
12
+		t.Fatal(err)
13
+	}
14
+	resolvConfSystem, err := ioutil.ReadFile("/etc/resolv.conf")
15
+	if err != nil {
16
+		t.Fatal(err)
17
+	}
18
+	if string(resolvConfUtils) != string(resolvConfSystem) {
19
+		t.Fatalf("/etc/resolv.conf and GetResolvConf have different content.")
20
+	}
21
+}
22
+
23
+func TestGetNameservers(t *testing.T) {
24
+	for resolv, result := range map[string][]string{`
25
+nameserver 1.2.3.4
26
+nameserver 40.3.200.10
27
+search example.com`: {"1.2.3.4", "40.3.200.10"},
28
+		`search example.com`: {},
29
+		`nameserver 1.2.3.4
30
+search example.com
31
+nameserver 4.30.20.100`: {"1.2.3.4", "4.30.20.100"},
32
+		``: {},
33
+		`  nameserver 1.2.3.4   `: {"1.2.3.4"},
34
+		`search example.com
35
+nameserver 1.2.3.4
36
+#nameserver 4.3.2.1`: {"1.2.3.4"},
37
+		`search example.com
38
+nameserver 1.2.3.4 # not 4.3.2.1`: {"1.2.3.4"},
39
+	} {
40
+		test := GetNameservers([]byte(resolv))
41
+		if !strSlicesEqual(test, result) {
42
+			t.Fatalf("Wrong nameserver string {%s} should be %v. Input: %s", test, result, resolv)
43
+		}
44
+	}
45
+}
46
+
47
+func TestGetNameserversAsCIDR(t *testing.T) {
48
+	for resolv, result := range map[string][]string{`
49
+nameserver 1.2.3.4
50
+nameserver 40.3.200.10
51
+search example.com`: {"1.2.3.4/32", "40.3.200.10/32"},
52
+		`search example.com`: {},
53
+		`nameserver 1.2.3.4
54
+search example.com
55
+nameserver 4.30.20.100`: {"1.2.3.4/32", "4.30.20.100/32"},
56
+		``: {},
57
+		`  nameserver 1.2.3.4   `: {"1.2.3.4/32"},
58
+		`search example.com
59
+nameserver 1.2.3.4
60
+#nameserver 4.3.2.1`: {"1.2.3.4/32"},
61
+		`search example.com
62
+nameserver 1.2.3.4 # not 4.3.2.1`: {"1.2.3.4/32"},
63
+	} {
64
+		test := GetNameserversAsCIDR([]byte(resolv))
65
+		if !strSlicesEqual(test, result) {
66
+			t.Fatalf("Wrong nameserver string {%s} should be %v. Input: %s", test, result, resolv)
67
+		}
68
+	}
69
+}
70
+
71
+func TestGetSearchDomains(t *testing.T) {
72
+	for resolv, result := range map[string][]string{
73
+		`search example.com`:           {"example.com"},
74
+		`search example.com # ignored`: {"example.com"},
75
+		` 	  search 	 example.com 	  `: {"example.com"},
76
+		` 	  search 	 example.com 	  # ignored`: {"example.com"},
77
+		`search foo.example.com example.com`: {"foo.example.com", "example.com"},
78
+		`	   search   	   foo.example.com 	 example.com 	`: {"foo.example.com", "example.com"},
79
+		`	   search   	   foo.example.com 	 example.com 	# ignored`: {"foo.example.com", "example.com"},
80
+		``:          {},
81
+		`# ignored`: {},
82
+		`nameserver 1.2.3.4
83
+search foo.example.com example.com`: {"foo.example.com", "example.com"},
84
+		`nameserver 1.2.3.4
85
+search dup1.example.com dup2.example.com
86
+search foo.example.com example.com`: {"foo.example.com", "example.com"},
87
+		`nameserver 1.2.3.4
88
+search foo.example.com example.com
89
+nameserver 4.30.20.100`: {"foo.example.com", "example.com"},
90
+	} {
91
+		test := GetSearchDomains([]byte(resolv))
92
+		if !strSlicesEqual(test, result) {
93
+			t.Fatalf("Wrong search domain string {%s} should be %v. Input: %s", test, result, resolv)
94
+		}
95
+	}
96
+}
97
+
98
+func strSlicesEqual(a, b []string) bool {
99
+	if len(a) != len(b) {
100
+		return false
101
+	}
102
+
103
+	for i, v := range a {
104
+		if v != b[i] {
105
+			return false
106
+		}
107
+	}
108
+
109
+	return true
110
+}
111
+
112
+func TestBuild(t *testing.T) {
113
+	file, err := ioutil.TempFile("", "")
114
+	if err != nil {
115
+		t.Fatal(err)
116
+	}
117
+	defer os.Remove(file.Name())
118
+
119
+	err = Build(file.Name(), []string{"ns1", "ns2", "ns3"}, []string{"search1"})
120
+	if err != nil {
121
+		t.Fatal(err)
122
+	}
123
+
124
+	content, err := ioutil.ReadFile(file.Name())
125
+	if err != nil {
126
+		t.Fatal(err)
127
+	}
128
+
129
+	if expected := "nameserver ns1\nnameserver ns2\nnameserver ns3\nsearch search1\n"; !bytes.Contains(content, []byte(expected)) {
130
+		t.Fatalf("Expected to find '%s' got '%s'", expected, content)
131
+	}
132
+}
... ...
@@ -9,7 +9,6 @@ import (
9 9
 	"encoding/json"
10 10
 	"errors"
11 11
 	"fmt"
12
-	"github.com/dotcloud/docker/dockerversion"
13 12
 	"index/suffixarray"
14 13
 	"io"
15 14
 	"io/ioutil"
... ...
@@ -23,6 +22,8 @@ import (
23 23
 	"strings"
24 24
 	"sync"
25 25
 	"time"
26
+
27
+	"github.com/dotcloud/docker/dockerversion"
26 28
 )
27 29
 
28 30
 type KeyValuePair struct {
... ...
@@ -779,17 +780,6 @@ func IsGIT(str string) bool {
779 779
 	return strings.HasPrefix(str, "git://") || strings.HasPrefix(str, "github.com/") || strings.HasPrefix(str, "git@github.com:") || (strings.HasSuffix(str, ".git") && IsURL(str))
780 780
 }
781 781
 
782
-// GetResolvConf opens and read the content of /etc/resolv.conf.
783
-// It returns it as byte slice.
784
-func GetResolvConf() ([]byte, error) {
785
-	resolv, err := ioutil.ReadFile("/etc/resolv.conf")
786
-	if err != nil {
787
-		Errorf("Error openning resolv.conf: %s", err)
788
-		return nil, err
789
-	}
790
-	return resolv, nil
791
-}
792
-
793 782
 // CheckLocalDns looks into the /etc/resolv.conf,
794 783
 // it returns true if there is a local nameserver or if there is no nameserver.
795 784
 func CheckLocalDns(resolvConf []byte) bool {
... ...
@@ -825,46 +815,6 @@ func GetLines(input []byte, commentMarker []byte) [][]byte {
825 825
 	return output
826 826
 }
827 827
 
828
-// GetNameservers returns nameservers (if any) listed in /etc/resolv.conf
829
-func GetNameservers(resolvConf []byte) []string {
830
-	nameservers := []string{}
831
-	re := regexp.MustCompile(`^\s*nameserver\s*(([0-9]+\.){3}([0-9]+))\s*$`)
832
-	for _, line := range GetLines(resolvConf, []byte("#")) {
833
-		var ns = re.FindSubmatch(line)
834
-		if len(ns) > 0 {
835
-			nameservers = append(nameservers, string(ns[1]))
836
-		}
837
-	}
838
-	return nameservers
839
-}
840
-
841
-// GetNameserversAsCIDR returns nameservers (if any) listed in
842
-// /etc/resolv.conf as CIDR blocks (e.g., "1.2.3.4/32")
843
-// This function's output is intended for net.ParseCIDR
844
-func GetNameserversAsCIDR(resolvConf []byte) []string {
845
-	nameservers := []string{}
846
-	for _, nameserver := range GetNameservers(resolvConf) {
847
-		nameservers = append(nameservers, nameserver+"/32")
848
-	}
849
-	return nameservers
850
-}
851
-
852
-// GetSearchDomains returns search domains (if any) listed in /etc/resolv.conf
853
-// If more than one search line is encountered, only the contents of the last
854
-// one is returned.
855
-func GetSearchDomains(resolvConf []byte) []string {
856
-	re := regexp.MustCompile(`^\s*search\s*(([^\s]+\s*)*)$`)
857
-	domains := []string{}
858
-	for _, line := range GetLines(resolvConf, []byte("#")) {
859
-		match := re.FindSubmatch(line)
860
-		if match == nil {
861
-			continue
862
-		}
863
-		domains = strings.Fields(string(match[1]))
864
-	}
865
-	return domains
866
-}
867
-
868 828
 // FIXME: Change this not to receive default value as parameter
869 829
 func ParseHost(defaultHost string, defaultUnix, addr string) (string, error) {
870 830
 	var (
... ...
@@ -377,20 +377,6 @@ func TestParseRepositoryTag(t *testing.T) {
377 377
 	}
378 378
 }
379 379
 
380
-func TestGetResolvConf(t *testing.T) {
381
-	resolvConfUtils, err := GetResolvConf()
382
-	if err != nil {
383
-		t.Fatal(err)
384
-	}
385
-	resolvConfSystem, err := ioutil.ReadFile("/etc/resolv.conf")
386
-	if err != nil {
387
-		t.Fatal(err)
388
-	}
389
-	if string(resolvConfUtils) != string(resolvConfSystem) {
390
-		t.Fatalf("/etc/resolv.conf and GetResolvConf have different content.")
391
-	}
392
-}
393
-
394 380
 func TestCheckLocalDns(t *testing.T) {
395 381
 	for resolv, result := range map[string]bool{`# Dynamic
396 382
 nameserver 10.0.2.3
... ...
@@ -464,95 +450,6 @@ func TestParsePortMapping(t *testing.T) {
464 464
 	}
465 465
 }
466 466
 
467
-func TestGetNameservers(t *testing.T) {
468
-	for resolv, result := range map[string][]string{`
469
-nameserver 1.2.3.4
470
-nameserver 40.3.200.10
471
-search example.com`: {"1.2.3.4", "40.3.200.10"},
472
-		`search example.com`: {},
473
-		`nameserver 1.2.3.4
474
-search example.com
475
-nameserver 4.30.20.100`: {"1.2.3.4", "4.30.20.100"},
476
-		``: {},
477
-		`  nameserver 1.2.3.4   `: {"1.2.3.4"},
478
-		`search example.com
479
-nameserver 1.2.3.4
480
-#nameserver 4.3.2.1`: {"1.2.3.4"},
481
-		`search example.com
482
-nameserver 1.2.3.4 # not 4.3.2.1`: {"1.2.3.4"},
483
-	} {
484
-		test := GetNameservers([]byte(resolv))
485
-		if !StrSlicesEqual(test, result) {
486
-			t.Fatalf("Wrong nameserver string {%s} should be %v. Input: %s", test, result, resolv)
487
-		}
488
-	}
489
-}
490
-
491
-func TestGetNameserversAsCIDR(t *testing.T) {
492
-	for resolv, result := range map[string][]string{`
493
-nameserver 1.2.3.4
494
-nameserver 40.3.200.10
495
-search example.com`: {"1.2.3.4/32", "40.3.200.10/32"},
496
-		`search example.com`: {},
497
-		`nameserver 1.2.3.4
498
-search example.com
499
-nameserver 4.30.20.100`: {"1.2.3.4/32", "4.30.20.100/32"},
500
-		``: {},
501
-		`  nameserver 1.2.3.4   `: {"1.2.3.4/32"},
502
-		`search example.com
503
-nameserver 1.2.3.4
504
-#nameserver 4.3.2.1`: {"1.2.3.4/32"},
505
-		`search example.com
506
-nameserver 1.2.3.4 # not 4.3.2.1`: {"1.2.3.4/32"},
507
-	} {
508
-		test := GetNameserversAsCIDR([]byte(resolv))
509
-		if !StrSlicesEqual(test, result) {
510
-			t.Fatalf("Wrong nameserver string {%s} should be %v. Input: %s", test, result, resolv)
511
-		}
512
-	}
513
-}
514
-
515
-func TestGetSearchDomains(t *testing.T) {
516
-	for resolv, result := range map[string][]string{
517
-		`search example.com`:           {"example.com"},
518
-		`search example.com # ignored`: {"example.com"},
519
-		` 	  search 	 example.com 	  `: {"example.com"},
520
-		` 	  search 	 example.com 	  # ignored`: {"example.com"},
521
-		`search foo.example.com example.com`: {"foo.example.com", "example.com"},
522
-		`	   search   	   foo.example.com 	 example.com 	`: {"foo.example.com", "example.com"},
523
-		`	   search   	   foo.example.com 	 example.com 	# ignored`: {"foo.example.com", "example.com"},
524
-		``:          {},
525
-		`# ignored`: {},
526
-		`nameserver 1.2.3.4
527
-search foo.example.com example.com`: {"foo.example.com", "example.com"},
528
-		`nameserver 1.2.3.4
529
-search dup1.example.com dup2.example.com
530
-search foo.example.com example.com`: {"foo.example.com", "example.com"},
531
-		`nameserver 1.2.3.4
532
-search foo.example.com example.com
533
-nameserver 4.30.20.100`: {"foo.example.com", "example.com"},
534
-	} {
535
-		test := GetSearchDomains([]byte(resolv))
536
-		if !StrSlicesEqual(test, result) {
537
-			t.Fatalf("Wrong search domain string {%s} should be %v. Input: %s", test, result, resolv)
538
-		}
539
-	}
540
-}
541
-
542
-func StrSlicesEqual(a, b []string) bool {
543
-	if len(a) != len(b) {
544
-		return false
545
-	}
546
-
547
-	for i, v := range a {
548
-		if v != b[i] {
549
-			return false
550
-		}
551
-	}
552
-
553
-	return true
554
-}
555
-
556 467
 func TestReplaceAndAppendEnvVars(t *testing.T) {
557 468
 	var (
558 469
 		d = []string{"HOME=/"}