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>
This commit is contained in:
parent
d4b4ce2588
commit
f988c98ff3
2 changed files with 177 additions and 0 deletions
|
@ -19,6 +19,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/docker/docker/pkg/integration/checker"
|
||||
"github.com/docker/docker/pkg/mount"
|
||||
"github.com/docker/docker/pkg/nat"
|
||||
"github.com/docker/docker/runconfig"
|
||||
"github.com/docker/libnetwork/resolvconf"
|
||||
|
@ -3812,3 +3813,114 @@ func (s *DockerSuite) TestRunWithOomScoreAdjInvalidRange(c *check.C) {
|
|||
c.Fatalf("Expected output to contain %q, got %q instead", expected, out)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *DockerSuite) TestRunVolumesMountedAsShared(c *check.C) {
|
||||
// Volume propagation is linux only. Also it creates directories for
|
||||
// bind mounting, so needs to be same host.
|
||||
testRequires(c, DaemonIsLinux, SameHostDaemon, NotUserNamespace)
|
||||
|
||||
// Prepare a source directory to bind mount
|
||||
tmpDir, err := ioutil.TempDir("", "volume-source")
|
||||
if err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
if err := os.Mkdir(path.Join(tmpDir, "mnt1"), 0755); err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
|
||||
// Convert this directory into a shared mount point so that we do
|
||||
// not rely on propagation properties of parent mount.
|
||||
cmd := exec.Command("mount", "--bind", tmpDir, tmpDir)
|
||||
if _, err = runCommand(cmd); err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
|
||||
cmd = exec.Command("mount", "--make-private", "--make-shared", tmpDir)
|
||||
if _, err = runCommand(cmd); err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
|
||||
dockerCmd(c, "run", "--privileged", "-v", fmt.Sprintf("%s:/volume-dest:shared", tmpDir), "busybox", "mount", "--bind", "/volume-dest/mnt1", "/volume-dest/mnt1")
|
||||
|
||||
// Make sure a bind mount under a shared volume propagated to host.
|
||||
if mounted, _ := mount.Mounted(path.Join(tmpDir, "mnt1")); !mounted {
|
||||
c.Fatalf("Bind mount under shared volume did not propagate to host")
|
||||
}
|
||||
|
||||
mount.Unmount(path.Join(tmpDir, "mnt1"))
|
||||
}
|
||||
|
||||
func (s *DockerSuite) TestRunVolumesMountedAsSlave(c *check.C) {
|
||||
// Volume propagation is linux only. Also it creates directories for
|
||||
// bind mounting, so needs to be same host.
|
||||
testRequires(c, DaemonIsLinux, SameHostDaemon, NotUserNamespace)
|
||||
|
||||
// Prepare a source directory to bind mount
|
||||
tmpDir, err := ioutil.TempDir("", "volume-source")
|
||||
if err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
if err := os.Mkdir(path.Join(tmpDir, "mnt1"), 0755); err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
|
||||
// Prepare a source directory with file in it. We will bind mount this
|
||||
// direcotry and see if file shows up.
|
||||
tmpDir2, err := ioutil.TempDir("", "volume-source2")
|
||||
if err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(tmpDir2)
|
||||
|
||||
if err := ioutil.WriteFile(path.Join(tmpDir2, "slave-testfile"), []byte("Test"), 0644); err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
|
||||
// Convert this directory into a shared mount point so that we do
|
||||
// not rely on propagation properties of parent mount.
|
||||
cmd := exec.Command("mount", "--bind", tmpDir, tmpDir)
|
||||
if _, err = runCommand(cmd); err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
|
||||
cmd = exec.Command("mount", "--make-private", "--make-shared", tmpDir)
|
||||
if _, err = runCommand(cmd); err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
|
||||
dockerCmd(c, "run", "-i", "-d", "--name", "parent", "-v", fmt.Sprintf("%s:/volume-dest:slave", tmpDir), "busybox", "top")
|
||||
|
||||
// Bind mount tmpDir2/ onto tmpDir/mnt1. If mount propagates inside
|
||||
// container then contents of tmpDir2/slave-testfile should become
|
||||
// visible at "/volume-dest/mnt1/slave-testfile"
|
||||
cmd = exec.Command("mount", "--bind", tmpDir2, path.Join(tmpDir, "mnt1"))
|
||||
if _, err = runCommand(cmd); err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
|
||||
out, _ := dockerCmd(c, "exec", "parent", "cat", "/volume-dest/mnt1/slave-testfile")
|
||||
|
||||
mount.Unmount(path.Join(tmpDir, "mnt1"))
|
||||
|
||||
if out != "Test" {
|
||||
c.Fatalf("Bind mount under slave volume did not propagate to container")
|
||||
}
|
||||
}
|
||||
|
||||
func (s *DockerSuite) TestRunNamedVolumesMountedAsShared(c *check.C) {
|
||||
testRequires(c, DaemonIsLinux, NotUserNamespace)
|
||||
out, exitcode, _ := dockerCmdWithError("run", "-v", "foo:/test:shared", "busybox", "touch", "/test/somefile")
|
||||
|
||||
if exitcode == 0 {
|
||||
c.Fatalf("expected non-zero exit code; received %d", exitcode)
|
||||
}
|
||||
|
||||
if expected := "Invalid volume specification"; !strings.Contains(out, expected) {
|
||||
c.Fatalf(`Expected %q in output; got: %s`, expected, out)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
65
volume/volume_propagation_linux_test.go
Normal file
65
volume/volume_propagation_linux_test.go
Normal file
|
@ -0,0 +1,65 @@
|
|||
// +build linux
|
||||
|
||||
package volume
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestParseMountSpecPropagation(t *testing.T) {
|
||||
var (
|
||||
valid []string
|
||||
invalid map[string]string
|
||||
)
|
||||
|
||||
valid = []string{
|
||||
"/hostPath:/containerPath:shared",
|
||||
"/hostPath:/containerPath:rshared",
|
||||
"/hostPath:/containerPath:slave",
|
||||
"/hostPath:/containerPath:rslave",
|
||||
"/hostPath:/containerPath:private",
|
||||
"/hostPath:/containerPath:rprivate",
|
||||
"/hostPath:/containerPath:ro,shared",
|
||||
"/hostPath:/containerPath:ro,slave",
|
||||
"/hostPath:/containerPath:ro,private",
|
||||
"/hostPath:/containerPath:ro,z,shared",
|
||||
"/hostPath:/containerPath:ro,Z,slave",
|
||||
"/hostPath:/containerPath:Z,ro,slave",
|
||||
"/hostPath:/containerPath:slave,Z,ro",
|
||||
"/hostPath:/containerPath:Z,slave,ro",
|
||||
"/hostPath:/containerPath:slave,ro,Z",
|
||||
"/hostPath:/containerPath:rslave,ro,Z",
|
||||
"/hostPath:/containerPath:ro,rshared,Z",
|
||||
"/hostPath:/containerPath:ro,Z,rprivate",
|
||||
}
|
||||
invalid = map[string]string{
|
||||
"/path:/path:ro,rshared,rslave": `invalid mode: "ro,rshared,rslave"`,
|
||||
"/path:/path:ro,z,rshared,rslave": `invalid mode: "ro,z,rshared,rslave"`,
|
||||
"/path:shared": "Invalid volume specification",
|
||||
"/path:slave": "Invalid volume specification",
|
||||
"/path:private": "Invalid volume specification",
|
||||
"name:/absolute-path:shared": "Invalid volume specification",
|
||||
"name:/absolute-path:rshared": "Invalid volume specification",
|
||||
"name:/absolute-path:slave": "Invalid volume specification",
|
||||
"name:/absolute-path:rslave": "Invalid volume specification",
|
||||
"name:/absolute-path:private": "Invalid volume specification",
|
||||
"name:/absolute-path:rprivate": "Invalid volume specification",
|
||||
}
|
||||
|
||||
for _, path := range valid {
|
||||
if _, err := ParseMountSpec(path, "local"); err != nil {
|
||||
t.Fatalf("ParseMountSpec(`%q`) should succeed: error %q", path, err)
|
||||
}
|
||||
}
|
||||
|
||||
for path, expectedError := range invalid {
|
||||
if _, err := ParseMountSpec(path, "local"); err == nil {
|
||||
t.Fatalf("ParseMountSpec(`%q`) should have failed validation. Err %v", path, err)
|
||||
} else {
|
||||
if !strings.Contains(err.Error(), expectedError) {
|
||||
t.Fatalf("ParseMountSpec(`%q`) error should contain %q, got %v", path, expectedError, err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue