Browse code

Merge pull request #40477 from cpuguy83/19.03_40446_check_other_mounts

[19.03] Check tmpfs mounts before create anon volume

Akihiro Suda authored on 2020/02/25 09:41:24
Showing 3 changed files
... ...
@@ -46,7 +46,9 @@ func (daemon *Daemon) createContainerOSSpecificSettings(container *container.Con
46 46
 
47 47
 		// Skip volumes for which we already have something mounted on that
48 48
 		// destination because of a --volume-from.
49
-		if container.IsDestinationMounted(destination) {
49
+		if container.HasMountFor(destination) {
50
+			logrus.WithField("container", container.ID).WithField("destination", spec).Debug("mountpoint already exists, skipping anonymous volume")
51
+			// Not an error, this could easily have come from the image config.
50 52
 			continue
51 53
 		}
52 54
 		path, err := container.GetResourcePath(destination)
... ...
@@ -535,3 +535,50 @@ func TestCreateWithInvalidHealthcheckParams(t *testing.T) {
535 535
 		})
536 536
 	}
537 537
 }
538
+
539
+// Make sure that anonymous volumes can be overritten by tmpfs
540
+// https://github.com/moby/moby/issues/40446
541
+func TestCreateTmpfsOverrideAnonymousVolume(t *testing.T) {
542
+	skip.If(t, testEnv.DaemonInfo.OSType == "windows", "windows does not support tmpfs")
543
+	defer setupTest(t)()
544
+	client := testEnv.APIClient()
545
+	ctx := context.Background()
546
+
547
+	id := ctr.Create(ctx, t, client,
548
+		ctr.WithVolume("/foo"),
549
+		ctr.WithTmpfs("/foo"),
550
+		ctr.WithVolume("/bar"),
551
+		ctr.WithTmpfs("/bar:size=999"),
552
+		ctr.WithCmd("/bin/sh", "-c", "mount | grep '/foo' | grep tmpfs && mount | grep '/bar' | grep tmpfs"),
553
+	)
554
+
555
+	defer func() {
556
+		err := client.ContainerRemove(ctx, id, types.ContainerRemoveOptions{Force: true})
557
+		assert.NilError(t, err)
558
+	}()
559
+
560
+	inspect, err := client.ContainerInspect(ctx, id)
561
+	assert.NilError(t, err)
562
+	// tmpfs do not currently get added to inspect.Mounts
563
+	// Normally an anoynmous volume would, except now tmpfs should prevent that.
564
+	assert.Assert(t, is.Len(inspect.Mounts, 0))
565
+
566
+	chWait, chErr := client.ContainerWait(ctx, id, container.WaitConditionNextExit)
567
+	assert.NilError(t, client.ContainerStart(ctx, id, types.ContainerStartOptions{}))
568
+
569
+	timeout := time.NewTimer(30 * time.Second)
570
+	defer timeout.Stop()
571
+
572
+	select {
573
+	case <-timeout.C:
574
+		t.Fatal("timeout waiting for container to exit")
575
+	case status := <-chWait:
576
+		var errMsg string
577
+		if status.Error != nil {
578
+			errMsg = status.Error.Message
579
+		}
580
+		assert.Equal(t, int(status.StatusCode), 0, errMsg)
581
+	case err := <-chErr:
582
+		assert.NilError(t, err)
583
+	}
584
+}
... ...
@@ -2,6 +2,7 @@ package container
2 2
 
3 3
 import (
4 4
 	"fmt"
5
+	"strings"
5 6
 
6 7
 	containertypes "github.com/docker/docker/api/types/container"
7 8
 	mounttypes "github.com/docker/docker/api/types/mount"
... ...
@@ -77,12 +78,12 @@ func WithMount(m mounttypes.Mount) func(*TestContainerConfig) {
77 77
 }
78 78
 
79 79
 // WithVolume sets the volume of the container
80
-func WithVolume(name string) func(*TestContainerConfig) {
80
+func WithVolume(target string) func(*TestContainerConfig) {
81 81
 	return func(c *TestContainerConfig) {
82 82
 		if c.Config.Volumes == nil {
83 83
 			c.Config.Volumes = map[string]struct{}{}
84 84
 		}
85
-		c.Config.Volumes[name] = struct{}{}
85
+		c.Config.Volumes[target] = struct{}{}
86 86
 	}
87 87
 }
88 88
 
... ...
@@ -93,6 +94,22 @@ func WithBind(src, target string) func(*TestContainerConfig) {
93 93
 	}
94 94
 }
95 95
 
96
+// WithTmpfs sets a target path in the container to a tmpfs
97
+func WithTmpfs(target string) func(config *TestContainerConfig) {
98
+	return func(c *TestContainerConfig) {
99
+		if c.HostConfig.Tmpfs == nil {
100
+			c.HostConfig.Tmpfs = make(map[string]string)
101
+		}
102
+
103
+		spec := strings.SplitN(target, ":", 2)
104
+		var opts string
105
+		if len(spec) > 1 {
106
+			opts = spec[1]
107
+		}
108
+		c.HostConfig.Tmpfs[spec[0]] = opts
109
+	}
110
+}
111
+
96 112
 // WithIPv4 sets the specified ip for the specified network of the container
97 113
 func WithIPv4(network, ip string) func(*TestContainerConfig) {
98 114
 	return func(c *TestContainerConfig) {