Преглед изворни кода

Merge pull request #21223 from cpuguy83/add_nocp_to_vols

Add explicit flags for volume cp/no-cp
Tibor Vass пре 9 година
родитељ
комит
de9ff4bdc0

+ 1 - 0
container/container.go

@@ -552,6 +552,7 @@ func (container *Container) AddMountPointWithVolume(destination string, vol volu
 		Destination: destination,
 		Destination: destination,
 		RW:          rw,
 		RW:          rw,
 		Volume:      vol,
 		Volume:      vol,
+		CopyData:    volume.DefaultCopyMode,
 	}
 	}
 }
 }
 
 

+ 2 - 6
container/container_unix.go

@@ -185,12 +185,8 @@ func (container *Container) CopyImagePathContent(v volume.Volume, destination st
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
-
-	if err := copyExistingContents(rootfs, path); err != nil {
-		return err
-	}
-
-	return v.Unmount()
+	defer v.Unmount()
+	return copyExistingContents(rootfs, path)
 }
 }
 
 
 // ShmResourcePath returns path to shm
 // ShmResourcePath returns path to shm

+ 1 - 2
daemon/create_unix.go

@@ -63,8 +63,7 @@ func (daemon *Daemon) createContainerPlatformSpecificSettings(container *contain
 // this is only called when the container is created.
 // this is only called when the container is created.
 func (daemon *Daemon) populateVolumes(c *container.Container) error {
 func (daemon *Daemon) populateVolumes(c *container.Container) error {
 	for _, mnt := range c.MountPoints {
 	for _, mnt := range c.MountPoints {
-		// skip binds and volumes referenced by other containers (ie, volumes-from)
-		if mnt.Driver == "" || mnt.Volume == nil || len(daemon.volumes.Refs(mnt.Volume)) > 1 {
+		if !mnt.CopyData || mnt.Volume == nil {
 			continue
 			continue
 		}
 		}
 
 

+ 1 - 0
daemon/volumes.go

@@ -131,6 +131,7 @@ func (daemon *Daemon) registerMountPoints(container *container.Container, hostCo
 				bind = setBindModeIfNull(bind)
 				bind = setBindModeIfNull(bind)
 			}
 			}
 		}
 		}
+
 		if label.RelabelNeeded(bind.Mode) {
 		if label.RelabelNeeded(bind.Mode) {
 			if err := label.Relabel(bind.Source, container.MountLabel, label.IsShared(bind.Mode)); err != nil {
 			if err := label.Relabel(bind.Source, container.MountLabel, label.IsShared(bind.Mode)); err != nil {
 				return err
 				return err

+ 1 - 0
docs/reference/api/docker_remote_api.md

@@ -126,6 +126,7 @@ This section lists each version from latest to oldest.  Each listing includes a
 * `POST /containers/create` now takes `PidsLimit` field, if the kernel is >= 4.3 and the pids cgroup is supported.
 * `POST /containers/create` now takes `PidsLimit` field, if the kernel is >= 4.3 and the pids cgroup is supported.
 * `GET /containers/(id or name)/stats` now returns `pids_stats`, if the kernel is >= 4.3 and the pids cgroup is supported.
 * `GET /containers/(id or name)/stats` now returns `pids_stats`, if the kernel is >= 4.3 and the pids cgroup is supported.
 * `POST /containers/create` now allows you to override usernamespaces remapping and use privileged options for the container.
 * `POST /containers/create` now allows you to override usernamespaces remapping and use privileged options for the container.
+* `POST /containers/create` now allows specifying `nocopy` for named volumes, which disables automatic copying from the container path to the volume.
 * `POST /auth` now returns an `IdentityToken` when supported by a registry.
 * `POST /auth` now returns an `IdentityToken` when supported by a registry.
 * `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`.
 * `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`.
 
 

+ 4 - 4
docs/reference/commandline/create.md

@@ -90,10 +90,10 @@ Creates a new container.
       --uts=""                      UTS namespace to use
       --uts=""                      UTS namespace to use
       -v, --volume=[host-src:]container-dest[:<options>]
       -v, --volume=[host-src:]container-dest[:<options>]
                                     Bind mount a volume. The comma-delimited
                                     Bind mount a volume. The comma-delimited
-                                    `options` are [rw|ro], [z|Z], or
-                                    [[r]shared|[r]slave|[r]private]. The
-                                    'host-src' is an absolute path or a name
-                                    value.
+                                    `options` are [rw|ro], [z|Z],
+                                    [[r]shared|[r]slave|[r]private], and
+                                    [nocopy]. The 'host-src' is an absolute path
+                                    or a name value.
       --volume-driver=""            Container's volume driver
       --volume-driver=""            Container's volume driver
       --volumes-from=[]             Mount volumes from the specified container(s)
       --volumes-from=[]             Mount volumes from the specified container(s)
       -w, --workdir=""              Working directory inside the container
       -w, --workdir=""              Working directory inside the container

+ 4 - 4
docs/reference/commandline/run.md

@@ -92,10 +92,10 @@ parent = "smn_cli"
       --uts=""                      UTS namespace to use
       --uts=""                      UTS namespace to use
       -v, --volume=[host-src:]container-dest[:<options>]
       -v, --volume=[host-src:]container-dest[:<options>]
                                     Bind mount a volume. The comma-delimited
                                     Bind mount a volume. The comma-delimited
-                                    `options` are [rw|ro], [z|Z], or
-                                    [[r]shared|[r]slave|[r]private]. The
-                                    'host-src' is an absolute path or a name
-                                    value.
+                                    `options` are [rw|ro], [z|Z],
+                                    [[r]shared|[r]slave|[r]private], and
+                                    [nocopy]. The 'host-src' is an absolute path
+                                    or a name value.
       --volume-driver=""            Container's volume driver
       --volume-driver=""            Container's volume driver
       --volumes-from=[]             Mount volumes from the specified container(s)
       --volumes-from=[]             Mount volumes from the specified container(s)
       -w, --workdir=""              Working directory inside the container
       -w, --workdir=""              Working directory inside the container

+ 8 - 3
docs/reference/run.md

@@ -1400,13 +1400,18 @@ The example below mounts an empty tmpfs into the container with the `rw`,
 ### VOLUME (shared filesystems)
 ### VOLUME (shared filesystems)
 
 
     -v, --volume=[host-src:]container-dest[:<options>]: Bind mount a volume.
     -v, --volume=[host-src:]container-dest[:<options>]: Bind mount a volume.
-    The comma-delimited `options` are [rw|ro], [z|Z], or
-    [[r]shared|[r]slave|[r]private]. The 'host-src' is an absolute path or a
-    name value.
+    The comma-delimited `options` are [rw|ro], [z|Z],
+    [[r]shared|[r]slave|[r]private], and [nocopy].
+    The 'host-src' is an absolute path or a name value.
 
 
     If neither 'rw' or 'ro' is specified then the volume is mounted in
     If neither 'rw' or 'ro' is specified then the volume is mounted in
     read-write mode.
     read-write mode.
 
 
+    The `nocopy` modes is used to disable automatic copying requested volume
+    path in the container to the volume storage location.
+    For named volumes, `copy` is the default mode. Copy modes are not supported
+    for bind-mounted volumes.
+
     --volumes-from="": Mount all volumes from the given container(s)
     --volumes-from="": Mount all volumes from the given container(s)
 
 
 > **Note**:
 > **Note**:

+ 39 - 1
integration-cli/docker_cli_run_test.go

@@ -4249,6 +4249,44 @@ func (s *DockerSuite) TestRunVolumeWithOneCharacter(c *check.C) {
 	testRequires(c, DaemonIsLinux)
 	testRequires(c, DaemonIsLinux)
 
 
 	out, _ := dockerCmd(c, "run", "-v", "/tmp/q:/foo", "busybox", "sh", "-c", "find /foo")
 	out, _ := dockerCmd(c, "run", "-v", "/tmp/q:/foo", "busybox", "sh", "-c", "find /foo")
-	fmt.Printf("OUTPUT: %+v", out)
 	c.Assert(strings.TrimSpace(out), checker.Equals, "/foo")
 	c.Assert(strings.TrimSpace(out), checker.Equals, "/foo")
 }
 }
+
+func (s *DockerSuite) TestRunVolumeCopyFlag(c *check.C) {
+	testRequires(c, DaemonIsLinux) // Windows does not support copying data from image to the volume
+	_, err := buildImage("volumecopy",
+		`FROM busybox
+		RUN mkdir /foo && echo hello > /foo/bar
+		CMD cat /foo/bar`,
+		true,
+	)
+	c.Assert(err, checker.IsNil)
+
+	dockerCmd(c, "volume", "create", "--name=test")
+
+	// test with the nocopy flag
+	out, _, err := dockerCmdWithError("run", "-v", "test:/foo:nocopy", "volumecopy")
+	c.Assert(err, checker.NotNil, check.Commentf(out))
+	// test default behavior which is to copy for non-binds
+	out, _ = dockerCmd(c, "run", "-v", "test:/foo", "volumecopy")
+	c.Assert(strings.TrimSpace(out), checker.Equals, "hello")
+	// error out when the volume is already populated
+	out, _, err = dockerCmdWithError("run", "-v", "test:/foo:copy", "volumecopy")
+	c.Assert(err, checker.NotNil, check.Commentf(out))
+	// do not error out when copy isn't explicitly set even though it's already populated
+	out, _ = dockerCmd(c, "run", "-v", "test:/foo", "volumecopy")
+	c.Assert(strings.TrimSpace(out), checker.Equals, "hello")
+
+	// do not allow copy modes on volumes-from
+	dockerCmd(c, "run", "--name=test", "-v", "/foo", "busybox", "true")
+	out, _, err = dockerCmdWithError("run", "--volumes-from=test:copy", "busybox", "true")
+	c.Assert(err, checker.NotNil, check.Commentf(out))
+	out, _, err = dockerCmdWithError("run", "--volumes-from=test:nocopy", "busybox", "true")
+	c.Assert(err, checker.NotNil, check.Commentf(out))
+
+	// do not allow copy modes on binds
+	out, _, err = dockerCmdWithError("run", "-v", "/foo:/bar:copy", "busybox", "true")
+	c.Assert(err, checker.NotNil, check.Commentf(out))
+	out, _, err = dockerCmdWithError("run", "-v", "/foo:/bar:nocopy", "busybox", "true")
+	c.Assert(err, checker.NotNil, check.Commentf(out))
+}

+ 4 - 0
man/docker-create.1.md

@@ -434,6 +434,10 @@ change propagation properties of source mount. Say `/` is source mount for
 > is `slave`, you may not be able to use the `shared` or `rshared` propagation on
 > is `slave`, you may not be able to use the `shared` or `rshared` propagation on
 > a volume.
 > a volume.
 
 
+
+To disable automatic copying of data from the container path to the volume, use
+the `nocopy` flag. The `nocopy` flag can be set on bind mounts and named volumes.
+
 **--volume-driver**=""
 **--volume-driver**=""
    Container's volume driver. This driver creates volumes specified either from
    Container's volume driver. This driver creates volumes specified either from
    a Dockerfile's `VOLUME` instruction or from the `docker run -v` flag.
    a Dockerfile's `VOLUME` instruction or from the `docker run -v` flag.

+ 4 - 0
man/docker-run.1.md

@@ -531,6 +531,7 @@ any options, the systems uses the following options:
    * [rw|ro]
    * [rw|ro]
    * [z|Z]
    * [z|Z]
    * [`[r]shared`|`[r]slave`|`[r]private`]
    * [`[r]shared`|`[r]slave`|`[r]private`]
+   * [nocopy]
 
 
 The `CONTAINER-DIR` must be an absolute path such as `/src/docs`. The `HOST-DIR`
 The `CONTAINER-DIR` must be an absolute path such as `/src/docs`. The `HOST-DIR`
 can be an absolute path or a `name` value. A `name` value must start with an
 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
 > is `slave`, you may not be able to use the `shared` or `rshared` propagation on
 > is `slave`, you may not be able to use the `shared` or `rshared` propagation on
 > a volume.
 > a volume.
 
 
+To disable automatic copying of data from the container path to the volume, use
+the `nocopy` flag. The `nocopy` flag can be set on bind mounts and named volumes.
+
 **--volume-driver**=""
 **--volume-driver**=""
    Container's volume driver. This driver creates volumes specified either from
    Container's volume driver. This driver creates volumes specified either from
    a Dockerfile's `VOLUME` instruction or from the `docker run -v` flag.
    a Dockerfile's `VOLUME` instruction or from the `docker run -v` flag.

+ 9 - 0
volume/volume.go

@@ -60,6 +60,11 @@ type MountPoint struct {
 	// Note Propagation is not used on Windows
 	// Note Propagation is not used on Windows
 	Propagation string // Mount propagation string
 	Propagation string // Mount propagation string
 	Named       bool   // specifies if the mountpoint was specified by name
 	Named       bool   // specifies if the mountpoint was specified by name
+
+	// Specifies if data should be copied from the container before the first mount
+	// Use a pointer here so we can tell if the user set this value explicitly
+	// This allows us to error out when the user explicitly enabled copy but we can't copy due to the volume being populated
+	CopyData bool `json:"-"`
 }
 }
 
 
 // Setup sets up a mount point by either mounting the volume if it is
 // 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) {
 		if HasPropagation(mode) {
 		if HasPropagation(mode) {
 			return "", "", errInvalidMode(mode)
 			return "", "", errInvalidMode(mode)
 		}
 		}
+		// Do not allow copy modes on volumes-from
+		if _, isSet := getCopyMode(mode); isSet {
+			return "", "", errInvalidMode(mode)
+		}
 	}
 	}
 	return id, mode, nil
 	return id, mode, nil
 }
 }

