diff --git a/daemonconfig/config.go b/daemonconfig/config.go index b26d3eec3a..6cb3659e18 100644 --- a/daemonconfig/config.go +++ b/daemonconfig/config.go @@ -18,6 +18,7 @@ type Config struct { Root string AutoRestart bool Dns []string + DnsSearch []string EnableIptables bool EnableIpForward bool DefaultIp net.IP @@ -49,6 +50,9 @@ func ConfigFromJob(job *engine.Job) *Config { if dns := job.GetenvList("Dns"); dns != nil { config.Dns = dns } + if dnsSearch := job.GetenvList("DnsSearch"); dnsSearch != nil { + config.DnsSearch = dnsSearch + } if mtu := job.GetenvInt("Mtu"); mtu != 0 { config.Mtu = mtu } else { diff --git a/docker/docker.go b/docker/docker.go index 277383fa76..e4ce8a0b74 100644 --- a/docker/docker.go +++ b/docker/docker.go @@ -48,6 +48,7 @@ func main() { flSocketGroup = flag.String([]string{"G", "-group"}, "docker", "Group to assign the unix socket specified by -H when running in daemon mode; use '' (the empty string) to disable setting of a group") flEnableCors = flag.Bool([]string{"#api-enable-cors", "-api-enable-cors"}, false, "Enable CORS headers in the remote API") flDns = opts.NewListOpts(opts.ValidateIp4Address) + flDnsSearch = opts.NewListOpts(opts.ValidateDomain) flEnableIptables = flag.Bool([]string{"#iptables", "-iptables"}, true, "Enable Docker's addition of iptables rules") flEnableIpForward = flag.Bool([]string{"#ip-forward", "-ip-forward"}, true, "Enable net.ipv4.ip_forward") flDefaultIp = flag.String([]string{"#ip", "-ip"}, "0.0.0.0", "Default IP address to use when binding container ports") @@ -63,6 +64,7 @@ func main() { flKey = flag.String([]string{"-tlskey"}, dockerConfDir+defaultKeyFile, "Path to TLS key file") ) flag.Var(&flDns, []string{"#dns", "-dns"}, "Force docker to use specific DNS servers") + flag.Var(&flDnsSearch, []string{"-dns-search"}, "Force Docker to use specific DNS search domains") flag.Var(&flHosts, []string{"H", "-host"}, "tcp://host:port, unix://path/to/socket, fd://* or fd://socketfd to use in daemon mode. Multiple sockets can be specified") flag.Parse() @@ -134,6 +136,7 @@ func main() { job.Setenv("Root", realRoot) job.SetenvBool("AutoRestart", *flAutoRestart) job.SetenvList("Dns", flDns.GetAll()) + job.SetenvList("DnsSearch", flDnsSearch.GetAll()) job.SetenvBool("EnableIptables", *flEnableIptables) job.SetenvBool("EnableIpForward", *flEnableIpForward) job.Setenv("BridgeIface", *bridgeName) diff --git a/docs/sources/reference/api/docker_remote_api_v1.10.rst b/docs/sources/reference/api/docker_remote_api_v1.10.rst index 649f58196e..98827c9eb2 100644 --- a/docs/sources/reference/api/docker_remote_api_v1.10.rst +++ b/docs/sources/reference/api/docker_remote_api_v1.10.rst @@ -136,6 +136,7 @@ Create a container }, "VolumesFrom":"", "WorkingDir":"", + "DisableNetwork": false, "ExposedPorts":{ "22/tcp": {} } diff --git a/docs/sources/reference/commandline/cli.rst b/docs/sources/reference/commandline/cli.rst index 76184b0b0b..d398b16e53 100644 --- a/docs/sources/reference/commandline/cli.rst +++ b/docs/sources/reference/commandline/cli.rst @@ -77,6 +77,7 @@ Commands --bip="": Use this CIDR notation address for the network bridge's IP, not compatible with -b -d, --daemon=false: Enable daemon mode --dns=[]: Force docker to use specific DNS servers + --dns-search=[]: Force Docker to use specific DNS search domains -g, --graph="/var/lib/docker": Path to use as the root of the docker runtime --icc=true: Enable inter-container communication --ip="0.0.0.0": Default IP address to use when binding container ports @@ -101,6 +102,8 @@ To force Docker to use devicemapper as the storage driver, use ``docker -d -s de To set the DNS server for all Docker containers, use ``docker -d --dns 8.8.8.8``. +To set the DNS search domain for all Docker containers, use ``docker -d --dns-search example.com``. + To run the daemon with debug output, use ``docker -d -D``. To use lxc as the execution driver, use ``docker -d -e lxc``. @@ -401,6 +404,7 @@ not overridden in the JSON hash will be merged in. "VolumesFrom" : "", "Cmd" : ["cat", "-e", "/etc/resolv.conf"], "Dns" : ["8.8.8.8", "8.8.4.4"], + "DnsSearch" : ["example.com"], "MemorySwap" : 0, "AttachStdin" : false, "AttachStderr" : false, @@ -1136,6 +1140,7 @@ image is removed. -t, --tty=false: Allocate a pseudo-tty -u, --user="": Username or UID --dns=[]: Set custom dns servers for the container + --dns-search=[]: Set custom DNS search domains for the container -v, --volume=[]: Create a bind mount to a directory or file with: [host-path]:[container-path]:[rw|ro]. If a directory "container-path" is missing, then docker creates a new volume. --volumes-from="": Mount all volumes from the given container(s) --entrypoint="": Overwrite the default entrypoint set by the image @@ -1293,7 +1298,7 @@ A complete example $ sudo docker run -d --name static static-web-files sh $ sudo docker run -d --expose=8098 --name riak riakserver $ sudo docker run -d -m 100m -e DEVELOPMENT=1 -e BRANCH=example-code -v $(pwd):/app/bin:ro --name app appserver - $ sudo docker run -d -p 1443:443 --dns=dns.dev.org -v /var/log/httpd --volumes-from static --link riak --link app -h www.sven.dev.org --name web webserver + $ sudo docker run -d -p 1443:443 --dns=dns.dev.org --dns-search=dev.org -v /var/log/httpd --volumes-from static --link riak --link app -h www.sven.dev.org --name web webserver $ sudo docker run -t -i --rm --volumes-from web -w /var/log/httpd busybox tail -f access.log This example shows 5 containers that might be set up to test a web application change: @@ -1301,7 +1306,7 @@ This example shows 5 containers that might be set up to test a web application c 1. Start a pre-prepared volume image ``static-web-files`` (in the background) that has CSS, image and static HTML in it, (with a ``VOLUME`` instruction in the ``Dockerfile`` to allow the web server to use those files); 2. Start a pre-prepared ``riakserver`` image, give the container name ``riak`` and expose port ``8098`` to any containers that link to it; 3. Start the ``appserver`` image, restricting its memory usage to 100MB, setting two environment variables ``DEVELOPMENT`` and ``BRANCH`` and bind-mounting the current directory (``$(pwd)``) in the container in read-only mode as ``/app/bin``; -4. Start the ``webserver``, mapping port ``443`` in the container to port ``1443`` on the Docker server, setting the DNS server to ``dns.dev.org``, creating a volume to put the log files into (so we can access it from another container), then importing the files from the volume exposed by the ``static`` container, and linking to all exposed ports from ``riak`` and ``app``. Lastly, we set the hostname to ``web.sven.dev.org`` so its consistent with the pre-generated SSL certificate; +4. Start the ``webserver``, mapping port ``443`` in the container to port ``1443`` on the Docker server, setting the DNS server to ``dns.dev.org`` and DNS search domain to ``dev.org``, creating a volume to put the log files into (so we can access it from another container), then importing the files from the volume exposed by the ``static`` container, and linking to all exposed ports from ``riak`` and ``app``. Lastly, we set the hostname to ``web.sven.dev.org`` so its consistent with the pre-generated SSL certificate; 5. Finally, we create a container that runs ``tail -f access.log`` using the logs volume from the ``web`` container, setting the workdir to ``/var/log/httpd``. The ``--rm`` option means that when the container exits, the container's layer is removed. diff --git a/opts/opts.go b/opts/opts.go index 4f5897c796..67f1c8fd48 100644 --- a/opts/opts.go +++ b/opts/opts.go @@ -136,3 +136,16 @@ func ValidateIp4Address(val string) (string, error) { } return "", fmt.Errorf("%s is not an ip4 address", val) } + +func ValidateDomain(val string) (string, error) { + alpha := regexp.MustCompile(`[a-zA-Z]`) + if alpha.FindString(val) == "" { + return "", fmt.Errorf("%s is not a valid domain", val) + } + re := regexp.MustCompile(`^(:?(:?[a-zA-Z0-9]|(:?[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9]))(:?\.(:?[a-zA-Z0-9]|(:?[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])))*)\.?\s*$`) + ns := re.FindSubmatch([]byte(val)) + if len(ns) > 0 { + return string(ns[1]), nil + } + return "", fmt.Errorf("%s is not a valid domain", val) +} diff --git a/opts/opts_test.go b/opts/opts_test.go index a5c1fac9ca..299cbfe503 100644 --- a/opts/opts_test.go +++ b/opts/opts_test.go @@ -22,3 +22,57 @@ func TestValidateIP4(t *testing.T) { } } + +func TestValidateDomain(t *testing.T) { + valid := []string{ + `a`, + `a.`, + `1.foo`, + `17.foo`, + `foo.bar`, + `foo.bar.baz`, + `foo.bar.`, + `foo.bar.baz`, + `foo1.bar2`, + `foo1.bar2.baz`, + `1foo.2bar.`, + `1foo.2bar.baz`, + `foo-1.bar-2`, + `foo-1.bar-2.baz`, + `foo-1.bar-2.`, + `foo-1.bar-2.baz`, + `1-foo.2-bar`, + `1-foo.2-bar.baz`, + `1-foo.2-bar.`, + `1-foo.2-bar.baz`, + } + + invalid := []string{ + ``, + `.`, + `17`, + `17.`, + `.17`, + `17-.`, + `17-.foo`, + `.foo`, + `foo-.bar`, + `-foo.bar`, + `foo.bar-`, + `foo.bar-.baz`, + `foo.-bar`, + `foo.-bar.baz`, + } + + for _, domain := range valid { + if ret, err := ValidateDomain(domain); err != nil || ret == "" { + t.Fatalf("ValidateDomain(`"+domain+"`) got %s %s", ret, err) + } + } + + for _, domain := range invalid { + if ret, err := ValidateDomain(domain); err == nil || ret != "" { + t.Fatalf("ValidateDomain(`"+domain+"`) got %s %s", ret, err) + } + } +} diff --git a/runconfig/compare.go b/runconfig/compare.go index c09f897716..6ed7405246 100644 --- a/runconfig/compare.go +++ b/runconfig/compare.go @@ -20,6 +20,7 @@ func Compare(a, b *Config) bool { } if len(a.Cmd) != len(b.Cmd) || len(a.Dns) != len(b.Dns) || + len(a.DnsSearch) != len(b.DnsSearch) || len(a.Env) != len(b.Env) || len(a.PortSpecs) != len(b.PortSpecs) || len(a.ExposedPorts) != len(b.ExposedPorts) || @@ -38,6 +39,11 @@ func Compare(a, b *Config) bool { return false } } + for i := 0; i < len(a.DnsSearch); i++ { + if a.DnsSearch[i] != b.DnsSearch[i] { + return false + } + } for i := 0; i < len(a.Env); i++ { if a.Env[i] != b.Env[i] { return false diff --git a/runconfig/config.go b/runconfig/config.go index 9faa823a57..e961d659d7 100644 --- a/runconfig/config.go +++ b/runconfig/config.go @@ -26,6 +26,7 @@ type Config struct { Env []string Cmd []string Dns []string + DnsSearch []string Image string // Name of the image as it was passed by the operator (eg. could be symbolic) Volumes map[string]struct{} VolumesFrom string @@ -68,6 +69,9 @@ func ContainerConfigFromJob(job *engine.Job) *Config { if Dns := job.GetenvList("Dns"); Dns != nil { config.Dns = Dns } + if DnsSearch := job.GetenvList("DnsSearch"); DnsSearch != nil { + config.DnsSearch = DnsSearch + } if Entrypoint := job.GetenvList("Entrypoint"); Entrypoint != nil { config.Entrypoint = Entrypoint } diff --git a/runconfig/config_test.go b/runconfig/config_test.go index 46e4691b93..84846e5b1d 100644 --- a/runconfig/config_test.go +++ b/runconfig/config_test.go @@ -164,6 +164,7 @@ func TestCompare(t *testing.T) { volumes1["/test1"] = struct{}{} config1 := Config{ Dns: []string{"1.1.1.1", "2.2.2.2"}, + DnsSearch: []string{"foo", "bar"}, PortSpecs: []string{"1111:1111", "2222:2222"}, Env: []string{"VAR1=1", "VAR2=2"}, VolumesFrom: "11111111", @@ -171,6 +172,7 @@ func TestCompare(t *testing.T) { } config2 := Config{ Dns: []string{"0.0.0.0", "2.2.2.2"}, + DnsSearch: []string{"foo", "bar"}, PortSpecs: []string{"1111:1111", "2222:2222"}, Env: []string{"VAR1=1", "VAR2=2"}, VolumesFrom: "11111111", @@ -178,6 +180,7 @@ func TestCompare(t *testing.T) { } config3 := Config{ Dns: []string{"1.1.1.1", "2.2.2.2"}, + DnsSearch: []string{"foo", "bar"}, PortSpecs: []string{"0000:0000", "2222:2222"}, Env: []string{"VAR1=1", "VAR2=2"}, VolumesFrom: "11111111", @@ -185,6 +188,7 @@ func TestCompare(t *testing.T) { } config4 := Config{ Dns: []string{"1.1.1.1", "2.2.2.2"}, + DnsSearch: []string{"foo", "bar"}, PortSpecs: []string{"0000:0000", "2222:2222"}, Env: []string{"VAR1=1", "VAR2=2"}, VolumesFrom: "22222222", @@ -194,11 +198,20 @@ func TestCompare(t *testing.T) { volumes2["/test2"] = struct{}{} config5 := Config{ Dns: []string{"1.1.1.1", "2.2.2.2"}, + DnsSearch: []string{"foo", "bar"}, PortSpecs: []string{"0000:0000", "2222:2222"}, Env: []string{"VAR1=1", "VAR2=2"}, VolumesFrom: "11111111", Volumes: volumes2, } + config6 := Config{ + Dns: []string{"1.1.1.1", "2.2.2.2"}, + DnsSearch: []string{"foos", "bars"}, + PortSpecs: []string{"1111:1111", "2222:2222"}, + Env: []string{"VAR1=1", "VAR2=2"}, + VolumesFrom: "11111111", + Volumes: volumes1, + } if Compare(&config1, &config2) { t.Fatalf("Compare should return false, Dns are different") } @@ -211,6 +224,9 @@ func TestCompare(t *testing.T) { if Compare(&config1, &config5) { t.Fatalf("Compare should return false, Volumes are different") } + if Compare(&config1, &config6) { + t.Fatalf("Compare should return false, DnsSearch are different") + } if !Compare(&config1, &config1) { t.Fatalf("Compare should return true") } diff --git a/runconfig/merge.go b/runconfig/merge.go index 3b91aa2af0..34faaf75e7 100644 --- a/runconfig/merge.go +++ b/runconfig/merge.go @@ -100,6 +100,12 @@ func Merge(userConf, imageConf *Config) error { //duplicates aren't an issue here userConf.Dns = append(userConf.Dns, imageConf.Dns...) } + if userConf.DnsSearch == nil || len(userConf.DnsSearch) == 0 { + userConf.DnsSearch = imageConf.DnsSearch + } else { + //duplicates aren't an issue here + userConf.DnsSearch = append(userConf.DnsSearch, imageConf.DnsSearch...) + } if userConf.Entrypoint == nil || len(userConf.Entrypoint) == 0 { userConf.Entrypoint = imageConf.Entrypoint } diff --git a/runconfig/parse.go b/runconfig/parse.go index 2138f4e68c..cc33188ad5 100644 --- a/runconfig/parse.go +++ b/runconfig/parse.go @@ -42,6 +42,7 @@ func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Conf flPublish opts.ListOpts flExpose opts.ListOpts flDns opts.ListOpts + flDnsSearch = opts.NewListOpts(opts.ValidateDomain) flVolumesFrom opts.ListOpts flLxcOpts opts.ListOpts @@ -73,6 +74,7 @@ func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Conf cmd.Var(&flPublish, []string{"p", "-publish"}, fmt.Sprintf("Publish a container's port to the host (format: %s) (use 'docker port' to see the actual mapping)", nat.PortSpecTemplateFormat)) cmd.Var(&flExpose, []string{"#expose", "-expose"}, "Expose a port from the container without publishing it to your host") cmd.Var(&flDns, []string{"#dns", "-dns"}, "Set custom dns servers") + cmd.Var(&flDnsSearch, []string{"-dns-search"}, "Set custom dns search domains") cmd.Var(&flVolumesFrom, []string{"#volumes-from", "-volumes-from"}, "Mount volumes from the specified container(s)") cmd.Var(&flLxcOpts, []string{"#lxc-conf", "-lxc-conf"}, "Add custom lxc options --lxc-conf=\"lxc.cgroup.cpuset.cpus = 0,1\"") @@ -196,6 +198,7 @@ func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Conf Env: flEnv.GetAll(), Cmd: runCmd, Dns: flDns.GetAll(), + DnsSearch: flDnsSearch.GetAll(), Image: image, Volumes: flVolumes.GetMap(), VolumesFrom: strings.Join(flVolumesFrom.GetAll(), ","), diff --git a/runtime/runtime.go b/runtime/runtime.go index 4408e13902..38a1beccd2 100644 --- a/runtime/runtime.go +++ b/runtime/runtime.go @@ -493,13 +493,19 @@ func (runtime *Runtime) Create(config *runconfig.Config, name string) (*Containe } // If custom dns exists, then create a resolv.conf for the container - if len(config.Dns) > 0 || len(runtime.config.Dns) > 0 { - var dns []string + if len(config.Dns) > 0 || len(runtime.config.Dns) > 0 || len(config.DnsSearch) > 0 || len(runtime.config.DnsSearch) > 0 { + dns := utils.GetNameservers(resolvConf) + dnsSearch := utils.GetSearchDomains(resolvConf) if len(config.Dns) > 0 { dns = config.Dns - } else { + } else if len(runtime.config.Dns) > 0 { dns = runtime.config.Dns } + if len(config.DnsSearch) > 0 { + dnsSearch = config.DnsSearch + } else if len(runtime.config.DnsSearch) > 0 { + dnsSearch = runtime.config.DnsSearch + } container.ResolvConfPath = path.Join(container.root, "resolv.conf") f, err := os.Create(container.ResolvConfPath) if err != nil { @@ -511,6 +517,11 @@ func (runtime *Runtime) Create(config *runconfig.Config, name string) (*Containe return nil, nil, err } } + if len(dnsSearch) > 0 { + if _, err := f.Write([]byte("search " + strings.Join(dnsSearch, " ") + "\n")); err != nil { + return nil, nil, err + } + } } else { container.ResolvConfPath = "/etc/resolv.conf" } diff --git a/utils/utils.go b/utils/utils.go index 57a8200a7c..2702555973 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -731,54 +731,78 @@ func GetResolvConf() ([]byte, error) { // CheckLocalDns looks into the /etc/resolv.conf, // it returns true if there is a local nameserver or if there is no nameserver. func CheckLocalDns(resolvConf []byte) bool { - var parsedResolvConf = StripComments(resolvConf, []byte("#")) - if !bytes.Contains(parsedResolvConf, []byte("nameserver")) { - return true - } - for _, ip := range [][]byte{ - []byte("127.0.0.1"), - []byte("127.0.1.1"), - } { - if bytes.Contains(parsedResolvConf, ip) { - return true + for _, line := range GetLines(resolvConf, []byte("#")) { + if !bytes.Contains(line, []byte("nameserver")) { + continue } + for _, ip := range [][]byte{ + []byte("127.0.0.1"), + []byte("127.0.1.1"), + } { + if bytes.Contains(line, ip) { + return true + } + } + return false } - return false + return true } -// StripComments parses input into lines and strips away comments. -func StripComments(input []byte, commentMarker []byte) []byte { +// GetLines parses input into lines and strips away comments. +func GetLines(input []byte, commentMarker []byte) [][]byte { lines := bytes.Split(input, []byte("\n")) - var output []byte + var output [][]byte for _, currentLine := range lines { var commentIndex = bytes.Index(currentLine, commentMarker) if commentIndex == -1 { - output = append(output, currentLine...) + output = append(output, currentLine) } else { - output = append(output, currentLine[:commentIndex]...) + output = append(output, currentLine[:commentIndex]) } - output = append(output, []byte("\n")...) } return output } +// GetNameservers returns nameservers (if any) listed in /etc/resolv.conf +func GetNameservers(resolvConf []byte) []string { + nameservers := []string{} + re := regexp.MustCompile(`^\s*nameserver\s*(([0-9]+\.){3}([0-9]+))\s*$`) + for _, line := range GetLines(resolvConf, []byte("#")) { + var ns = re.FindSubmatch(line) + if len(ns) > 0 { + nameservers = append(nameservers, string(ns[1])) + } + } + return nameservers +} + // GetNameserversAsCIDR returns nameservers (if any) listed in // /etc/resolv.conf as CIDR blocks (e.g., "1.2.3.4/32") // This function's output is intended for net.ParseCIDR func GetNameserversAsCIDR(resolvConf []byte) []string { - var parsedResolvConf = StripComments(resolvConf, []byte("#")) nameservers := []string{} - re := regexp.MustCompile(`^\s*nameserver\s*(([0-9]+\.){3}([0-9]+))\s*$`) - for _, line := range bytes.Split(parsedResolvConf, []byte("\n")) { - var ns = re.FindSubmatch(line) - if len(ns) > 0 { - nameservers = append(nameservers, string(ns[1])+"/32") - } + for _, nameserver := range GetNameservers(resolvConf) { + nameservers = append(nameservers, nameserver+"/32") } - 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 { + re := regexp.MustCompile(`^\s*search\s*(([^\s]+\s*)*)$`) + domains := []string{} + for _, line := range GetLines(resolvConf, []byte("#")) { + match := re.FindSubmatch(line) + if match == nil { + continue + } + domains = strings.Fields(string(match[1])) + } + return domains +} + // FIXME: Change this not to receive default value as parameter func ParseHost(defaultHost string, defaultUnix, addr string) (string, error) { var ( diff --git a/utils/utils_test.go b/utils/utils_test.go index 444d2a2428..177d3667e1 100644 --- a/utils/utils_test.go +++ b/utils/utils_test.go @@ -444,6 +444,30 @@ func TestParsePortMapping(t *testing.T) { } } +func TestGetNameservers(t *testing.T) { + for resolv, result := range map[string][]string{` +nameserver 1.2.3.4 +nameserver 40.3.200.10 +search example.com`: {"1.2.3.4", "40.3.200.10"}, + `search example.com`: {}, + `nameserver 1.2.3.4 +search example.com +nameserver 4.30.20.100`: {"1.2.3.4", "4.30.20.100"}, + ``: {}, + ` nameserver 1.2.3.4 `: {"1.2.3.4"}, + `search example.com +nameserver 1.2.3.4 +#nameserver 4.3.2.1`: {"1.2.3.4"}, + `search example.com +nameserver 1.2.3.4 # not 4.3.2.1`: {"1.2.3.4"}, + } { + test := GetNameservers([]byte(resolv)) + if !StrSlicesEqual(test, result) { + t.Fatalf("Wrong nameserver string {%s} should be %v. Input: %s", test, result, resolv) + } + } +} + func TestGetNameserversAsCIDR(t *testing.T) { for resolv, result := range map[string][]string{` nameserver 1.2.3.4 @@ -468,6 +492,33 @@ nameserver 1.2.3.4 # not 4.3.2.1`: {"1.2.3.4/32"}, } } +func TestGetSearchDomains(t *testing.T) { + for resolv, result := range map[string][]string{ + `search example.com`: {"example.com"}, + `search example.com # ignored`: {"example.com"}, + ` search example.com `: {"example.com"}, + ` search example.com # ignored`: {"example.com"}, + `search foo.example.com example.com`: {"foo.example.com", "example.com"}, + ` search foo.example.com example.com `: {"foo.example.com", "example.com"}, + ` search foo.example.com example.com # ignored`: {"foo.example.com", "example.com"}, + ``: {}, + `# ignored`: {}, + `nameserver 1.2.3.4 +search foo.example.com example.com`: {"foo.example.com", "example.com"}, + `nameserver 1.2.3.4 +search dup1.example.com dup2.example.com +search foo.example.com example.com`: {"foo.example.com", "example.com"}, + `nameserver 1.2.3.4 +search foo.example.com example.com +nameserver 4.30.20.100`: {"foo.example.com", "example.com"}, + } { + test := GetSearchDomains([]byte(resolv)) + if !StrSlicesEqual(test, result) { + t.Fatalf("Wrong search domain string {%s} should be %v. Input: %s", test, result, resolv) + } + } +} + func StrSlicesEqual(a, b []string) bool { if len(a) != len(b) { return false