This moves the two tests from integration-CLI to integration.
Signed-off-by: Paul "TBBle" Hampson <Paul.Hampson@Pobox.com>
| ... | ... |
@@ -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 |
+} |