+ 28 - 0
volume/volume_copy.go

@@ -0,0 +1,28 @@
+package volume
+
+import "strings"
+
+const (
+	// DefaultCopyMode is the copy mode used by default for normal/named volumes
+	DefaultCopyMode = true
+)
+
+// {<copy mode>=isEnabled}
+var copyModes = map[string]bool{
+	"nocopy": false,
+}
+
+func copyModeExists(mode string) bool {
+	_, exists := copyModes[mode]
+	return exists
+}
+
+// GetCopyMode gets the copy mode from the mode string for mounts
+func getCopyMode(mode string) (bool, bool) {
+	for _, o := range strings.Split(mode, ",") {
+		if isEnabled, exists := copyModes[o]; exists {
+			return isEnabled, true
+		}
+	}
+	return DefaultCopyMode, false
+}

+ 17 - 8
volume/volume_unix.go

@@ -110,6 +110,13 @@ func ParseMountSpec(spec, volumeDriver string) (*MountPoint, error) {
 		mp.Source = filepath.Clean(source)
 		mp.Source = filepath.Clean(source)
 	}
 	}
 
 
+	copyData, isSet := getCopyMode(mp.Mode)
+	// do not allow copy modes on binds
+	if len(name) == 0 && isSet {
+		return nil, errInvalidMode(mp.Mode)
+	}
+
+	mp.CopyData = copyData
 	mp.Name = name
 	mp.Name = name
 
 
 	return mp, nil
 	return mp, nil
