Browse code

The option --dns, --dns-search, --dns-opt and --net=host should not be mutually exclusive.

This fix tries to address the issue raised in #21976 and allows
the options of `--dns`, `--dns-search`, `--dns-opt` and `--net=host`
to work at the same time.

The documentation has been updated and additional tests have been
added to cover this change.

This fix fixes #21976.

Signed-off-by: Yong Tang <yong.tang.github@outlook.com>

Yong Tang authored on 2016/04/29 14:46:57
Showing 6 changed files
... ...
@@ -48,7 +48,11 @@ func (daemon *Daemon) buildSandboxOptions(container *container.Container, n libn
48 48
 	if container.HostConfig.NetworkMode.IsHost() {
49 49
 		sboxOptions = append(sboxOptions, libnetwork.OptionUseDefaultSandbox())
50 50
 		sboxOptions = append(sboxOptions, libnetwork.OptionOriginHostsPath("/etc/hosts"))
51
-		sboxOptions = append(sboxOptions, libnetwork.OptionOriginResolvConfPath("/etc/resolv.conf"))
51
+		if len(container.HostConfig.DNS) == 0 && len(daemon.configStore.DNS) == 0 &&
52
+			len(container.HostConfig.DNSSearch) == 0 && len(daemon.configStore.DNSSearch) == 0 &&
53
+			len(container.HostConfig.DNSOptions) == 0 && len(daemon.configStore.DNSOptions) == 0 {
54
+			sboxOptions = append(sboxOptions, libnetwork.OptionOriginResolvConfPath("/etc/resolv.conf"))
55
+		}
52 56
 	} else {
53 57
 		// OptionUseExternalKey is mandatory for userns support.
54 58
 		// But optional for non-userns support
... ...
@@ -382,11 +382,14 @@ name, they must be linked.
382 382
 With the network set to `host` a container will share the host's
383 383
 network stack and all interfaces from the host will be available to the
384 384
 container.  The container's hostname will match the hostname on the host
385
-system.  Note that `--add-host` `--dns` `--dns-search`
386
-`--dns-opt` and `--mac-address` are invalid in `host` netmode. Even in `host`
385
+system.  Note that `--add-host` and `--mac-address` are invalid in `host` netmode. Even in `host`
387 386
 network mode a container has its own UTS namespace by default. As such
388 387
 `--hostname` is allowed in `host` network mode and will only change the
389 388
 hostname inside the container.
389
+Note also that `--dns`, `--dns-search` and `--dns-opt` are
390
+valid in `host` mode and `/etc/resolv.conf` will be updated accordingly. However, the
391
+update in `/etc/resolv.conf` only happens inside the container. No change will be
392
+made for `/etc/resolv.conf` in host.
390 393
 
391 394
 Compared to the default `bridge` mode, the `host` mode gives *significantly*
392 395
 better networking performance since it uses the host's native networking stack
... ...
@@ -2332,3 +2332,39 @@ func (s *DockerDaemonSuite) TestBuildOnDisabledBridgeNetworkDaemon(c *check.C) {
2332 2332
 	c.Assert(err, check.IsNil, comment)
2333 2333
 	c.Assert(code, check.Equals, 0, comment)
2334 2334
 }
2335
+
2336
+// Test case for #21976
2337
+func (s *DockerDaemonSuite) TestDaemonDnsInHostMode(c *check.C) {
2338
+	testRequires(c, SameHostDaemon, DaemonIsLinux)
2339
+
2340
+	err := s.d.StartWithBusybox("--dns", "1.2.3.4")
2341
+	c.Assert(err, checker.IsNil)
2342
+
2343
+	expectedOutput := "nameserver 1.2.3.4"
2344
+	out, _ := s.d.Cmd("run", "--net=host", "busybox", "cat", "/etc/resolv.conf")
2345
+	c.Assert(out, checker.Contains, expectedOutput, check.Commentf("Expected '%s', but got %q", expectedOutput, out))
2346
+}
2347
+
2348
+// Test case for #21976
2349
+func (s *DockerDaemonSuite) TestDaemonDnsSearchInHostMode(c *check.C) {
2350
+	testRequires(c, SameHostDaemon, DaemonIsLinux)
2351
+
2352
+	err := s.d.StartWithBusybox("--dns-search", "example.com")
2353
+	c.Assert(err, checker.IsNil)
2354
+
2355
+	expectedOutput := "search example.com"
2356
+	out, _ := s.d.Cmd("run", "--net=host", "busybox", "cat", "/etc/resolv.conf")
2357
+	c.Assert(out, checker.Contains, expectedOutput, check.Commentf("Expected '%s', but got %q", expectedOutput, out))
2358
+}
2359
+
2360
+// Test case for #21976
2361
+func (s *DockerDaemonSuite) TestDaemonDnsOptionsInHostMode(c *check.C) {
2362
+	testRequires(c, SameHostDaemon, DaemonIsLinux)
2363
+
2364
+	err := s.d.StartWithBusybox("--dns-opt", "timeout:3")
2365
+	c.Assert(err, checker.IsNil)
2366
+
2367
+	expectedOutput := "options timeout:3"
2368
+	out, _ := s.d.Cmd("run", "--net=host", "busybox", "cat", "/etc/resolv.conf")
2369
+	c.Assert(out, checker.Contains, expectedOutput, check.Commentf("Expected '%s', but got %q", expectedOutput, out))
2370
+}
... ...
@@ -67,10 +67,7 @@ func (s *DockerSuite) TestConflictContainerNetworkHostAndLinks(c *check.C) {
67 67
 func (s *DockerSuite) TestConflictNetworkModeNetHostAndOptions(c *check.C) {
68 68
 	testRequires(c, DaemonIsLinux, NotUserNamespace)
69 69
 
70
-	out, _ := dockerCmdWithFail(c, "run", "--net=host", "--dns=8.8.8.8", "busybox", "ps")
71
-	c.Assert(out, checker.Contains, runconfig.ErrConflictNetworkAndDNS.Error())
72
-
73
-	out, _ = dockerCmdWithFail(c, "run", "--net=host", "--add-host=name:8.8.8.8", "busybox", "ps")
70
+	out, _ := dockerCmdWithFail(c, "run", "--net=host", "--add-host=name:8.8.8.8", "busybox", "ps")
74 71
 	c.Assert(out, checker.Contains, runconfig.ErrConflictNetworkHosts.Error())
75 72
 
76 73
 	out, _ = dockerCmdWithFail(c, "run", "--net=host", "--mac-address=92:d0:c6:0a:29:33", "busybox", "ps")
... ...
@@ -4370,3 +4370,34 @@ func (s *DockerSuite) TestRunTooLongHostname(c *check.C) {
4370 4370
 
4371 4371
 	}
4372 4372
 }
4373
+
4374
+// Test case for #21976
4375
+func (s *DockerSuite) TestRunDnsInHostMode(c *check.C) {
4376
+	testRequires(c, DaemonIsLinux, NotUserNamespace)
4377
+
4378
+	expectedOutput := "nameserver 127.0.0.1"
4379
+	expectedWarning := "Localhost DNS setting"
4380
+	out, stderr, _ := dockerCmdWithStdoutStderr(c, "run", "--dns=127.0.0.1", "--net=host", "busybox", "cat", "/etc/resolv.conf")
4381
+	c.Assert(out, checker.Contains, expectedOutput, check.Commentf("Expected '%s', but got %q", expectedOutput, out))
4382
+	c.Assert(stderr, checker.Contains, expectedWarning, check.Commentf("Expected warning on stderr about localhost resolver, but got %q", stderr))
4383
+
4384
+	expectedOutput = "nameserver 1.2.3.4"
4385
+	out, _ = dockerCmd(c, "run", "--dns=1.2.3.4", "--net=host", "busybox", "cat", "/etc/resolv.conf")
4386
+	c.Assert(out, checker.Contains, expectedOutput, check.Commentf("Expected '%s', but got %q", expectedOutput, out))
4387
+
4388
+	expectedOutput = "search example.com"
4389
+	out, _ = dockerCmd(c, "run", "--dns-search=example.com", "--net=host", "busybox", "cat", "/etc/resolv.conf")
4390
+	c.Assert(out, checker.Contains, expectedOutput, check.Commentf("Expected '%s', but got %q", expectedOutput, out))
4391
+
4392
+	expectedOutput = "options timeout:3"
4393
+	out, _ = dockerCmd(c, "run", "--dns-opt=timeout:3", "--net=host", "busybox", "cat", "/etc/resolv.conf")
4394
+	c.Assert(out, checker.Contains, expectedOutput, check.Commentf("Expected '%s', but got %q", expectedOutput, out))
4395
+
4396
+	expectedOutput1 := "nameserver 1.2.3.4"
4397
+	expectedOutput2 := "search example.com"
4398
+	expectedOutput3 := "options timeout:3"
4399
+	out, _ = dockerCmd(c, "run", "--dns=1.2.3.4", "--dns-search=example.com", "--dns-opt=timeout:3", "--net=host", "busybox", "cat", "/etc/resolv.conf")
4400
+	c.Assert(out, checker.Contains, expectedOutput1, check.Commentf("Expected '%s', but got %q", expectedOutput1, out))
4401
+	c.Assert(out, checker.Contains, expectedOutput2, check.Commentf("Expected '%s', but got %q", expectedOutput2, out))
4402
+	c.Assert(out, checker.Contains, expectedOutput3, check.Commentf("Expected '%s', but got %q", expectedOutput3, out))
4403
+}
... ...
@@ -52,7 +52,7 @@ func ValidateNetMode(c *container.Config, hc *container.HostConfig) error {
52 52
 		return ErrConflictContainerNetworkAndLinks
53 53
 	}
54 54
 
55
-	if (hc.NetworkMode.IsHost() || hc.NetworkMode.IsContainer()) && len(hc.DNS) > 0 {
55
+	if hc.NetworkMode.IsContainer() && len(hc.DNS) > 0 {
56 56
 		return ErrConflictNetworkAndDNS
57 57
 	}
58 58