diff --git a/daemon/daemon_linux.go b/daemon/daemon_linux.go index 7af41d2603..5adbbad299 100644 --- a/daemon/daemon_linux.go +++ b/daemon/daemon_linux.go @@ -146,6 +146,7 @@ func setupResolvConf(config *config.Config) { if config.ResolvConf != "" { return } + // FIXME(thaJeztah): we can't use [github.com/moby/moby/v2/daemon/libnetwork/internal/resolvconf.Path] here, because it's internal to libnetwork. config.ResolvConf = resolvconf.Path() } diff --git a/daemon/libnetwork/netutils/utils_linux.go b/daemon/libnetwork/netutils/utils_linux.go index 06607fdd14..828971c167 100644 --- a/daemon/libnetwork/netutils/utils_linux.go +++ b/daemon/libnetwork/netutils/utils_linux.go @@ -3,14 +3,15 @@ package netutils import ( + "bytes" "net/netip" "os" "slices" "github.com/moby/moby/v2/daemon/libnetwork/internal/netiputil" + "github.com/moby/moby/v2/daemon/libnetwork/internal/resolvconf" "github.com/moby/moby/v2/daemon/libnetwork/nlwrap" "github.com/moby/moby/v2/daemon/libnetwork/ns" - "github.com/moby/moby/v2/daemon/libnetwork/resolvconf" "github.com/moby/moby/v2/daemon/libnetwork/types" "github.com/pkg/errors" "github.com/vishvananda/netlink" @@ -62,7 +63,7 @@ func InferReservedNetworks(v6 bool) []netip.Prefix { // We don't really care if os.ReadFile fails here. It either doesn't exist, // or we can't read it for some reason. if rc, err := os.ReadFile(resolvconf.Path()); err == nil { - reserved = slices.DeleteFunc(resolvconf.GetNameserversAsPrefix(rc), func(p netip.Prefix) bool { + reserved = slices.DeleteFunc(tryGetNameserversAsPrefix(rc), func(p netip.Prefix) bool { return p.Addr().Is6() != v6 }) } @@ -75,6 +76,22 @@ func InferReservedNetworks(v6 bool) []netip.Prefix { return reserved } +// tryGetNameserversAsPrefix returns nameservers (if any) listed in +// /etc/resolv.conf as CIDR blocks (e.g., "1.2.3.4/32"). It ignores +// failures to parse the file, as this utility is used as a "best-effort". +func tryGetNameserversAsPrefix(resolvConf []byte) []netip.Prefix { + rc, err := resolvconf.Parse(bytes.NewBuffer(resolvConf), "") + if err != nil { + return nil + } + nsAddrs := rc.NameServers() + nameservers := make([]netip.Prefix, 0, len(nsAddrs)) + for _, addr := range nsAddrs { + nameservers = append(nameservers, netip.PrefixFrom(addr, addr.BitLen())) + } + return nameservers +} + // queryOnLinkRoutes returns a list of on-link routes available on the host. // Only IPv4 prefixes are returned as there's no such thing as on-link // routes for IPv6. diff --git a/daemon/libnetwork/netutils/utils_linux_test.go b/daemon/libnetwork/netutils/utils_linux_test.go index 9e8b7bc117..9e4a848e43 100644 --- a/daemon/libnetwork/netutils/utils_linux_test.go +++ b/daemon/libnetwork/netutils/utils_linux_test.go @@ -8,6 +8,7 @@ import ( "strings" "testing" + "github.com/google/go-cmp/cmp/cmpopts" "github.com/moby/moby/v2/daemon/libnetwork/internal/netiputil" "github.com/moby/moby/v2/internal/testutils/netnsutils" "github.com/vishvananda/netlink" @@ -60,6 +61,61 @@ func TestGenerateRandomName(t *testing.T) { } } +func TestGetNameserversAsPrefix(t *testing.T) { + for _, tc := range []struct { + input string + result []netip.Prefix + }{ + { + input: ``, + result: []netip.Prefix{}, + }, + { + input: `search example.com`, + result: []netip.Prefix{}, + }, + { + input: ` nameserver 1.2.3.4 `, + result: []netip.Prefix{netip.MustParsePrefix("1.2.3.4/32")}, + }, + { + input: ` +nameserver 1.2.3.4 +nameserver 40.3.200.10 +search example.com`, + result: []netip.Prefix{netip.MustParsePrefix("1.2.3.4/32"), netip.MustParsePrefix("40.3.200.10/32")}, + }, + { + input: `nameserver 1.2.3.4 +search example.com +nameserver 4.30.20.100`, + result: []netip.Prefix{netip.MustParsePrefix("1.2.3.4/32"), netip.MustParsePrefix("4.30.20.100/32")}, + }, + { + input: `search example.com +nameserver 1.2.3.4 +#nameserver 4.3.2.1`, + result: []netip.Prefix{netip.MustParsePrefix("1.2.3.4/32")}, + }, + { + input: `search example.com +nameserver 1.2.3.4 # not 4.3.2.1`, + result: []netip.Prefix{netip.MustParsePrefix("1.2.3.4/32")}, + }, + { + input: `nameserver fd6f:c490:ec68::1`, + result: []netip.Prefix{netip.MustParsePrefix("fd6f:c490:ec68::1/128")}, + }, + { + input: `nameserver fe80::1234%eth0`, + result: []netip.Prefix{netip.MustParsePrefix("fe80::1234/128")}, + }, + } { + test := tryGetNameserversAsPrefix([]byte(tc.input)) + assert.DeepEqual(t, test, tc.result, cmpopts.EquateComparable(netip.Prefix{})) + } +} + // Test mac generation. func TestUtilGenerateRandomMAC(t *testing.T) { mac1 := GenerateRandomMAC() diff --git a/daemon/libnetwork/resolvconf/resolvconf.go b/daemon/libnetwork/resolvconf/resolvconf.go index c84ec526a8..56cadca533 100644 --- a/daemon/libnetwork/resolvconf/resolvconf.go +++ b/daemon/libnetwork/resolvconf/resolvconf.go @@ -1,159 +1,14 @@ -// Package resolvconf provides utility code to query and update DNS configuration in /etc/resolv.conf +// Package resolvconf provides utility code to get the host's "resolv.conf" path. package resolvconf -import ( - "bytes" - "fmt" - "net/netip" - "os" - - "github.com/moby/moby/v2/daemon/libnetwork/internal/resolvconf" - "github.com/opencontainers/go-digest" -) - -// constants for the IP address type -const ( - IP = iota // IPv4 and IPv6 - IPv4 - IPv6 -) - -// File contains the resolv.conf content and its hash -type File struct { - Content []byte - Hash []byte -} +import "github.com/moby/moby/v2/daemon/libnetwork/internal/resolvconf" +// Path is an alias for [resolvconf.Path], which is internal to libnetwork. +// +// FIXME(thaJeztah): remove this when possible. This is only used in [github.com/moby/moby/v2/daemon.setupResolvConf]. +// Either we can move "libnetwork/internal/resolvconf" to "daemon/internal", +// or move the "Path" function to daemon/config (considering it a default +// for the daemon's config). func Path() string { return resolvconf.Path() } - -// Get returns the contents of /etc/resolv.conf and its hash -func Get() (*File, error) { - return GetSpecific(Path()) -} - -// GetSpecific returns the contents of the user specified resolv.conf file and its hash -func GetSpecific(path string) (*File, error) { - resolv, err := os.ReadFile(path) - if err != nil { - return nil, err - } - hash := digest.FromBytes(resolv) - return &File{Content: resolv, Hash: []byte(hash)}, nil -} - -// FilterResolvDNS cleans up the config in resolvConf. It has two main jobs: -// 1. It looks for localhost (127.*|::1) entries in the provided -// resolv.conf, removing local nameserver entries, and, if the resulting -// cleaned config has no defined nameservers left, adds default DNS entries -// 2. Given the caller provides the enable/disable state of IPv6, the filter -// code will remove all IPv6 nameservers if it is not enabled for containers -func FilterResolvDNS(resolvConf []byte, ipv6Enabled bool) (*File, error) { - rc, err := resolvconf.Parse(bytes.NewBuffer(resolvConf), "") - if err != nil { - return nil, err - } - rc.TransformForLegacyNw(ipv6Enabled) - content, err := rc.Generate(false) - if err != nil { - return nil, err - } - hash := digest.FromBytes(content) - return &File{Content: content, Hash: []byte(hash)}, nil -} - -// GetNameservers returns nameservers (if any) listed in /etc/resolv.conf -func GetNameservers(resolvConf []byte, kind int) []string { - rc, err := resolvconf.Parse(bytes.NewBuffer(resolvConf), "") - if err != nil { - return nil - } - nsAddrs := rc.NameServers() - var nameservers []string - for _, addr := range nsAddrs { - if kind == IP { - nameservers = append(nameservers, addr.String()) - } else if kind == IPv4 && addr.Is4() { - nameservers = append(nameservers, addr.String()) - } else if kind == IPv6 && addr.Is6() { - nameservers = append(nameservers, addr.String()) - } - } - return nameservers -} - -// GetNameserversAsPrefix returns nameservers (if any) listed in -// /etc/resolv.conf as CIDR blocks (e.g., "1.2.3.4/32") -func GetNameserversAsPrefix(resolvConf []byte) []netip.Prefix { - rc, err := resolvconf.Parse(bytes.NewBuffer(resolvConf), "") - if err != nil { - return nil - } - nsAddrs := rc.NameServers() - nameservers := make([]netip.Prefix, 0, len(nsAddrs)) - for _, addr := range nsAddrs { - nameservers = append(nameservers, netip.PrefixFrom(addr, addr.BitLen())) - } - return nameservers -} - -// GetSearchDomains returns search domains (if any) listed in /etc/resolv.conf -// If more than one search line is encountered, only the contents of the last -// one is returned. -func GetSearchDomains(resolvConf []byte) []string { - rc, err := resolvconf.Parse(bytes.NewBuffer(resolvConf), "") - if err != nil { - return nil - } - return rc.Search() -} - -// GetOptions returns options (if any) listed in /etc/resolv.conf -// If more than one options line is encountered, only the contents of the last -// one is returned. -func GetOptions(resolvConf []byte) []string { - rc, err := resolvconf.Parse(bytes.NewBuffer(resolvConf), "") - if err != nil { - return nil - } - return rc.Options() -} - -// Build generates and writes a configuration file to path containing a nameserver -// entry for every element in nameservers, a "search" entry for every element in -// dnsSearch, and an "options" entry for every element in dnsOptions. It returns -// a File containing the generated content and its (sha256) hash. -// -// Note that the resolv.conf file is written, but the hash file is not. -func Build(path string, nameservers, dnsSearch, dnsOptions []string) (*File, error) { - var ns []netip.Addr - for _, addr := range nameservers { - ipAddr, err := netip.ParseAddr(addr) - if err != nil { - return nil, fmt.Errorf("bad nameserver address: %w", err) - } - ns = append(ns, ipAddr) - } - rc := resolvconf.ResolvConf{} - rc.OverrideNameServers(ns) - rc.OverrideSearch(dnsSearch) - rc.OverrideOptions(dnsOptions) - - content, err := rc.Generate(false) - if err != nil { - return nil, err - } - - // Write the resolv.conf file - it's bind-mounted into the container, so can't - // move a temp file into place, just have to truncate and write it. - // - // TODO(thaJeztah): the Build function is currently only used by BuildKit, which only uses "File.Content", and doesn't require the file to be written. - if err := os.WriteFile(path, content, 0o644); err != nil { - return nil, err - } - - // TODO(thaJeztah): the Build function is currently only used by BuildKit, which does not use the Hash - hash := digest.FromBytes(content) - return &File{Content: content, Hash: []byte(hash)}, nil -} diff --git a/daemon/libnetwork/resolvconf/resolvconf_unix_test.go b/daemon/libnetwork/resolvconf/resolvconf_unix_test.go deleted file mode 100644 index 3fb627c791..0000000000 --- a/daemon/libnetwork/resolvconf/resolvconf_unix_test.go +++ /dev/null @@ -1,455 +0,0 @@ -//go:build !windows - -package resolvconf - -import ( - "bytes" - "net/netip" - "os" - "strings" - "testing" - - "github.com/google/go-cmp/cmp/cmpopts" - "github.com/opencontainers/go-digest" - "gotest.tools/v3/assert" - is "gotest.tools/v3/assert/cmp" -) - -func TestGet(t *testing.T) { - actual, err := Get() - if err != nil { - t.Fatal(err) - } - expected, err := os.ReadFile(Path()) - if err != nil { - t.Fatal(err) - } - if !bytes.Equal(actual.Content, expected) { - t.Errorf("%s and GetResolvConf have different content.", Path()) - } - hash := digest.FromBytes(expected) - if !bytes.Equal(actual.Hash, []byte(hash)) { - t.Errorf("%s and GetResolvConf have different hashes.", Path()) - } -} - -func TestGetNameservers(t *testing.T) { - for _, tc := range []struct { - input string - result []string - }{ - { - input: ``, - }, - { - input: `search example.com`, - }, - { - input: ` nameserver 1.2.3.4 `, - result: []string{"1.2.3.4"}, - }, - { - input: ` -nameserver 1.2.3.4 -nameserver 40.3.200.10 -search example.com`, - result: []string{"1.2.3.4", "40.3.200.10"}, - }, - { - input: `nameserver 1.2.3.4 -search example.com -nameserver 4.30.20.100`, - result: []string{"1.2.3.4", "4.30.20.100"}, - }, - { - input: `search example.com -nameserver 1.2.3.4 -#nameserver 4.3.2.1`, - result: []string{"1.2.3.4"}, - }, - { - input: `search example.com -nameserver 1.2.3.4 # not 4.3.2.1`, - result: []string{"1.2.3.4"}, - }, - } { - test := GetNameservers([]byte(tc.input), IP) - if !strSlicesEqual(test, tc.result) { - t.Errorf("Wrong nameserver string {%s} should be %v. Input: %s", test, tc.result, tc.input) - } - } -} - -func TestGetNameserversAsPrefix(t *testing.T) { - for _, tc := range []struct { - input string - result []netip.Prefix - }{ - { - input: ``, - result: []netip.Prefix{}, - }, - { - input: `search example.com`, - result: []netip.Prefix{}, - }, - { - input: ` nameserver 1.2.3.4 `, - result: []netip.Prefix{netip.MustParsePrefix("1.2.3.4/32")}, - }, - { - input: ` -nameserver 1.2.3.4 -nameserver 40.3.200.10 -search example.com`, - result: []netip.Prefix{netip.MustParsePrefix("1.2.3.4/32"), netip.MustParsePrefix("40.3.200.10/32")}, - }, - { - input: `nameserver 1.2.3.4 -search example.com -nameserver 4.30.20.100`, - result: []netip.Prefix{netip.MustParsePrefix("1.2.3.4/32"), netip.MustParsePrefix("4.30.20.100/32")}, - }, - { - input: `search example.com -nameserver 1.2.3.4 -#nameserver 4.3.2.1`, - result: []netip.Prefix{netip.MustParsePrefix("1.2.3.4/32")}, - }, - { - input: `search example.com -nameserver 1.2.3.4 # not 4.3.2.1`, - result: []netip.Prefix{netip.MustParsePrefix("1.2.3.4/32")}, - }, - { - input: `nameserver fd6f:c490:ec68::1`, - result: []netip.Prefix{netip.MustParsePrefix("fd6f:c490:ec68::1/128")}, - }, - { - input: `nameserver fe80::1234%eth0`, - result: []netip.Prefix{netip.MustParsePrefix("fe80::1234/128")}, - }, - } { - test := GetNameserversAsPrefix([]byte(tc.input)) - assert.DeepEqual(t, test, tc.result, cmpopts.EquateComparable(netip.Prefix{})) - } -} - -func TestGetSearchDomains(t *testing.T) { - for _, tc := range []struct { - input string - result []string - }{ - { - input: ``, - }, - { - input: `# ignored`, - }, - { - input: `search example.com`, - result: []string{"example.com"}, - }, - { - input: `search example.com # notignored`, - result: []string{"example.com", "#", "notignored"}, - }, - { - input: ` search example.com `, - result: []string{"example.com"}, - }, - { - input: ` search example.com # notignored`, - result: []string{"example.com", "#", "notignored"}, - }, - { - input: `search foo.example.com example.com`, - result: []string{"foo.example.com", "example.com"}, - }, - { - input: ` search foo.example.com example.com `, - result: []string{"foo.example.com", "example.com"}, - }, - { - input: ` search foo.example.com example.com # notignored`, - result: []string{"foo.example.com", "example.com", "#", "notignored"}, - }, - { - input: `nameserver 1.2.3.4 -search foo.example.com example.com`, - result: []string{"foo.example.com", "example.com"}, - }, - { - input: `nameserver 1.2.3.4 -search dup1.example.com dup2.example.com -search foo.example.com example.com`, - result: []string{"foo.example.com", "example.com"}, - }, - { - input: `nameserver 1.2.3.4 -search foo.example.com example.com -nameserver 4.30.20.100`, - result: []string{"foo.example.com", "example.com"}, - }, - { - input: `domain an.example`, - result: []string{"an.example"}, - }, - } { - test := GetSearchDomains([]byte(tc.input)) - if !strSlicesEqual(test, tc.result) { - t.Errorf("Wrong search domain string {%s} should be %v. Input: %s", test, tc.result, tc.input) - } - } -} - -func TestGetOptions(t *testing.T) { - for _, tc := range []struct { - input string - result []string - }{ - { - input: ``, - }, - { - input: `# ignored`, - }, - { - input: `; ignored`, - }, - { - input: `nameserver 1.2.3.4`, - }, - { - input: `options opt1`, - result: []string{"opt1"}, - }, - { - input: `options opt1 # notignored`, - result: []string{"opt1", "#", "notignored"}, - }, - { - input: `options opt1 ; notignored`, - result: []string{"opt1", ";", "notignored"}, - }, - { - input: ` options opt1 `, - result: []string{"opt1"}, - }, - { - input: ` options opt1 # notignored`, - result: []string{"opt1", "#", "notignored"}, - }, - { - input: `options opt1 opt2 opt3`, - result: []string{"opt1", "opt2", "opt3"}, - }, - { - input: `options opt1 opt2 opt3 # notignored`, - result: []string{"opt1", "opt2", "opt3", "#", "notignored"}, - }, - { - input: ` options opt1 opt2 opt3 `, - result: []string{"opt1", "opt2", "opt3"}, - }, - { - input: ` options opt1 opt2 opt3 # notignored`, - result: []string{"opt1", "opt2", "opt3", "#", "notignored"}, - }, - { - input: `nameserver 1.2.3.4 -options opt1 opt2 opt3`, - result: []string{"opt1", "opt2", "opt3"}, - }, - { - input: `nameserver 1.2.3.4 -options opt1 opt2 -options opt3 opt4`, - result: []string{"opt1", "opt2", "opt3", "opt4"}, - }, - } { - test := GetOptions([]byte(tc.input)) - if !strSlicesEqual(test, tc.result) { - t.Errorf("Wrong options string {%s} should be %v. Input: %s", test, tc.result, tc.input) - } - } -} - -func strSlicesEqual(a, b []string) bool { - if len(a) != len(b) { - return false - } - - for i, v := range a { - if v != b[i] { - return false - } - } - - return true -} - -const ( - // Example IP-addresses as defined in [RFC 5737], [RFC 3849, section 2]. - // - // [RFC 5737]: https://datatracker.ietf.org/doc/html/rfc5737 - // [RFC 3849, section 2]: https://datatracker.ietf.org/doc/html/rfc3849#section-2 - testNS1 = "192.0.2.1" - testNS2 = "2001:db8::1" - testNS3 = "203.0.113.3" -) - -func TestBuild(t *testing.T) { - tests := []struct { - doc string - nameServers []string - dnsSearch []string - dnsOptions []string - expOut string - expErr string - }{ - { - doc: "no options", - expOut: ``, - }, - { - doc: "all options", - nameServers: []string{testNS1, testNS2, testNS3}, - dnsSearch: []string{"search1"}, - dnsOptions: []string{"opt1"}, - expOut: `nameserver 192.0.2.1 -nameserver 2001:db8::1 -nameserver 203.0.113.3 -search search1 -options opt1 -`, - }, - { - doc: "zero-length dns search", - nameServers: []string{testNS1, testNS2, testNS3}, - dnsSearch: []string{"."}, - dnsOptions: []string{"opt1"}, - expOut: `nameserver 192.0.2.1 -nameserver 2001:db8::1 -nameserver 203.0.113.3 -options opt1 -`, - }, - { - doc: "no dns options", - nameServers: []string{testNS1, testNS2, testNS3}, - dnsSearch: []string{"search1"}, - dnsOptions: []string{}, - expOut: `nameserver 192.0.2.1 -nameserver 2001:db8::1 -nameserver 203.0.113.3 -search search1 -`, - }, - { - doc: "invalid nameserver", - nameServers: []string{"resolver.example.com"}, - expErr: `bad nameserver address: ParseAddr("resolver.example.com"): unexpected character (at "resolver.example.com")`, - }, - } - - tmpDir := t.TempDir() - for _, tc := range tests { - t.Run(tc.doc, func(t *testing.T) { - file, err := os.CreateTemp(tmpDir, "") - assert.NilError(t, err) - - f, err := Build(file.Name(), tc.nameServers, tc.dnsSearch, tc.dnsOptions) - if tc.expErr != "" { - assert.Error(t, err, tc.expErr) - } else if err != nil { - assert.NilError(t, err) - assert.Equal(t, string(f.Content), tc.expOut) - } - - // Verify the content matches the expected; for error-cases, - // this verifies the file is empty. - content, err := os.ReadFile(file.Name()) - assert.NilError(t, err) - assert.Equal(t, string(content), tc.expOut) - }) - } -} - -func TestFilterResolvDNS(t *testing.T) { - testcases := []struct { - name string - input string - ipv6Enabled bool - expOut string - }{ - { - name: "No localhost", - input: "nameserver 10.16.60.14\nnameserver 10.16.60.21\n", - expOut: "nameserver 10.16.60.14\nnameserver 10.16.60.21", - }, - { - name: "Localhost last", - input: "nameserver 10.16.60.14\nnameserver 10.16.60.21\nnameserver 127.0.0.1\n", - expOut: "nameserver 10.16.60.14\nnameserver 10.16.60.21", - }, - { - name: "Localhost middle", - input: "nameserver 10.16.60.14\nnameserver 127.0.0.1\nnameserver 10.16.60.21\n", - expOut: "nameserver 10.16.60.14\nnameserver 10.16.60.21", - }, - { - name: "Localhost first", - input: "nameserver 127.0.1.1\nnameserver 10.16.60.14\nnameserver 10.16.60.21\n", - expOut: "nameserver 10.16.60.14\nnameserver 10.16.60.21", - }, - { - name: "IPv6 Localhost", - input: "nameserver ::1\nnameserver 10.16.60.14\nnameserver 127.0.2.1\nnameserver 10.16.60.21\n", - expOut: "nameserver 10.16.60.14\nnameserver 10.16.60.21", - }, - { - name: "Two IPv6 Localhosts", - input: "nameserver 10.16.60.14\nnameserver ::1\nnameserver 10.16.60.21\nnameserver ::1", - expOut: "nameserver 10.16.60.14\nnameserver 10.16.60.21", - }, - { - name: "IPv6 disabled", - input: "nameserver 10.16.60.14\nnameserver 2002:dead:beef::1\nnameserver 10.16.60.21\nnameserver ::1", - expOut: "nameserver 10.16.60.14\nnameserver 10.16.60.21", - }, - { - name: "IPv6 link-local disabled", - input: "nameserver 10.16.60.14\nnameserver FE80::BB1%1\nnameserver FE80::BB1%eth0\nnameserver 10.16.60.21", - expOut: "nameserver 10.16.60.14\nnameserver 10.16.60.21", - }, - { - name: "IPv6 enabled", - input: "nameserver 10.16.60.14\nnameserver 2002:dead:beef::1\nnameserver 10.16.60.21\nnameserver ::1\n", - ipv6Enabled: true, - expOut: "nameserver 10.16.60.14\nnameserver 2002:dead:beef::1\nnameserver 10.16.60.21", - }, - { - // with IPv6 enabled, and no non-localhost servers, Google defaults (both IPv4+IPv6) should be added - name: "localhost only IPv6", - input: "nameserver 127.0.0.1\nnameserver ::1\nnameserver 127.0.2.1", - ipv6Enabled: true, - expOut: "nameserver 8.8.8.8\nnameserver 8.8.4.4\nnameserver 2001:4860:4860::8888\nnameserver 2001:4860:4860::8844", - }, - { - // with IPv6 disabled, and no non-localhost servers, Google defaults (only IPv4) should be added - name: "localhost only no IPv6", - input: "nameserver 127.0.0.1\nnameserver ::1\nnameserver 127.0.2.1", - expOut: "nameserver 8.8.8.8\nnameserver 8.8.4.4", - }, - } - - for _, tc := range testcases { - t.Run(tc.name, func(t *testing.T) { - f, err := FilterResolvDNS([]byte(tc.input), tc.ipv6Enabled) - assert.Check(t, is.Nil(err)) - out := strings.TrimSpace(string(f.Content)) - assert.Check(t, is.Equal(out, tc.expOut)) - }) - } -} diff --git a/daemon/libnetwork/sandbox_dns_unix_test.go b/daemon/libnetwork/sandbox_dns_unix_test.go index 8d8fe5a240..3bb64cf5ce 100644 --- a/daemon/libnetwork/sandbox_dns_unix_test.go +++ b/daemon/libnetwork/sandbox_dns_unix_test.go @@ -3,15 +3,26 @@ package libnetwork import ( + "bytes" "context" + "os" "testing" "github.com/moby/moby/v2/daemon/libnetwork/config" - "github.com/moby/moby/v2/daemon/libnetwork/resolvconf" + "github.com/moby/moby/v2/daemon/libnetwork/internal/resolvconf" "gotest.tools/v3/assert" is "gotest.tools/v3/assert/cmp" ) +func getResolvConfOptions(t *testing.T, rcPath string) []string { + t.Helper() + resolv, err := os.ReadFile(rcPath) + assert.NilError(t, err) + rc, err := resolvconf.Parse(bytes.NewBuffer(resolv), "") + assert.NilError(t, err) + return rc.Options() +} + func TestDNSOptions(t *testing.T) { c, err := New(context.Background(), config.OptionDataDir(t.TempDir())) assert.NilError(t, err) @@ -32,26 +43,20 @@ func TestDNSOptions(t *testing.T) { assert.NilError(t, err) err = sb.rebuildDNS() assert.NilError(t, err) - currRC, err := resolvconf.GetSpecific(sb.config.resolvConfPath) - assert.NilError(t, err) - dnsOptionsList := resolvconf.GetOptions(currRC.Content) + dnsOptionsList := getResolvConfOptions(t, sb.config.resolvConfPath) assert.Check(t, is.Len(dnsOptionsList, 1)) assert.Check(t, is.Equal("ndots:0", dnsOptionsList[0])) sb.config.dnsOptionsList = []string{"ndots:5"} err = sb.setupDNS() assert.NilError(t, err) - currRC, err = resolvconf.GetSpecific(sb.config.resolvConfPath) - assert.NilError(t, err) - dnsOptionsList = resolvconf.GetOptions(currRC.Content) + dnsOptionsList = getResolvConfOptions(t, sb.config.resolvConfPath) assert.Check(t, is.Len(dnsOptionsList, 1)) assert.Check(t, is.Equal("ndots:5", dnsOptionsList[0])) err = sb.rebuildDNS() assert.NilError(t, err) - currRC, err = resolvconf.GetSpecific(sb.config.resolvConfPath) - assert.NilError(t, err) - dnsOptionsList = resolvconf.GetOptions(currRC.Content) + dnsOptionsList = getResolvConfOptions(t, sb.config.resolvConfPath) assert.Check(t, is.Len(dnsOptionsList, 1)) assert.Check(t, is.Equal("ndots:5", dnsOptionsList[0])) @@ -65,9 +70,7 @@ func TestDNSOptions(t *testing.T) { assert.NilError(t, err) err = sb2.rebuildDNS() assert.NilError(t, err) - currRC, err = resolvconf.GetSpecific(sb2.config.resolvConfPath) - assert.NilError(t, err) - dnsOptionsList = resolvconf.GetOptions(currRC.Content) + dnsOptionsList = getResolvConfOptions(t, sb2.config.resolvConfPath) assert.Check(t, is.Len(dnsOptionsList, 1)) assert.Check(t, is.Equal("ndots:0", dnsOptionsList[0])) @@ -76,9 +79,7 @@ func TestDNSOptions(t *testing.T) { assert.NilError(t, err) err = sb2.rebuildDNS() assert.NilError(t, err) - currRC, err = resolvconf.GetSpecific(sb2.config.resolvConfPath) - assert.NilError(t, err) - dnsOptionsList = resolvconf.GetOptions(currRC.Content) + dnsOptionsList = getResolvConfOptions(t, sb2.config.resolvConfPath) assert.Check(t, is.DeepEqual([]string{"ndots:0"}, dnsOptionsList)) sb2.config.dnsOptionsList = []string{"ndots:-1"} @@ -86,8 +87,6 @@ func TestDNSOptions(t *testing.T) { assert.NilError(t, err) err = sb2.rebuildDNS() assert.NilError(t, err) - currRC, err = resolvconf.GetSpecific(sb2.config.resolvConfPath) - assert.NilError(t, err) - dnsOptionsList = resolvconf.GetOptions(currRC.Content) + dnsOptionsList = getResolvConfOptions(t, sb2.config.resolvConfPath) assert.Check(t, is.DeepEqual([]string{"ndots:0"}, dnsOptionsList)) } diff --git a/daemon/libnetwork/types/types.go b/daemon/libnetwork/types/types.go index d7cf1f762c..1100d1ef17 100644 --- a/daemon/libnetwork/types/types.go +++ b/daemon/libnetwork/types/types.go @@ -12,8 +12,7 @@ import ( "github.com/moby/moby/v2/errdefs" ) -// constants for the IP address type -// Deprecated: use the consts defined in github.com/docker/docker/libnetwork/resolvconf +// constants for the IP address type. const ( IP = iota // IPv4 and IPv6 IPv4