@@ -137,23 +144,25 @@ func ValidMountMode(mode string) bool {
 	rwModeCount := 0
 	rwModeCount := 0
 	labelModeCount := 0
 	labelModeCount := 0
 	propagationModeCount := 0
 	propagationModeCount := 0
+	copyModeCount := 0
 
 
 	for _, o := range strings.Split(mode, ",") {
 	for _, o := range strings.Split(mode, ",") {
-		if rwModes[o] {
+		switch {
+		case rwModes[o]:
 			rwModeCount++
 			rwModeCount++
-			continue
-		} else if labelModes[o] {
+		case labelModes[o]:
 			labelModeCount++
 			labelModeCount++
-			continue
-		} else if propagationModes[o] {
+		case propagationModes[o]:
 			propagationModeCount++
 			propagationModeCount++
-			continue
+		case copyModeExists(o):
+			copyModeCount++
+		default:
+			return false
 		}
 		}
-		return false
 	}
 	}
 
 
 	// Only one string for each mode is allowed.
 	// Only one string for each mode is allowed.
-	if rwModeCount > 1 || labelModeCount > 1 || propagationModeCount > 1 {
+	if rwModeCount > 1 || labelModeCount > 1 || propagationModeCount > 1 || copyModeCount > 1 {
 		return false
 		return false
 	}
 	}
 	return true
 	return true