diff --git a/daemon/container.go b/daemon/container.go index f359adfe0a..df6bd66190 100644 --- a/daemon/container.go +++ b/daemon/container.go @@ -849,18 +849,16 @@ func (container *Container) setupContainerDns() error { daemon = container.daemon ) - if config.NetworkMode == "host" { - container.ResolvConfPath = "/etc/resolv.conf" - return nil - } - resolvConf, err := resolvconf.Get() if err != nil { return err } + container.ResolvConfPath, err = container.getRootResourcePath("resolv.conf") + if err != nil { + return err + } - // If custom dns exists, then create a resolv.conf for the container - if len(config.Dns) > 0 || len(daemon.config.Dns) > 0 || len(config.DnsSearch) > 0 || len(daemon.config.DnsSearch) > 0 { + if config.NetworkMode != "host" && (len(config.Dns) > 0 || len(daemon.config.Dns) > 0 || len(config.DnsSearch) > 0 || len(daemon.config.DnsSearch) > 0) { var ( dns = resolvconf.GetNameservers(resolvConf) dnsSearch = resolvconf.GetSearchDomains(resolvConf) @@ -875,18 +873,9 @@ func (container *Container) setupContainerDns() error { } else if len(daemon.config.DnsSearch) > 0 { dnsSearch = daemon.config.DnsSearch } - - resolvConfPath, err := container.getRootResourcePath("resolv.conf") - if err != nil { - return err - } - container.ResolvConfPath = resolvConfPath - return resolvconf.Build(container.ResolvConfPath, dns, dnsSearch) - } else { - container.ResolvConfPath = "/etc/resolv.conf" } - return nil + return ioutil.WriteFile(container.ResolvConfPath, resolvConf, 0644) } func (container *Container) initializeNetworking() error { diff --git a/daemon/volumes.go b/daemon/volumes.go index ea18a62b8c..b60118c953 100644 --- a/daemon/volumes.go +++ b/daemon/volumes.go @@ -50,15 +50,15 @@ func prepareVolumesForContainer(container *Container) error { func setupMountsForContainer(container *Container) error { mounts := []execdriver.Mount{ - {container.ResolvConfPath, "/etc/resolv.conf", false, true}, + {container.ResolvConfPath, "/etc/resolv.conf", true, true}, } if container.HostnamePath != "" { - mounts = append(mounts, execdriver.Mount{container.HostnamePath, "/etc/hostname", false, true}) + mounts = append(mounts, execdriver.Mount{container.HostnamePath, "/etc/hostname", true, true}) } if container.HostsPath != "" { - mounts = append(mounts, execdriver.Mount{container.HostsPath, "/etc/hosts", false, true}) + mounts = append(mounts, execdriver.Mount{container.HostsPath, "/etc/hosts", true, true}) } // Mount user specified volumes diff --git a/docs/sources/articles/networking.md b/docs/sources/articles/networking.md index e465429cdb..f9aa2d26d3 100644 --- a/docs/sources/articles/networking.md +++ b/docs/sources/articles/networking.md @@ -731,3 +731,14 @@ usual containers. But unless you have very specific networking needs that drive you to such a solution, it is probably far preferable to use `--icc=false` to lock down inter-container communication, as we explored earlier. + +## Editing networking config files + +Starting with Docker v.1.2.0, you can now edit `/etc/hosts`, `/etc/hostname` +and `/etc/resolve.conf` in a running container. This is useful if you need +to install bind or other services that might override one of those files. + +Note, however, that changes to these files will not be saved by +`docker commit`, nor will they be saved during `docker run`. +That means they won't be saved in the image, nor will they persist when a +container is restarted; they will only "stick" in a running container. diff --git a/integration-cli/docker_cli_run_test.go b/integration-cli/docker_cli_run_test.go index 2c065caacc..b95fd86d2b 100644 --- a/integration-cli/docker_cli_run_test.go +++ b/integration-cli/docker_cli_run_test.go @@ -1549,3 +1549,84 @@ func TestRunExitOnStdinClose(t *testing.T) { } logDone("run - exit on stdin closing") } + +// Test for #2267 +func TestWriteHostsFileAndNotCommit(t *testing.T) { + name := "writehosts" + cmd := exec.Command(dockerBinary, "run", "--name", name, "busybox", "sh", "-c", "echo test2267 >> /etc/hosts && cat /etc/hosts") + out, _, err := runCommandWithOutput(cmd) + if err != nil { + t.Fatal(err, out) + } + if !strings.Contains(out, "test2267") { + t.Fatal("/etc/hosts should contain 'test2267'") + } + + cmd = exec.Command(dockerBinary, "diff", name) + if err != nil { + t.Fatal(err, out) + } + out, _, err = runCommandWithOutput(cmd) + if err != nil { + t.Fatal(err, out) + } + if len(strings.Trim(out, "\r\n")) != 0 { + t.Fatal("diff should be empty") + } + + logDone("run - write to /etc/hosts and not commited") +} + +// Test for #2267 +func TestWriteHostnameFileAndNotCommit(t *testing.T) { + name := "writehostname" + cmd := exec.Command(dockerBinary, "run", "--name", name, "busybox", "sh", "-c", "echo test2267 >> /etc/hostname && cat /etc/hostname") + out, _, err := runCommandWithOutput(cmd) + if err != nil { + t.Fatal(err, out) + } + if !strings.Contains(out, "test2267") { + t.Fatal("/etc/hostname should contain 'test2267'") + } + + cmd = exec.Command(dockerBinary, "diff", name) + if err != nil { + t.Fatal(err, out) + } + out, _, err = runCommandWithOutput(cmd) + if err != nil { + t.Fatal(err, out) + } + if len(strings.Trim(out, "\r\n")) != 0 { + t.Fatal("diff should be empty") + } + + logDone("run - write to /etc/hostname and not commited") +} + +// Test for #2267 +func TestWriteResolvFileAndNotCommit(t *testing.T) { + name := "writeresolv" + cmd := exec.Command(dockerBinary, "run", "--name", name, "busybox", "sh", "-c", "echo test2267 >> /etc/resolv.conf && cat /etc/resolv.conf") + out, _, err := runCommandWithOutput(cmd) + if err != nil { + t.Fatal(err, out) + } + if !strings.Contains(out, "test2267") { + t.Fatal("/etc/resolv.conf should contain 'test2267'") + } + + cmd = exec.Command(dockerBinary, "diff", name) + if err != nil { + t.Fatal(err, out) + } + out, _, err = runCommandWithOutput(cmd) + if err != nil { + t.Fatal(err, out) + } + if len(strings.Trim(out, "\r\n")) != 0 { + t.Fatal("diff should be empty") + } + + logDone("run - write to /etc/resolv.conf and not commited") +}