Browse code

Allow /etc/hosts and /etc/resolv.conf to be updated both outside and inside the container.

Docker-DCO-1.1-Signed-off-by: Erik Hollensbe <github@hollensbe.org> (github: erikh)

Erik Hollensbe authored on 2014/09/14 01:42:10
Showing 4 changed files
... ...
@@ -78,6 +78,7 @@ type Mount struct {
78 78
 	Destination string `json:"destination"`
79 79
 	Writable    bool   `json:"writable"`
80 80
 	Private     bool   `json:"private"`
81
+	Slave       bool   `json:"slave"`
81 82
 }
82 83
 
83 84
 // Describes a process that will be run inside a container.
... ...
@@ -167,6 +167,7 @@ func (d *driver) setupMounts(container *libcontainer.Config, c *execdriver.Comma
167 167
 			Destination: m.Destination,
168 168
 			Writable:    m.Writable,
169 169
 			Private:     m.Private,
170
+			Slave:       m.Slave,
170 171
 		})
171 172
 	}
172 173
 
... ...
@@ -43,15 +43,30 @@ func prepareVolumesForContainer(container *Container) error {
43 43
 
44 44
 func setupMountsForContainer(container *Container) error {
45 45
 	mounts := []execdriver.Mount{
46
-		{container.ResolvConfPath, "/etc/resolv.conf", true, true},
46
+		{
47
+			Source:      container.ResolvConfPath,
48
+			Destination: "/etc/resolv.conf",
49
+			Writable:    true,
50
+			Slave:       true,
51
+		},
47 52
 	}
48 53
 
49 54
 	if container.HostnamePath != "" {
50
-		mounts = append(mounts, execdriver.Mount{container.HostnamePath, "/etc/hostname", true, true})
55
+		mounts = append(mounts, execdriver.Mount{
56
+			Source:      container.HostnamePath,
57
+			Destination: "/etc/hostname",
58
+			Writable:    true,
59
+			Private:     true,
60
+		})
51 61
 	}
52 62
 
53 63
 	if container.HostsPath != "" {
54
-		mounts = append(mounts, execdriver.Mount{container.HostsPath, "/etc/hosts", true, true})
64
+		mounts = append(mounts, execdriver.Mount{
65
+			Source:      container.HostsPath,
66
+			Destination: "/etc/hosts",
67
+			Writable:    true,
68
+			Slave:       true,
69
+		})
55 70
 	}
56 71
 
57 72
 	// Mount user specified volumes
... ...
@@ -59,7 +74,11 @@ func setupMountsForContainer(container *Container) error {
59 59
 	// volumes. For instance if you use -v /usr:/usr and the host later mounts /usr/share you
60 60
 	// want this new mount in the container
61 61
 	for r, v := range container.Volumes {
62
-		mounts = append(mounts, execdriver.Mount{v, r, container.VolumesRW[r], false})
62
+		mounts = append(mounts, execdriver.Mount{
63
+			Source:      v,
64
+			Destination: r,
65
+			Writable:    container.VolumesRW[r],
66
+		})
63 67
 	}
64 68
 
65 69
 	container.command.Mounts = mounts
... ...
@@ -1749,27 +1749,103 @@ func TestBindMounts(t *testing.T) {
1749 1749
 	logDone("run - bind mounts")
1750 1750
 }
1751 1751
 
1752
-func TestHostsLinkedContainerUpdate(t *testing.T) {
1753
-	tmpdir, err := ioutil.TempDir("", "docker-integration")
1754
-	if err != nil {
1755
-		t.Fatal(err)
1752
+func TestMutableNetworkFiles(t *testing.T) {
1753
+	defer deleteAllContainers()
1754
+
1755
+	for _, fn := range []string{"resolv.conf", "hosts"} {
1756
+		deleteAllContainers()
1757
+
1758
+		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)))
1759
+		if err != nil {
1760
+			t.Fatal(err, out)
1761
+		}
1762
+
1763
+		time.Sleep(1 * time.Second)
1764
+
1765
+		contID := strings.TrimSpace(out)
1766
+
1767
+		f, err := os.Open(filepath.Join("/var/lib/docker/containers", contID, fn))
1768
+		if err != nil {
1769
+			t.Fatal(err)
1770
+		}
1771
+
1772
+		content, err := ioutil.ReadAll(f)
1773
+		f.Close()
1774
+
1775
+		if strings.TrimSpace(string(content)) != "success" {
1776
+			t.Fatal("Content was not what was modified in the container", string(content))
1777
+		}
1778
+
1779
+		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)))
1780
+		if err != nil {
1781
+			t.Fatal(err)
1782
+		}
1783
+
1784
+		contID = strings.TrimSpace(out)
1785
+
1786
+		resolvConfPath := filepath.Join("/var/lib/docker/containers", contID, fn)
1787
+
1788
+		f, err = os.OpenFile(resolvConfPath, os.O_WRONLY|os.O_SYNC|os.O_APPEND, 0644)
1789
+		if err != nil {
1790
+			t.Fatal(err)
1791
+		}
1792
+
1793
+		if _, err := f.Seek(0, 0); err != nil {
1794
+			f.Close()
1795
+			t.Fatal(err)
1796
+		}
1797
+
1798
+		if err := f.Truncate(0); err != nil {
1799
+			f.Close()
1800
+			t.Fatal(err)
1801
+		}
1802
+
1803
+		if _, err := f.Write([]byte("success2\n")); err != nil {
1804
+			f.Close()
1805
+			t.Fatal(err)
1806
+		}
1807
+
1808
+		f.Close()
1809
+
1810
+		time.Sleep(2 * time.Second) // don't race sleep
1811
+
1812
+		out, _, err = runCommandWithOutput(exec.Command(dockerBinary, "logs", "c2"))
1813
+		if err != nil {
1814
+			t.Fatal(err)
1815
+		}
1816
+
1817
+		lines := strings.Split(out, "\n")
1818
+		if strings.TrimSpace(lines[len(lines)-2]) != "success2" {
1819
+			t.Fatalf("Did not find the correct output in /etc/%s: %s %#v", fn, out, lines)
1820
+		}
1756 1821
 	}
