Browse code

Add explicit flags for volume cp/no-cp

This allows a user to specify explicitly to enable
automatic copying of data from the container path to the volume path.
This does not change the default behavior of automatically copying, but
does allow a user to disable it at runtime.

Signed-off-by: Brian Goff <cpuguy83@gmail.com>

Brian Goff authored on 2016/03/15 12:31:42
Showing 14 changed files
... ...
@@ -552,6 +552,7 @@ func (container *Container) AddMountPointWithVolume(destination string, vol volu
552 552
 		Destination: destination,
553 553
 		RW:          rw,
554 554
 		Volume:      vol,
555
+		CopyData:    volume.DefaultCopyMode,
555 556
 	}
556 557
 }
557 558
 
... ...
@@ -185,12 +185,8 @@ func (container *Container) CopyImagePathContent(v volume.Volume, destination st
185 185
 	if err != nil {
186 186
 		return err
187 187
 	}
188
-
189
-	if err := copyExistingContents(rootfs, path); err != nil {
190
-		return err
191
-	}
192
-
193
-	return v.Unmount()
188
+	defer v.Unmount()
189
+	return copyExistingContents(rootfs, path)
194 190
 }
195 191
 
196 192
 // ShmResourcePath returns path to shm
... ...
@@ -63,8 +63,7 @@ func (daemon *Daemon) createContainerPlatformSpecificSettings(container *contain
63 63
 // this is only called when the container is created.
64 64
 func (daemon *Daemon) populateVolumes(c *container.Container) error {
65 65
 	for _, mnt := range c.MountPoints {
66
-		// skip binds and volumes referenced by other containers (ie, volumes-from)
67
-		if mnt.Driver == "" || mnt.Volume == nil || len(daemon.volumes.Refs(mnt.Volume)) > 1 {
66
+		if !mnt.CopyData || mnt.Volume == nil {
68 67
 			continue
69 68
 		}
70 69
 
... ...
@@ -131,6 +131,7 @@ func (daemon *Daemon) registerMountPoints(container *container.Container, hostCo
131 131
 				bind = setBindModeIfNull(bind)
132 132
 			}
133 133
 		}
134
+
134 135
 		if label.RelabelNeeded(bind.Mode) {
135 136
 			if err := label.Relabel(bind.Source, container.MountLabel, label.IsShared(bind.Mode)); err != nil {
136 137
 				return err
... ...
@@ -126,6 +126,7 @@ This section lists each version from latest to oldest.  Each listing includes a
126 126
 * `POST /containers/create` now takes `PidsLimit` field, if the kernel is >= 4.3 and the pids cgroup is supported.
127 127
 * `GET /containers/(id or name)/stats` now returns `pids_stats`, if the kernel is >= 4.3 and the pids cgroup is supported.
128 128
 * `POST /containers/create` now allows you to override usernamespaces remapping and use privileged options for the container.
129
+* `POST /containers/create` now allows specifying `nocopy` for named volumes, which disables automatic copying from the container path to the volume.
129 130
 * `POST /auth` now returns an `IdentityToken` when supported by a registry.
130 131
 * `POST /containers/create` with both `Hostname` and `Domainname` fields specified will result in the container's hostname being set to `Hostname`, rather than `Hostname.Domainname`.
131 132
 
... ...
@@ -90,10 +90,10 @@ Creates a new container.
90 90
       --uts=""                      UTS namespace to use
91 91
       -v, --volume=[host-src:]container-dest[:<options>]
92 92
                                     Bind mount a volume. The comma-delimited
93
-                                    `options` are [rw|ro], [z|Z], or
94
-                                    [[r]shared|[r]slave|[r]private]. The
95
-                                    'host-src' is an absolute path or a name
96
-                                    value.
93
+                                    `options` are [rw|ro], [z|Z],
94
+                                    [[r]shared|[r]slave|[r]private], and
95
+                                    [nocopy]. The 'host-src' is an absolute path
96
+                                    or a name value.
97 97
       --volume-driver=""            Container's volume driver
98 98
       --volumes-from=[]             Mount volumes from the specified container(s)
99 99
       -w, --workdir=""              Working directory inside the container
... ...
@@ -92,10 +92,10 @@ parent = "smn_cli"
92 92
       --uts=""                      UTS namespace to use
93 93
       -v, --volume=[host-src:]container-dest[:<options>]
94 94
                                     Bind mount a volume. The comma-delimited
95
-                                    `options` are [rw|ro], [z|Z], or
96
-                                    [[r]shared|[r]slave|[r]private]. The
97
-                                    'host-src' is an absolute path or a name
98
-                                    value.
95
+                                    `options` are [rw|ro], [z|Z],
96
+                                    [[r]shared|[r]slave|[r]private], and
97
+                                    [nocopy]. The 'host-src' is an absolute path
98
+                                    or a name value.
99 99
       --volume-driver=""            Container's volume driver
100 100
       --volumes-from=[]             Mount volumes from the specified container(s)
101 101
       -w, --workdir=""              Working directory inside the container
... ...
@@ -1400,13 +1400,18 @@ The example below mounts an empty tmpfs into the container with the `rw`,
1400 1400
 ### VOLUME (shared filesystems)
1401 1401
 
1402 1402
     -v, --volume=[host-src:]container-dest[:<options>]: Bind mount a volume.
1403
-    The comma-delimited `options` are [rw|ro], [z|Z], or
1404
-    [[r]shared|[r]slave|[r]private]. The 'host-src' is an absolute path or a
1405
-    name value.
1403
+    The comma-delimited `options` are [rw|ro], [z|Z],
1404
+    [[r]shared|[r]slave|[r]private], and [nocopy].
1405
+    The 'host-src' is an absolute path or a name value.
1406 1406
 
1407 1407
     If neither 'rw' or 'ro' is specified then the volume is mounted in
1408 1408
     read-write mode.
1409 1409
 
1410
+    The `nocopy` modes is used to disable automatic copying requested volume
1411
+    path in the container to the volume storage location.
1412
+    For named volumes, `copy` is the default mode. Copy modes are not supported
1413
+    for bind-mounted volumes.
1414
+
1410 1415
     --volumes-from="": Mount all volumes from the given container(s)
1411 1416
 
1412 1417
 > **Note**:
... ...
@@ -4249,6 +4249,44 @@ func (s *DockerSuite) TestRunVolumeWithOneCharacter(c *check.C) {
4249 4249
 	testRequires(c, DaemonIsLinux)
4250 4250
 
4251 4251
 	out, _ := dockerCmd(c, "run", "-v", "/tmp/q:/foo", "busybox", "sh", "-c", "find /foo")
4252
-	fmt.Printf("OUTPUT: %+v", out)
4253 4252
 	c.Assert(strings.TrimSpace(out), checker.Equals, "/foo")
4254 4253
 }
4254
+
4255
+func (s *DockerSuite) TestRunVolumeCopyFlag(c *check.C) {
4256
+	testRequires(c, DaemonIsLinux) // Windows does not support copying data from image to the volume
4257
+	_, err := buildImage("volumecopy",
4258
+		`FROM busybox
4259
+		RUN mkdir /foo && echo hello > /foo/bar
4260
+		CMD cat /foo/bar`,
4261
+		true,
4262
+	)
4263
+	c.Assert(err, checker.IsNil)
4264
+
4265
+	dockerCmd(c, "volume", "create", "--name=test")
4266
+
4267
+	// test with the nocopy flag
4268
+	out, _, err := dockerCmdWithError("run", "-v", "test:/foo:nocopy", "volumecopy")
4269
+	c.Assert(err, checker.NotNil, check.Commentf(out))
4270
+	// test default behavior which is to copy for non-binds
4271
+	out, _ = dockerCmd(c, "run", "-v", "test:/foo", "volumecopy")
4272
+	c.Assert(strings.TrimSpace(out), checker.Equals, "hello")
4273
+	// error out when the volume is already populated
4274
+	out, _, err = dockerCmdWithError("run", "-v", "test:/foo:copy", "volumecopy")
4275
+	c.Assert(err, checker.NotNil, check.Commentf(out))
4276
+	// do not error out when copy isn't explicitly set even though it's already populated
4277
+	out, _ = dockerCmd(c, "run", "-v", "test:/foo", "volumecopy")
4278
+	c.Assert(strings.TrimSpace(out), checker.Equals, "hello")
4279
+
4280
+	// do not allow copy modes on volumes-from
4281
+	dockerCmd(c, "run", "--name=test", "-v", "/foo", "busybox", "true")
4282
+	out, _, err = dockerCmdWithError("run", "--volumes-from=test:copy", "busybox", "true")
4283
+	c.Assert(err, checker.NotNil, check.Commentf(out))
4284
+	out, _, err = dockerCmdWithError("run", "--volumes-from=test:nocopy", "busybox", "true")
4285
+	c.Assert(err, checker.NotNil, check.Commentf(out))
4286
+
4287
+	// do not allow copy modes on binds
4288
+	out, _, err = dockerCmdWithError("run", "-v", "/foo:/bar:copy", "busybox", "true")
4289
+	c.Assert(err, checker.NotNil, check.Commentf(out))
4290
+	out, _, err = dockerCmdWithError("run", "-v", "/foo:/bar:nocopy", "busybox", "true")
4291
+	c.Assert(err, checker.NotNil, check.Commentf(out))
4292
+}
... ...
@@ -434,6 +434,10 @@ change propagation properties of source mount. Say `/` is source mount for
434 434
 > is `slave`, you may not be able to use the `shared` or `rshared` propagation on
435 435
 > a volume.
436 436
 
437
+
438
+To disable automatic copying of data from the container path to the volume, use
439
+the `nocopy` flag. The `nocopy` flag can be set on bind mounts and named volumes.
440
+
437 441
 **--volume-driver**=""
438 442
    Container's volume driver. This driver creates volumes specified either from
439 443
    a Dockerfile's `VOLUME` instruction or from the `docker run -v` flag.
... ...
@@ -531,6 +531,7 @@ any options, the systems uses the following options:
531 531
    * [rw|ro]
532 532
    * [z|Z]
533 533
    * [`[r]shared`|`[r]slave`|`[r]private`]
534
+   * [nocopy]
534 535
 
535 536
 The `CONTAINER-DIR` must be an absolute path such as `/src/docs`. The `HOST-DIR`
536 537
 can be an absolute path or a `name` value. A `name` value must start with an
... ...
@@ -603,6 +604,9 @@ change propagation properties of source mount. Say `/` is source mount for
603 603
 > is `slave`, you may not be able to use the `shared` or `rshared` propagation on
604 604
 > a volume.
605 605
 
606
+To disable automatic copying of data from the container path to the volume, use
607
+the `nocopy` flag. The `nocopy` flag can be set on bind mounts and named volumes.
608
+
606 609
 **--volume-driver**=""
607 610
    Container's volume driver. This driver creates volumes specified either from
608 611
    a Dockerfile's `VOLUME` instruction or from the `docker run -v` flag.
... ...
@@ -60,6 +60,11 @@ type MountPoint struct {
60 60
 	// Note Propagation is not used on Windows
61 61
 	Propagation string // Mount propagation string
62 62
 	Named       bool   // specifies if the mountpoint was specified by name
63
+
64
+	// Specifies if data should be copied from the container before the first mount
65
+	// Use a pointer here so we can tell if the user set this value explicitly
66
+	// This allows us to error out when the user explicitly enabled copy but we can't copy due to the volume being populated
67
+	CopyData bool `json:"-"`
63 68
 }
64 69
 
65 70
 // Setup sets up a mount point by either mounting the volume if it is
... ...
@@ -115,6 +120,10 @@ func ParseVolumesFrom(spec string) (string, string, error) {
115 115
 		if HasPropagation(mode) {
116 116
 			return "", "", errInvalidMode(mode)
117 117
 		}
118
+		// Do not allow copy modes on volumes-from
119
+		if _, isSet := getCopyMode(mode); isSet {
120
+			return "", "", errInvalidMode(mode)
121
+		}
118 122
 	}
119 123
 	return id, mode, nil
120 124
 }
121 125
new file mode 100644
... ...
@@ -0,0 +1,28 @@
0
+package volume
1
+
2
+import "strings"
3
+
4
+const (
5
+	// DefaultCopyMode is the copy mode used by default for normal/named volumes
6
+	DefaultCopyMode = true
7
+)
8
+
9
+// {<copy mode>=isEnabled}
10
+var copyModes = map[string]bool{
11
+	"nocopy": false,
12
+}
13
+
14
+func copyModeExists(mode string) bool {
15
+	_, exists := copyModes[mode]
16
+	return exists
17
+}
18
+
19
+// GetCopyMode gets the copy mode from the mode string for mounts
20
+func getCopyMode(mode string) (bool, bool) {
21
+	for _, o := range strings.Split(mode, ",") {
22
+		if isEnabled, exists := copyModes[o]; exists {
23
+			return isEnabled, true
24
+		}
25
+	}
26
+	return DefaultCopyMode, false
27
+}
... ...
@@ -110,6 +110,13 @@ func ParseMountSpec(spec, volumeDriver string) (*MountPoint, error) {
110 110
 		mp.Source = filepath.Clean(source)
111 111
 	}
112 112
 
113
+	copyData, isSet := getCopyMode(mp.Mode)
114
+	// do not allow copy modes on binds
115
+	if len(name) == 0 && isSet {
116
+		return nil, errInvalidMode(mp.Mode)
117
+	}
118
+
119
+	mp.CopyData = copyData
113 120
 	mp.Name = name
114 121
 
115 122
 	return mp, nil
... ...
@@ -137,23 +144,25 @@ func ValidMountMode(mode string) bool {
137 137
 	rwModeCount := 0
138 138
 	labelModeCount := 0
139 139
 	propagationModeCount := 0
140
+	copyModeCount := 0
140 141
 
141 142
 	for _, o := range strings.Split(mode, ",") {
142
-		if rwModes[o] {
143
+		switch {
144
+		case rwModes[o]:
143 145
 			rwModeCount++
144
-			continue
145
-		} else if labelModes[o] {
146
+		case labelModes[o]:
146 147
 			labelModeCount++
147
-			continue
148
-		} else if propagationModes[o] {
148
+		case propagationModes[o]:
149 149
 			propagationModeCount++
150
-			continue
150
+		case copyModeExists(o):
151
+			copyModeCount++
152
+		default:
153
+			return false
151 154
 		}
152
-		return false
153 155
 	}
154 156
 
155 157
 	// Only one string for each mode is allowed.
156
-	if rwModeCount > 1 || labelModeCount > 1 || propagationModeCount > 1 {
158
+	if rwModeCount > 1 || labelModeCount > 1 || propagationModeCount > 1 || copyModeCount > 1 {
157 159
 		return false
158 160
 	}
159 161
 	return true