From 23821fe5867427fa36c265bc994b1a2c3cf9b21f Mon Sep 17 00:00:00 2001 From: Yong Tang Date: Thu, 28 Apr 2016 22:46:57 -0700 Subject: [PATCH] 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 --- daemon/container_operations.go | 6 +++- docs/reference/run.md | 7 +++-- integration-cli/docker_cli_daemon_test.go | 36 ++++++++++++++++++++++ integration-cli/docker_cli_netmode_test.go | 5 +-- integration-cli/docker_cli_run_test.go | 31 +++++++++++++++++++ runconfig/hostconfig_unix.go | 2 +- 6 files changed, 79 insertions(+), 8 deletions(-) diff --git a/daemon/container_operations.go b/daemon/container_operations.go index 95aae69db2..c9db37dc3c 100644 --- a/daemon/container_operations.go +++ b/daemon/container_operations.go @@ -48,7 +48,11 @@ func (daemon *Daemon) buildSandboxOptions(container *container.Container, n libn if container.HostConfig.NetworkMode.IsHost() { sboxOptions = append(sboxOptions, libnetwork.OptionUseDefaultSandbox()) sboxOptions = append(sboxOptions, libnetwork.OptionOriginHostsPath("/etc/hosts")) - sboxOptions = append(sboxOptions, libnetwork.OptionOriginResolvConfPath("/etc/resolv.conf")) + if len(container.HostConfig.DNS) == 0 && len(daemon.configStore.DNS) == 0 && + len(container.HostConfig.DNSSearch) == 0 && len(daemon.configStore.DNSSearch) == 0 && + len(container.HostConfig.DNSOptions) == 0 && len(daemon.configStore.DNSOptions) == 0 { + sboxOptions = append(sboxOptions, libnetwork.OptionOriginResolvConfPath("/etc/resolv.conf")) + } } else { // OptionUseExternalKey is mandatory for userns support. // But optional for non-userns support diff --git a/docs/reference/run.md b/docs/reference/run.md index edf1f023e5..9fd6d05f43 100644 --- a/docs/reference/run.md +++ b/docs/reference/run.md @@ -382,11 +382,14 @@ name, they must be linked. With the network set to `host` a container will share the host's network stack and all interfaces from the host will be available to the container. The container's hostname will match the hostname on the host -system. Note that `--add-host` `--dns` `--dns-search` -`--dns-opt` and `--mac-address` are invalid in `host` netmode. Even in `host` +system. Note that `--add-host` and `--mac-address` are invalid in `host` netmode. Even in `host` network mode a container has its own UTS namespace by default. As such `--hostname` is allowed in `host` network mode and will only change the hostname inside the container. +Note also that `--dns`, `--dns-search` and `--dns-opt` are +valid in `host` mode and `/etc/resolv.conf` will be updated accordingly. However, the +update in `/etc/resolv.conf` only happens inside the container. No change will be +made for `/etc/resolv.conf` in host. Compared to the default `bridge` mode, the `host` mode gives *significantly* better networking performance since it uses the host's native networking stack diff --git a/integration-cli/docker_cli_daemon_test.go b/integration-cli/docker_cli_daemon_test.go index f4a352ec06..0fec878900 100644 --- a/integration-cli/docker_cli_daemon_test.go +++ b/integration-cli/docker_cli_daemon_test.go @@ -2332,3 +2332,39 @@ func (s *DockerDaemonSuite) TestBuildOnDisabledBridgeNetworkDaemon(c *check.C) { c.Assert(err, check.IsNil, comment) c.Assert(code, check.Equals, 0, comment) } + +// Test case for #21976 +func (s *DockerDaemonSuite) TestDaemonDnsInHostMode(c *check.C) { + testRequires(c, SameHostDaemon, DaemonIsLinux) + + err := s.d.StartWithBusybox("--dns", "1.2.3.4") + c.Assert(err, checker.IsNil) + + expectedOutput := "nameserver 1.2.3.4" + out, _ := s.d.Cmd("run", "--net=host", "busybox", "cat", "/etc/resolv.conf") + c.Assert(out, checker.Contains, expectedOutput, check.Commentf("Expected '%s', but got %q", expectedOutput, out)) +} + +// Test case for #21976 +func (s *DockerDaemonSuite) TestDaemonDnsSearchInHostMode(c *check.C) { + testRequires(c, SameHostDaemon, DaemonIsLinux) + + err := s.d.StartWithBusybox("--dns-search", "example.com") + c.Assert(err, checker.IsNil) + + expectedOutput := "search example.com" + out, _ := s.d.Cmd("run", "--net=host", "busybox", "cat", "/etc/resolv.conf") + c.Assert(out, checker.Contains, expectedOutput, check.Commentf("Expected '%s', but got %q", expectedOutput, out)) +} + +// Test case for #21976 +func (s *DockerDaemonSuite) TestDaemonDnsOptionsInHostMode(c *check.C) { + testRequires(c, SameHostDaemon, DaemonIsLinux) + + err := s.d.StartWithBusybox("--dns-opt", "timeout:3") + c.Assert(err, checker.IsNil) + + expectedOutput := "options timeout:3" + out, _ := s.d.Cmd("run", "--net=host", "busybox", "cat", "/etc/resolv.conf") + c.Assert(out, checker.Contains, expectedOutput, check.Commentf("Expected '%s', but got %q", expectedOutput, out)) +} diff --git a/integration-cli/docker_cli_netmode_test.go b/integration-cli/docker_cli_netmode_test.go index 8284869874..62d4198e8d 100644 --- a/integration-cli/docker_cli_netmode_test.go +++ b/integration-cli/docker_cli_netmode_test.go @@ -67,10 +67,7 @@ func (s *DockerSuite) TestConflictContainerNetworkHostAndLinks(c *check.C) { func (s *DockerSuite) TestConflictNetworkModeNetHostAndOptions(c *check.C) { testRequires(c, DaemonIsLinux, NotUserNamespace) - out, _ := dockerCmdWithFail(c, "run", "--net=host", "--dns=8.8.8.8", "busybox", "ps") - c.Assert(out, checker.Contains, runconfig.ErrConflictNetworkAndDNS.Error()) - - out, _ = dockerCmdWithFail(c, "run", "--net=host", "--add-host=name:8.8.8.8", "busybox", "ps") + out, _ := dockerCmdWithFail(c, "run", "--net=host", "--add-host=name:8.8.8.8", "busybox", "ps") c.Assert(out, checker.Contains, runconfig.ErrConflictNetworkHosts.Error()) out, _ = dockerCmdWithFail(c, "run", "--net=host", "--mac-address=92:d0:c6:0a:29:33", "busybox", "ps") diff --git a/integration-cli/docker_cli_run_test.go b/integration-cli/docker_cli_run_test.go index 42f9a7b9f9..a001a0eb5c 100644 --- a/integration-cli/docker_cli_run_test.go +++ b/integration-cli/docker_cli_run_test.go @@ -4370,3 +4370,34 @@ func (s *DockerSuite) TestRunTooLongHostname(c *check.C) { } } + +// Test case for #21976 +func (s *DockerSuite) TestRunDnsInHostMode(c *check.C) { + testRequires(c, DaemonIsLinux, NotUserNamespace) + + expectedOutput := "nameserver 127.0.0.1" + expectedWarning := "Localhost DNS setting" + out, stderr, _ := dockerCmdWithStdoutStderr(c, "run", "--dns=127.0.0.1", "--net=host", "busybox", "cat", "/etc/resolv.conf") + c.Assert(out, checker.Contains, expectedOutput, check.Commentf("Expected '%s', but got %q", expectedOutput, out)) + c.Assert(stderr, checker.Contains, expectedWarning, check.Commentf("Expected warning on stderr about localhost resolver, but got %q", stderr)) + + expectedOutput = "nameserver 1.2.3.4" + out, _ = dockerCmd(c, "run", "--dns=1.2.3.4", "--net=host", "busybox", "cat", "/etc/resolv.conf") + c.Assert(out, checker.Contains, expectedOutput, check.Commentf("Expected '%s', but got %q", expectedOutput, out)) + + expectedOutput = "search example.com" + out, _ = dockerCmd(c, "run", "--dns-search=example.com", "--net=host", "busybox", "cat", "/etc/resolv.conf") + c.Assert(out, checker.Contains, expectedOutput, check.Commentf("Expected '%s', but got %q", expectedOutput, out)) + + expectedOutput = "options timeout:3" + out, _ = dockerCmd(c, "run", "--dns-opt=timeout:3", "--net=host", "busybox", "cat", "/etc/resolv.conf") + c.Assert(out, checker.Contains, expectedOutput, check.Commentf("Expected '%s', but got %q", expectedOutput, out)) + + expectedOutput1 := "nameserver 1.2.3.4" + expectedOutput2 := "search example.com" + expectedOutput3 := "options timeout:3" + out, _ = dockerCmd(c, "run", "--dns=1.2.3.4", "--dns-search=example.com", "--dns-opt=timeout:3", "--net=host", "busybox", "cat", "/etc/resolv.conf") + c.Assert(out, checker.Contains, expectedOutput1, check.Commentf("Expected '%s', but got %q", expectedOutput1, out)) + c.Assert(out, checker.Contains, expectedOutput2, check.Commentf("Expected '%s', but got %q", expectedOutput2, out)) + c.Assert(out, checker.Contains, expectedOutput3, check.Commentf("Expected '%s', but got %q", expectedOutput3, out)) +} diff --git a/runconfig/hostconfig_unix.go b/runconfig/hostconfig_unix.go index 9f5e289e2c..a4860c4da3 100644 --- a/runconfig/hostconfig_unix.go +++ b/runconfig/hostconfig_unix.go @@ -52,7 +52,7 @@ func ValidateNetMode(c *container.Config, hc *container.HostConfig) error { return ErrConflictContainerNetworkAndLinks } - if (hc.NetworkMode.IsHost() || hc.NetworkMode.IsContainer()) && len(hc.DNS) > 0 { + if hc.NetworkMode.IsContainer() && len(hc.DNS) > 0 { return ErrConflictNetworkAndDNS }