1757
-	defer os.RemoveAll(tmpdir)
1822
+}
1758 1823
 
1759
-	out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "run", "-d", "--name", "c1", "busybox", "sleep", "5"))
1824
+func TestHostsLinkedContainerUpdate(t *testing.T) {
1825
+	deleteAllContainers()
1826
+	out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "run", "-d", "--name", "c1", "busybox", "sh", "-c", "while true; do sleep 1; done"))
1760 1827
 	if err != nil {
1761 1828
 		t.Fatal(err, out)
1762 1829
 	}
1763 1830
 
1764 1831
 	// TODO fix docker cp and /etc/hosts
1765
-	out, _, err = runCommandWithOutput(exec.Command(dockerBinary, "run", "-d", "--link", "c1:c1", "--name", "c2", "busybox", "sh", "-c", "while true;do cp /etc/hosts /hosts; done"))
1832
+	out, _, err = runCommandWithOutput(exec.Command(dockerBinary, "run", "-d", "--link", "c1:c1", "--name", "c2", "busybox", "sh", "-c", "while true;do sleep 1; done"))
1766 1833
 	if err != nil {
1767 1834
 		t.Fatal(err, out)
1768 1835
 	}
1769 1836
 
1770
-	out, _, err = runCommandWithOutput(exec.Command(dockerBinary, "cp", "c2:/hosts", tmpdir+"/1"))
1837
+	contID := strings.TrimSpace(out)
1838
+
1839
+	f, err := os.Open(filepath.Join("/var/lib/docker/containers", contID, "hosts"))
1771 1840
 	if err != nil {
1772
-		t.Fatal(err, out)
1841
+		t.Fatal(err)
1842
+	}
1843
+
1844
+	originalContent, err := ioutil.ReadAll(f)
1845
+	f.Close()
1846
+
1847
+	if err != nil {
1848
+		t.Fatal(err)
1773 1849
 	}
1774 1850
 
1775 1851
 	out, _, err = runCommandWithOutput(exec.Command(dockerBinary, "restart", "-t", "0", "c1"))
... ...
@@ -1777,17 +1853,19 @@ func TestHostsLinkedContainerUpdate(t *testing.T) {
1777 1777
 		t.Fatal(err, out)
1778 1778
 	}
1779 1779
 
1780
-	out, _, err = runCommandWithOutput(exec.Command(dockerBinary, "cp", "c2:/hosts", tmpdir+"/2"))
1780
+	f, err = os.Open(filepath.Join("/var/lib/docker/containers", contID, "hosts"))
1781 1781
 	if err != nil {
1782
-		t.Fatal(err, out)
1782
+		t.Fatal(err)
1783 1783
 	}
1784 1784
 
1785
-	out, _, _, err = runCommandWithStdoutStderr(exec.Command("diff", tmpdir+"/1", tmpdir+"/2"))
1786
-	if err == nil {
1787
-		t.Fatalf("Expecting error, got none")
1785
+	newContent, err := ioutil.ReadAll(f)
1786
+	f.Close()
1787
+
1788
+	if err != nil {
1789
+		t.Fatal(err)
1788 1790
 	}
1789
-	out = stripTrailingCharacters(out)
1790
-	if out == "" {
1791
+
1792
+	if strings.TrimSpace(string(originalContent)) == strings.TrimSpace(string(newContent)) {
1791 1793
 		t.Fatalf("expected /etc/hosts to be updated, but wasn't")
1792 1794
 	}
1793 1795