diff --git a/daemon/execdriver/driver.go b/daemon/execdriver/driver.go index 94cb1632ab..e725777dae 100644 --- a/daemon/execdriver/driver.go +++ b/daemon/execdriver/driver.go @@ -78,6 +78,7 @@ type Mount struct { Destination string `json:"destination"` Writable bool `json:"writable"` Private bool `json:"private"` + Slave bool `json:"slave"` } // Describes a process that will be run inside a container. diff --git a/daemon/execdriver/native/create.go b/daemon/execdriver/native/create.go index 8f40407c06..7aee2d6cea 100644 --- a/daemon/execdriver/native/create.go +++ b/daemon/execdriver/native/create.go @@ -167,6 +167,7 @@ func (d *driver) setupMounts(container *libcontainer.Config, c *execdriver.Comma Destination: m.Destination, Writable: m.Writable, Private: m.Private, + Slave: m.Slave, }) } diff --git a/daemon/volumes.go b/daemon/volumes.go index 85836f1be6..6486a62b0b 100644 --- a/daemon/volumes.go +++ b/daemon/volumes.go @@ -43,15 +43,30 @@ func prepareVolumesForContainer(container *Container) error { func setupMountsForContainer(container *Container) error { mounts := []execdriver.Mount{ - {container.ResolvConfPath, "/etc/resolv.conf", true, true}, + { + Source: container.ResolvConfPath, + Destination: "/etc/resolv.conf", + Writable: true, + Slave: true, + }, } if container.HostnamePath != "" { - mounts = append(mounts, execdriver.Mount{container.HostnamePath, "/etc/hostname", true, true}) + mounts = append(mounts, execdriver.Mount{ + Source: container.HostnamePath, + Destination: "/etc/hostname", + Writable: true, + Private: true, + }) } if container.HostsPath != "" { - mounts = append(mounts, execdriver.Mount{container.HostsPath, "/etc/hosts", true, true}) + mounts = append(mounts, execdriver.Mount{ + Source: container.HostsPath, + Destination: "/etc/hosts", + Writable: true, + Slave: true, + }) } // Mount user specified volumes @@ -59,7 +74,11 @@ func setupMountsForContainer(container *Container) error { // volumes. For instance if you use -v /usr:/usr and the host later mounts /usr/share you // want this new mount in the container for r, v := range container.Volumes { - mounts = append(mounts, execdriver.Mount{v, r, container.VolumesRW[r], false}) + mounts = append(mounts, execdriver.Mount{ + Source: v, + Destination: r, + Writable: container.VolumesRW[r], + }) } container.command.Mounts = mounts diff --git a/integration-cli/docker_cli_run_test.go b/integration-cli/docker_cli_run_test.go index 21aa383f40..f48152bf50 100644 --- a/integration-cli/docker_cli_run_test.go +++ b/integration-cli/docker_cli_run_test.go @@ -1749,27 +1749,103 @@ func TestBindMounts(t *testing.T) { logDone("run - bind mounts") } -func TestHostsLinkedContainerUpdate(t *testing.T) { - tmpdir, err := ioutil.TempDir("", "docker-integration") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tmpdir) +func TestMutableNetworkFiles(t *testing.T) { + defer deleteAllContainers() - out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "run", "-d", "--name", "c1", "busybox", "sleep", "5")) + for _, fn := range []string{"resolv.conf", "hosts"} { + deleteAllContainers() + + out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "run", "-d", "--name", "c1", "busybox", "sh", "-c", fmt.Sprintf("echo success >/etc/%s; while true; do sleep 1; done", fn))) + if err != nil { + t.Fatal(err, out) + } + + time.Sleep(1 * time.Second) + + contID := strings.TrimSpace(out) + + f, err := os.Open(filepath.Join("/var/lib/docker/containers", contID, fn)) + if err != nil { + t.Fatal(err) + } + + content, err := ioutil.ReadAll(f) + f.Close() + + if strings.TrimSpace(string(content)) != "success" { + t.Fatal("Content was not what was modified in the container", string(content)) + } + + out, _, err = runCommandWithOutput(exec.Command(dockerBinary, "run", "-d", "--name", "c2", "busybox", "sh", "-c", fmt.Sprintf("while true; do cat /etc/%s; sleep 1; done", fn))) + if err != nil { + t.Fatal(err) + } + + contID = strings.TrimSpace(out) + + resolvConfPath := filepath.Join("/var/lib/docker/containers", contID, fn) + + f, err = os.OpenFile(resolvConfPath, os.O_WRONLY|os.O_SYNC|os.O_APPEND, 0644) + if err != nil { + t.Fatal(err) + } + + if _, err := f.Seek(0, 0); err != nil { + f.Close() + t.Fatal(err) + } + + if err := f.Truncate(0); err != nil { + f.Close() + t.Fatal(err) + } + + if _, err := f.Write([]byte("success2\n")); err != nil { + f.Close() + t.Fatal(err) + } + + f.Close() + + time.Sleep(2 * time.Second) // don't race sleep + + out, _, err = runCommandWithOutput(exec.Command(dockerBinary, "logs", "c2")) + if err != nil { + t.Fatal(err) + } + + lines := strings.Split(out, "\n") + if strings.TrimSpace(lines[len(lines)-2]) != "success2" { + t.Fatalf("Did not find the correct output in /etc/%s: %s %#v", fn, out, lines) + } + } +} + +func TestHostsLinkedContainerUpdate(t *testing.T) { + deleteAllContainers() + out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "run", "-d", "--name", "c1", "busybox", "sh", "-c", "while true; do sleep 1; done")) if err != nil { t.Fatal(err, out) } // TODO fix docker cp and /etc/hosts - out, _, err = runCommandWithOutput(exec.Command(dockerBinary, "run", "-d", "--link", "c1:c1", "--name", "c2", "busybox", "sh", "-c", "while true;do cp /etc/hosts /hosts; done")) + out, _, err = runCommandWithOutput(exec.Command(dockerBinary, "run", "-d", "--link", "c1:c1", "--name", "c2", "busybox", "sh", "-c", "while true;do sleep 1; done")) if err != nil { t.Fatal(err, out) } - out, _, err = runCommandWithOutput(exec.Command(dockerBinary, "cp", "c2:/hosts", tmpdir+"/1")) + contID := strings.TrimSpace(out) + + f, err := os.Open(filepath.Join("/var/lib/docker/containers", contID, "hosts")) if err != nil { - t.Fatal(err, out) + t.Fatal(err) + } + + originalContent, err := ioutil.ReadAll(f) + f.Close() + + if err != nil { + t.Fatal(err) } out, _, err = runCommandWithOutput(exec.Command(dockerBinary, "restart", "-t", "0", "c1")) @@ -1777,17 +1853,19 @@ func TestHostsLinkedContainerUpdate(t *testing.T) { t.Fatal(err, out) } - out, _, err = runCommandWithOutput(exec.Command(dockerBinary, "cp", "c2:/hosts", tmpdir+"/2")) + f, err = os.Open(filepath.Join("/var/lib/docker/containers", contID, "hosts")) if err != nil { - t.Fatal(err, out) + t.Fatal(err) } - out, _, _, err = runCommandWithStdoutStderr(exec.Command("diff", tmpdir+"/1", tmpdir+"/2")) - if err == nil { - t.Fatalf("Expecting error, got none") + newContent, err := ioutil.ReadAll(f) + f.Close() + + if err != nil { + t.Fatal(err) } - out = stripTrailingCharacters(out) - if out == "" { + + if strings.TrimSpace(string(originalContent)) == strings.TrimSpace(string(newContent)) { t.Fatalf("expected /etc/hosts to be updated, but wasn't") }