Browse code

Rewrite VolumesMountedAsShared/Slave as Integration tests

This moves the two tests from integration-CLI to integration.

Signed-off-by: Paul "TBBle" Hampson <Paul.Hampson@Pobox.com>

Paul "TBBle" Hampson authored on 2020/11/07 16:06:44
Showing 2 changed files
... ...
@@ -10,7 +10,6 @@ import (
10 10
 	"io/ioutil"
11 11
 	"os"
12 12
 	"os/exec"
13
-	"path"
14 13
 	"path/filepath"
15 14
 	"regexp"
16 15
 	"strconv"
... ...
@@ -27,7 +26,6 @@ import (
27 27
 	"github.com/docker/docker/pkg/parsers"
28 28
 	"github.com/docker/docker/pkg/sysinfo"
29 29
 	"github.com/moby/sys/mount"
30
-	"github.com/moby/sys/mountinfo"
31 30
 	"gotest.tools/v3/assert"
32 31
 	"gotest.tools/v3/icmd"
33 32
 )
... ...
@@ -1601,83 +1599,3 @@ func (s *DockerSuite) TestRunWithNanoCPUs(c *testing.T) {
1601 1601
 	assert.ErrorContains(c, err, "")
1602 1602
 	assert.Assert(c, strings.Contains(out, "Conflicting options: Nano CPUs and CPU Period cannot both be set"))
1603 1603
 }
1604
-
1605
-func (s *DockerSuite) TestRunVolumesMountedAsShared(c *testing.T) {
1606
-	// Volume propagation is linux only. Also it creates directories for
1607
-	// bind mounting, so needs to be same host.
1608
-	testRequires(c, DaemonIsLinux, testEnv.IsLocalDaemon, NotUserNamespace)
1609
-
1610
-	// Prepare a source directory to bind mount
1611
-	tmpDir, err := ioutil.TempDir("", "volume-source")
1612
-	if err != nil {
1613
-		c.Fatal(err)
1614
-	}
1615
-	defer os.RemoveAll(tmpDir)
1616
-
1617
-	if err := os.Mkdir(path.Join(tmpDir, "mnt1"), 0755); err != nil {
1618
-		c.Fatal(err)
1619
-	}
1620
-
1621
-	// Convert this directory into a shared mount point so that we do
1622
-	// not rely on propagation properties of parent mount.
1623
-	icmd.RunCommand("mount", "--bind", tmpDir, tmpDir).Assert(c, icmd.Success)
1624
-	icmd.RunCommand("mount", "--make-private", "--make-shared", tmpDir).Assert(c, icmd.Success)
1625
-
1626
-	dockerCmd(c, "run", "--privileged", "-v", fmt.Sprintf("%s:/volume-dest:shared", tmpDir), "busybox", "mount", "--bind", "/volume-dest/mnt1", "/volume-dest/mnt1")
1627
-
1628
-	// Make sure a bind mount under a shared volume propagated to host.
1629
-	if mounted, _ := mountinfo.Mounted(path.Join(tmpDir, "mnt1")); !mounted {
1630
-		c.Fatalf("Bind mount under shared volume did not propagate to host")
1631
-	}
1632
-
1633
-	mount.Unmount(path.Join(tmpDir, "mnt1"))
1634
-}
1635
-
1636
-func (s *DockerSuite) TestRunVolumesMountedAsSlave(c *testing.T) {
1637
-	// Volume propagation is linux only. Also it creates directories for
1638
-	// bind mounting, so needs to be same host.
1639
-	testRequires(c, DaemonIsLinux, testEnv.IsLocalDaemon, NotUserNamespace)
1640
-
1641
-	// Prepare a source directory to bind mount
1642
-	tmpDir, err := ioutil.TempDir("", "volume-source")
1643
-	if err != nil {
1644
-		c.Fatal(err)
1645
-	}
1646
-	defer os.RemoveAll(tmpDir)
1647
-
1648
-	if err := os.Mkdir(path.Join(tmpDir, "mnt1"), 0755); err != nil {
1649
-		c.Fatal(err)
1650
-	}
1651
-
1652
-	// Prepare a source directory with file in it. We will bind mount this
1653
-	// directory and see if file shows up.
1654
-	tmpDir2, err := ioutil.TempDir("", "volume-source2")
1655
-	if err != nil {
1656
-		c.Fatal(err)
1657
-	}
1658
-	defer os.RemoveAll(tmpDir2)
1659
-
1660
-	if err := ioutil.WriteFile(path.Join(tmpDir2, "slave-testfile"), []byte("Test"), 0644); err != nil {
1661
-		c.Fatal(err)
1662
-	}
1663
-
1664
-	// Convert this directory into a shared mount point so that we do
1665
-	// not rely on propagation properties of parent mount.
1666
-	icmd.RunCommand("mount", "--bind", tmpDir, tmpDir).Assert(c, icmd.Success)
1667
-	icmd.RunCommand("mount", "--make-private", "--make-shared", tmpDir).Assert(c, icmd.Success)
1668
-
1669
-	dockerCmd(c, "run", "-i", "-d", "--name", "parent", "-v", fmt.Sprintf("%s:/volume-dest:slave", tmpDir), "busybox", "top")
1670
-
1671
-	// Bind mount tmpDir2/ onto tmpDir/mnt1. If mount propagates inside
1672
-	// container then contents of tmpDir2/slave-testfile should become
1673
-	// visible at "/volume-dest/mnt1/slave-testfile"
1674
-	icmd.RunCommand("mount", "--bind", tmpDir2, path.Join(tmpDir, "mnt1")).Assert(c, icmd.Success)
1675
-
1676
-	out, _ := dockerCmd(c, "exec", "parent", "cat", "/volume-dest/mnt1/slave-testfile")
1677
-
1678
-	mount.Unmount(path.Join(tmpDir, "mnt1"))
1679
-
1680
-	if out != "Test" {
1681
-		c.Fatalf("Bind mount under slave volume did not propagate to container")
1682
-	}
1683
-}
... ...
@@ -16,6 +16,7 @@ import (
16 16
 	"github.com/docker/docker/integration/internal/container"
17 17
 	"github.com/docker/docker/pkg/system"
18 18
 	"github.com/moby/sys/mount"
19
+	"github.com/moby/sys/mountinfo"
19 20
 	"gotest.tools/v3/assert"
20 21
 	is "gotest.tools/v3/assert/cmp"
21 22
 	"gotest.tools/v3/fs"
... ...
@@ -266,3 +267,127 @@ func TestContainerBindMountNonRecursive(t *testing.T) {
266 266
 		poll.WaitOn(t, container.IsSuccessful(ctx, client, c), poll.WithDelay(100*time.Millisecond))
267 267
 	}
268 268
 }
269
+
270
+func TestContainerVolumesMountedAsShared(t *testing.T) {
271
+	// Volume propagation is linux only. Also it creates directories for
272
+	// bind mounting, so needs to be same host.
273
+	skip.If(t, testEnv.IsRemoteDaemon)
274
+	skip.If(t, testEnv.IsUserNamespace)
275
+	skip.If(t, testEnv.IsRootless, "cannot be tested because RootlessKit executes the daemon in private mount namespace (https://github.com/rootless-containers/rootlesskit/issues/97)")
276
+
277
+	defer setupTest(t)()
278
+
279
+	// Prepare a source directory to bind mount
280
+	tmpDir1 := fs.NewDir(t, "volume-source", fs.WithMode(0755),
281
+		fs.WithDir("mnt1", fs.WithMode(0755)))
282
+	defer tmpDir1.Remove()
283
+	tmpDir1Mnt := filepath.Join(tmpDir1.Path(), "mnt1")
284
+
285
+	// Convert this directory into a shared mount point so that we do
286
+	// not rely on propagation properties of parent mount.
287
+	if err := mount.Mount(tmpDir1.Path(), tmpDir1.Path(), "none", "bind,private"); err != nil {
288
+		t.Fatal(err)
289
+	}
290
+	defer func() {
291
+		if err := mount.Unmount(tmpDir1.Path()); err != nil {
292
+			t.Fatal(err)
293
+		}
294
+	}()
295
+	if err := mount.Mount("none", tmpDir1.Path(), "none", "shared"); err != nil {
296
+		t.Fatal(err)
297
+	}
298
+
299
+	sharedMount := mounttypes.Mount{
300
+		Type:   mounttypes.TypeBind,
301
+		Source: tmpDir1.Path(),
302
+		Target: "/volume-dest",
303
+		BindOptions: &mounttypes.BindOptions{
304
+			Propagation: mounttypes.PropagationShared,
305
+		},
306
+	}
307
+
308
+	bindMountCmd := []string{"mount", "--bind", "/volume-dest/mnt1", "/volume-dest/mnt1"}
309
+
310
+	ctx := context.Background()
311
+	client := testEnv.APIClient()
312
+	containerID := container.Run(ctx, t, client, container.WithPrivileged(true), container.WithMount(sharedMount), container.WithCmd(bindMountCmd...))
313
+	poll.WaitOn(t, container.IsSuccessful(ctx, client, containerID), poll.WithDelay(100*time.Millisecond))
314
+
315
+	// Make sure a bind mount under a shared volume propagated to host.
316
+	if mounted, _ := mountinfo.Mounted(tmpDir1Mnt); !mounted {
317
+		t.Fatalf("Bind mount under shared volume did not propagate to host")
318
+	}
319
+
320
+	mount.Unmount(tmpDir1Mnt)
321
+}
322
+
323
+func TestContainerVolumesMountedAsSlave(t *testing.T) {
324
+	// Volume propagation is linux only. Also it creates directories for
325
+	// bind mounting, so needs to be same host.
326
+	skip.If(t, testEnv.IsRemoteDaemon)
327
+	skip.If(t, testEnv.IsUserNamespace)
328
+	skip.If(t, testEnv.IsRootless, "cannot be tested because RootlessKit executes the daemon in private mount namespace (https://github.com/rootless-containers/rootlesskit/issues/97)")
329
+
330
+	// Prepare a source directory to bind mount
331
+	tmpDir1 := fs.NewDir(t, "volume-source", fs.WithMode(0755),
332
+		fs.WithDir("mnt1", fs.WithMode(0755)))
333
+	defer tmpDir1.Remove()
334
+	tmpDir1Mnt := filepath.Join(tmpDir1.Path(), "mnt1")
335
+
336
+	// Prepare a source directory with file in it. We will bind mount this
337
+	// directory and see if file shows up.
338
+	tmpDir2 := fs.NewDir(t, "volume-source2", fs.WithMode(0755),
339
+		fs.WithFile("slave-testfile", "Test", fs.WithMode(0644)))
340
+	defer tmpDir2.Remove()
341
+
342
+	// Convert this directory into a shared mount point so that we do
343
+	// not rely on propagation properties of parent mount.
344
+	if err := mount.Mount(tmpDir1.Path(), tmpDir1.Path(), "none", "bind,private"); err != nil {
345
+		t.Fatal(err)
346
+	}
347
+	defer func() {
348
+		if err := mount.Unmount(tmpDir1.Path()); err != nil {
349
+			t.Fatal(err)
350
+		}
351
+	}()
352
+	if err := mount.Mount("none", tmpDir1.Path(), "none", "shared"); err != nil {
353
+		t.Fatal(err)
354
+	}
355
+
356
+	slaveMount := mounttypes.Mount{
357
+		Type:   mounttypes.TypeBind,
358
+		Source: tmpDir1.Path(),
359
+		Target: "/volume-dest",
360
+		BindOptions: &mounttypes.BindOptions{
361
+			Propagation: mounttypes.PropagationSlave,
362
+		},
363
+	}
364
+
365
+	topCmd := []string{"top"}
366
+
367
+	ctx := context.Background()
368
+	client := testEnv.APIClient()
369
+	containerID := container.Run(ctx, t, client, container.WithTty(true), container.WithMount(slaveMount), container.WithCmd(topCmd...))
370
+
371
+	// Bind mount tmpDir2/ onto tmpDir1/mnt1. If mount propagates inside
372
+	// container then contents of tmpDir2/slave-testfile should become
373
+	// visible at "/volume-dest/mnt1/slave-testfile"
374
+	if err := mount.Mount(tmpDir2.Path(), tmpDir1Mnt, "none", "bind"); err != nil {
375
+		t.Fatal(err)
376
+	}
377
+	defer func() {
378
+		if err := mount.Unmount(tmpDir1Mnt); err != nil {
379
+			t.Fatal(err)
380
+		}
381
+	}()
382
+
383
+	mountCmd := []string{"cat", "/volume-dest/mnt1/slave-testfile"}
384
+
385
+	if result, err := container.Exec(ctx, client, containerID, mountCmd); err == nil {
386
+		if result.Stdout() != "Test" {
387
+			t.Fatalf("Bind mount under slave volume did not propagate to container")
388
+		}
389
+	} else {
390
+		t.Fatal(err)
391
+	}
392
+}