Browse code

On container rm, don't remove named mountpoints

This makes it so when calling `docker run --rm`, or `docker rm -v`, only
volumes specified without a name, e.g. `docker run -v /foo` instead of
`docker run -v awesome:/foo` are removed.

Note that all volumes are named, some are named by the user, some get a
generated name. This is specifically about how the volume was specified
on `run`, assuming that if the user specified it with a name they expect
it to persist after the container is cleaned up.

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

Brian Goff authored on 2016/01/22 11:57:53
Showing 8 changed files
... ...
@@ -25,6 +25,11 @@ func (daemon *Daemon) removeMountPoints(container *container.Container, rm bool)
25 25
 		}
26 26
 		daemon.volumes.Dereference(m.Volume, container.ID)
27 27
 		if rm {
28
+			// Do not remove named mountpoints
29
+			// these are mountpoints specified like `docker run -v <name>:/foo`
30
+			if m.Named {
31
+				continue
32
+			}
28 33
 			err := daemon.volumes.Remove(m.Volume)
29 34
 			// Ignore volume in use errors because having this
30 35
 			// volume being referenced by other container is
... ...
@@ -90,6 +90,7 @@ func (daemon *Daemon) registerMountPoints(container *container.Container, hostCo
90 90
 				Driver:      m.Driver,
91 91
 				Destination: m.Destination,
92 92
 				Propagation: m.Propagation,
93
+				Named:       m.Named,
93 94
 			}
94 95
 
95 96
 			if len(cp.Source) == 0 {
... ...
@@ -126,6 +127,7 @@ func (daemon *Daemon) registerMountPoints(container *container.Container, hostCo
126 126
 			bind.Source = v.Path()
127 127
 			// bind.Name is an already existing volume, we need to use that here
128 128
 			bind.Driver = v.DriverName()
129
+			bind.Named = true
129 130
 			bind = setBindModeIfNull(bind)
130 131
 		}
131 132
 		if label.RelabelNeeded(bind.Mode) {
... ...
@@ -45,3 +45,17 @@ This command will delete all stopped containers. The command
45 45
 `docker ps -a -q` will return all existing container IDs and pass them to
46 46
 the `rm` command which will delete them. Any running containers will not be
47 47
 deleted.
48
+
49
+  $ docker rm -v redis
50
+  redis
51
+
52
+This command will remove the container and any volumes associated with it.
53
+Note that if a volume was specified with a name, it will not be removed.
54
+
55
+  $ docker create -v awesome:/foo -v /bar --name hello redis
56
+  hello
57
+  $ docker rm -v hello
58
+
59
+In this example, the volume for `/foo` will remain intact, but the volume for
60
+`/bar` will be removed. The same behavior holds for volumes inherited with
61
+`--volumes-from`.
... ...
@@ -590,7 +590,11 @@ the container exits**, you can add the `--rm` flag:
590 590
 
591 591
 > **Note**: When you set the `--rm` flag, Docker also removes the volumes
592 592
 associated with the container when the container is removed. This is similar
593
-to running `docker rm -v my-container`.
593
+to running `docker rm -v my-container`. Only volumes that are specified without a
594
+name are removed. For example, with
595
+`docker run --rm -v /foo -v awesome:/bar busybox top`, the volume for `/foo` will be removed,
596
+but the volume for `/bar` will not. Volumes inheritted via `--volumes-from` will be removed
597
+with the same logic -- if the original volume was specified with a name it will **not** be removed.
594 598
 
595 599
 ## Security configuration
596 600
     --security-opt="label:user:USER"   : Set the label user for the container
... ...
@@ -4137,3 +4137,42 @@ func (s *DockerSuite) TestRunNamedVolumeCopyImageData(c *check.C) {
4137 4137
 	out, _ := dockerCmd(c, "run", "-v", "foo:/foo", "busybox", "cat", "/foo/hello")
4138 4138
 	c.Assert(strings.TrimSpace(out), check.Equals, "hello")
4139 4139
 }
4140
+
4141
+func (s *DockerSuite) TestRunNamedVolumeNotRemoved(c *check.C) {
4142
+	prefix := ""
4143
+	if daemonPlatform == "windows" {
4144
+		prefix = "c:"
4145
+	}
4146
+
4147
+	dockerCmd(c, "volume", "create", "--name", "test")
4148
+
4149
+	dockerCmd(c, "run", "--rm", "-v", "test:"+prefix+"/foo", "-v", prefix+"/bar", "busybox", "true")
4150
+	dockerCmd(c, "volume", "inspect", "test")
4151
+	out, _ := dockerCmd(c, "volume", "ls", "-q")
4152
+	c.Assert(strings.TrimSpace(out), checker.Equals, "test")
4153
+
4154
+	dockerCmd(c, "run", "--name=test", "-v", "test:"+prefix+"/foo", "-v", prefix+"/bar", "busybox", "true")
4155
+	dockerCmd(c, "rm", "-fv", "test")
4156
+	dockerCmd(c, "volume", "inspect", "test")
4157
+	out, _ = dockerCmd(c, "volume", "ls", "-q")
4158
+	c.Assert(strings.TrimSpace(out), checker.Equals, "test")
4159
+}
4160
+
4161
+func (s *DockerSuite) TestRunNamedVolumesFromNotRemoved(c *check.C) {
4162
+	prefix := ""
4163
+	if daemonPlatform == "windows" {
4164
+		prefix = "c:"
4165
+	}
4166
+
4167
+	dockerCmd(c, "volume", "create", "--name", "test")
4168
+	dockerCmd(c, "run", "--name=parent", "-v", "test:"+prefix+"/foo", "-v", prefix+"/bar", "busybox", "true")
4169
+	dockerCmd(c, "run", "--name=child", "--volumes-from=parent", "busybox", "true")
4170
+
4171
+	// Remove the parent so there are not other references to the volumes
4172
+	dockerCmd(c, "rm", "-f", "parent")
4173
+	// now remove the child and ensure the named volume (and only the named volume) still exists
4174
+	dockerCmd(c, "rm", "-fv", "child")
4175
+	dockerCmd(c, "volume", "inspect", "test")
4176
+	out, _ := dockerCmd(c, "volume", "ls", "-q")
4177
+	c.Assert(strings.TrimSpace(out), checker.Equals, "test")
4178
+}
... ...
@@ -228,6 +228,9 @@ func (s *DockerExternalVolumeSuite) TestExternalVolumeDriverNamed(c *check.C) {
228 228
 	c.Assert(err, checker.IsNil, check.Commentf(out))
229 229
 	c.Assert(out, checker.Contains, s.server.URL)
230 230
 
231
+	_, err = s.d.Cmd("volume", "rm", "external-volume-test")
232
+	c.Assert(err, checker.IsNil)
233
+
231 234
 	p := hostVolumePath("external-volume-test")
232 235
 	_, err = os.Lstat(p)
233 236
 	c.Assert(err, checker.NotNil)
... ...
@@ -362,6 +365,9 @@ func (s *DockerExternalVolumeSuite) TestExternalVolumeDriverRetryNotImmediatelyE
362 362
 		c.Fatal("volume creates fail when plugin not immediately available")
363 363
 	}
364 364
 
365
+	_, err = s.d.Cmd("volume", "rm", "external-volume-test")
366
+	c.Assert(err, checker.IsNil)
367
+
365 368
 	c.Assert(s.ec.activations, checker.Equals, 1)
366 369
 	c.Assert(s.ec.creations, checker.Equals, 1)
367 370
 	c.Assert(s.ec.removals, checker.Equals, 1)
... ...
@@ -385,7 +391,7 @@ func (s *DockerExternalVolumeSuite) TestExternalVolumeDriverBindExternalVolume(c
385 385
 	c.Assert(mounts[0].Driver, checker.Equals, "test-external-volume-driver")
386 386
 }
387 387
 
388
-func (s *DockerExternalVolumeSuite) TestStartExternalVolumeDriverList(c *check.C) {
388
+func (s *DockerExternalVolumeSuite) TesttExternalVolumeDriverList(c *check.C) {
389 389
 	dockerCmd(c, "volume", "create", "-d", "test-external-volume-driver", "--name", "abc")
390 390
 	out, _ := dockerCmd(c, "volume", "ls")
391 391
 	ls := strings.Split(strings.TrimSpace(out), "\n")
... ...
@@ -399,7 +405,7 @@ func (s *DockerExternalVolumeSuite) TestStartExternalVolumeDriverList(c *check.C
399 399
 	c.Assert(s.ec.lists, check.Equals, 1)
400 400
 }
401 401
 
402
-func (s *DockerExternalVolumeSuite) TestStartExternalVolumeDriverGet(c *check.C) {
402
+func (s *DockerExternalVolumeSuite) TestExternalVolumeDriverGet(c *check.C) {
403 403
 	out, _, err := dockerCmdWithError("volume", "inspect", "dummy")
404 404
 	c.Assert(err, check.NotNil, check.Commentf(out))
405 405
 	c.Assert(s.ec.gets, check.Equals, 1)
... ...
@@ -48,6 +48,22 @@ command. The use that name as follows:
48 48
 
49 49
     docker rm hopeful_morse
50 50
 
51
+## Removing a container and all associated volumes
52
+
53
+  $ docker rm -v redis
54
+  redis
55
+
56
+This command will remove the container and any volumes associated with it.
57
+Note that if a volume was specified with a name, it will not be removed.
58
+
59
+  $ docker create -v awesome:/foo -v /bar --name hello redis
60
+  hello
61
+  $ docker rm -v hello
62
+
63
+In this example, the volume for `/foo` will remain in tact, but the volume for
64
+`/bar` will be removed. The same behavior holds for volumes inherited with
65
+`--volumes-from`.
66
+
51 67
 # HISTORY
52 68
 April 2014, Originally compiled by William Henry (whenry at redhat dot com)
53 69
 based on docker.com source material and internal work.
... ...
@@ -59,6 +59,7 @@ type MountPoint struct {
59 59
 
60 60
 	// Note Propagation is not used on Windows
61 61
 	Propagation string // Mount propagation string
62
+	Named       bool   // specifies if the mountpoint was specified by name
62 63
 }
63 64
 
64 65
 // Setup sets up a mount point by either mounting the volume if it is