Browse code

Add some unit and integration tests

Add a unit test and couple of integration tests for volume propagation.

Signed-off-by: Vivek Goyal <vgoyal@redhat.com>

Vivek Goyal authored on 2015/12/15 00:39:53
Showing 2 changed files
... ...
@@ -19,6 +19,7 @@ import (
19 19
 	"time"
20 20
 
21 21
 	"github.com/docker/docker/pkg/integration/checker"
22
+	"github.com/docker/docker/pkg/mount"
22 23
 	"github.com/docker/docker/pkg/nat"
23 24
 	"github.com/docker/docker/runconfig"
24 25
 	"github.com/docker/libnetwork/resolvconf"
... ...
@@ -3812,3 +3813,114 @@ func (s *DockerSuite) TestRunWithOomScoreAdjInvalidRange(c *check.C) {
3812 3812
 		c.Fatalf("Expected output to contain %q, got %q instead", expected, out)
3813 3813
 	}
3814 3814
 }
3815
+
3816
+func (s *DockerSuite) TestRunVolumesMountedAsShared(c *check.C) {
3817
+	// Volume propagation is linux only. Also it creates directories for
3818
+	// bind mounting, so needs to be same host.
3819
+	testRequires(c, DaemonIsLinux, SameHostDaemon, NotUserNamespace)
3820
+
3821
+	// Prepare a source directory to bind mount
3822
+	tmpDir, err := ioutil.TempDir("", "volume-source")
3823
+	if err != nil {
3824
+		c.Fatal(err)
3825
+	}
3826
+	defer os.RemoveAll(tmpDir)
3827
+
3828
+	if err := os.Mkdir(path.Join(tmpDir, "mnt1"), 0755); err != nil {
3829
+		c.Fatal(err)
3830
+	}
3831
+
3832
+	// Convert this directory into a shared mount point so that we do
3833
+	// not rely on propagation properties of parent mount.
3834
+	cmd := exec.Command("mount", "--bind", tmpDir, tmpDir)
3835
+	if _, err = runCommand(cmd); err != nil {
3836
+		c.Fatal(err)
3837
+	}
3838
+
3839
+	cmd = exec.Command("mount", "--make-private", "--make-shared", tmpDir)
3840
+	if _, err = runCommand(cmd); err != nil {
3841
+		c.Fatal(err)
3842
+	}
3843
+
3844
+	dockerCmd(c, "run", "--privileged", "-v", fmt.Sprintf("%s:/volume-dest:shared", tmpDir), "busybox", "mount", "--bind", "/volume-dest/mnt1", "/volume-dest/mnt1")
3845
+
3846
+	// Make sure a bind mount under a shared volume propagated to host.
3847
+	if mounted, _ := mount.Mounted(path.Join(tmpDir, "mnt1")); !mounted {
3848
+		c.Fatalf("Bind mount under shared volume did not propagate to host")
3849
+	}
3850
+
3851
+	mount.Unmount(path.Join(tmpDir, "mnt1"))
3852
+}
3853
+
3854
+func (s *DockerSuite) TestRunVolumesMountedAsSlave(c *check.C) {
3855
+	// Volume propagation is linux only. Also it creates directories for
3856
+	// bind mounting, so needs to be same host.
3857
+	testRequires(c, DaemonIsLinux, SameHostDaemon, NotUserNamespace)
3858
+
3859
+	// Prepare a source directory to bind mount
3860
+	tmpDir, err := ioutil.TempDir("", "volume-source")
3861
+	if err != nil {
3862
+		c.Fatal(err)
3863
+	}
3864
+	defer os.RemoveAll(tmpDir)
3865
+
3866
+	if err := os.Mkdir(path.Join(tmpDir, "mnt1"), 0755); err != nil {
3867
+		c.Fatal(err)
3868
+	}
3869
+
3870
+	// Prepare a source directory with file in it. We will bind mount this
3871
+	// direcotry and see if file shows up.
3872
+	tmpDir2, err := ioutil.TempDir("", "volume-source2")
3873
+	if err != nil {
3874
+		c.Fatal(err)
3875
+	}
3876
+	defer os.RemoveAll(tmpDir2)
3877
+
3878
+	if err := ioutil.WriteFile(path.Join(tmpDir2, "slave-testfile"), []byte("Test"), 0644); err != nil {
3879
+		c.Fatal(err)
3880
+	}
3881
+
3882
+	// Convert this directory into a shared mount point so that we do
3883
+	// not rely on propagation properties of parent mount.
3884
+	cmd := exec.Command("mount", "--bind", tmpDir, tmpDir)
3885
+	if _, err = runCommand(cmd); err != nil {
3886
+		c.Fatal(err)
3887
+	}
3888
+
3889
+	cmd = exec.Command("mount", "--make-private", "--make-shared", tmpDir)
3890
+	if _, err = runCommand(cmd); err != nil {
3891
+		c.Fatal(err)
3892
+	}
3893
+
3894
+	dockerCmd(c, "run", "-i", "-d", "--name", "parent", "-v", fmt.Sprintf("%s:/volume-dest:slave", tmpDir), "busybox", "top")
3895
+
3896
+	// Bind mount tmpDir2/ onto tmpDir/mnt1. If mount propagates inside
3897
+	// container then contents of tmpDir2/slave-testfile should become
3898
+	// visible at "/volume-dest/mnt1/slave-testfile"
3899
+	cmd = exec.Command("mount", "--bind", tmpDir2, path.Join(tmpDir, "mnt1"))
3900
+	if _, err = runCommand(cmd); err != nil {
3901
+		c.Fatal(err)
3902
+	}
3903
+
3904
+	out, _ := dockerCmd(c, "exec", "parent", "cat", "/volume-dest/mnt1/slave-testfile")
3905
+
3906
+	mount.Unmount(path.Join(tmpDir, "mnt1"))
3907
+
3908
+	if out != "Test" {
3909
+		c.Fatalf("Bind mount under slave volume did not propagate to container")
3910
+	}
3911
+}
3912
+
3913
+func (s *DockerSuite) TestRunNamedVolumesMountedAsShared(c *check.C) {
3914
+	testRequires(c, DaemonIsLinux, NotUserNamespace)
3915
+	out, exitcode, _ := dockerCmdWithError("run", "-v", "foo:/test:shared", "busybox", "touch", "/test/somefile")
3916
+
3917
+	if exitcode == 0 {
3918
+		c.Fatalf("expected non-zero exit code; received %d", exitcode)
3919
+	}
3920
+
3921
+	if expected := "Invalid volume specification"; !strings.Contains(out, expected) {
3922
+		c.Fatalf(`Expected %q in output; got: %s`, expected, out)
3923
+	}
3924
+
3925
+}
3815 3926
new file mode 100644
... ...
@@ -0,0 +1,65 @@
0
+// +build linux
1
+
2
+package volume
3
+
4
+import (
5
+	"strings"
6
+	"testing"
7
+)
8
+
9
+func TestParseMountSpecPropagation(t *testing.T) {
10
+	var (
11
+		valid   []string
12
+		invalid map[string]string
13
+	)
14
+
15
+	valid = []string{
16
+		"/hostPath:/containerPath:shared",
17
+		"/hostPath:/containerPath:rshared",
18
+		"/hostPath:/containerPath:slave",
19
+		"/hostPath:/containerPath:rslave",
20
+		"/hostPath:/containerPath:private",
21
+		"/hostPath:/containerPath:rprivate",
22
+		"/hostPath:/containerPath:ro,shared",
23
+		"/hostPath:/containerPath:ro,slave",
24
+		"/hostPath:/containerPath:ro,private",
25
+		"/hostPath:/containerPath:ro,z,shared",
26
+		"/hostPath:/containerPath:ro,Z,slave",
27
+		"/hostPath:/containerPath:Z,ro,slave",
28
+		"/hostPath:/containerPath:slave,Z,ro",
29
+		"/hostPath:/containerPath:Z,slave,ro",
30
+		"/hostPath:/containerPath:slave,ro,Z",
31
+		"/hostPath:/containerPath:rslave,ro,Z",
32
+		"/hostPath:/containerPath:ro,rshared,Z",
33
+		"/hostPath:/containerPath:ro,Z,rprivate",
34
+	}
35
+	invalid = map[string]string{
36
+		"/path:/path:ro,rshared,rslave":   `invalid mode: "ro,rshared,rslave"`,
37
+		"/path:/path:ro,z,rshared,rslave": `invalid mode: "ro,z,rshared,rslave"`,
38
+		"/path:shared":                    "Invalid volume specification",
39
+		"/path:slave":                     "Invalid volume specification",
40
+		"/path:private":                   "Invalid volume specification",
41
+		"name:/absolute-path:shared":      "Invalid volume specification",
42
+		"name:/absolute-path:rshared":     "Invalid volume specification",
43
+		"name:/absolute-path:slave":       "Invalid volume specification",
44
+		"name:/absolute-path:rslave":      "Invalid volume specification",
45
+		"name:/absolute-path:private":     "Invalid volume specification",
46
+		"name:/absolute-path:rprivate":    "Invalid volume specification",
47
+	}
48
+
49
+	for _, path := range valid {
50
+		if _, err := ParseMountSpec(path, "local"); err != nil {
51
+			t.Fatalf("ParseMountSpec(`%q`) should succeed: error %q", path, err)
52
+		}
53
+	}
54
+
55
+	for path, expectedError := range invalid {
56
+		if _, err := ParseMountSpec(path, "local"); err == nil {
57
+			t.Fatalf("ParseMountSpec(`%q`) should have failed validation. Err %v", path, err)
58
+		} else {
59
+			if !strings.Contains(err.Error(), expectedError) {
60
+				t.Fatalf("ParseMountSpec(`%q`) error should contain %q, got %v", path, expectedError, err.Error())
61
+			}
62
+		}
63
+	}
64
+}