1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00

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>
This commit is contained in:
Brian Goff 2016-01-21 21:57:53 -05:00
parent bf85a49509
commit dd7d1c8a02
8 changed files with 90 additions and 3 deletions

View file

@ -25,6 +25,11 @@ func (daemon *Daemon) removeMountPoints(container *container.Container, rm bool)
} }
daemon.volumes.Dereference(m.Volume, container.ID) daemon.volumes.Dereference(m.Volume, container.ID)
if rm { if rm {
// Do not remove named mountpoints
// these are mountpoints specified like `docker run -v <name>:/foo`
if m.Named {
continue
}
err := daemon.volumes.Remove(m.Volume) err := daemon.volumes.Remove(m.Volume)
// Ignore volume in use errors because having this // Ignore volume in use errors because having this
// volume being referenced by other container is // volume being referenced by other container is

View file

@ -90,6 +90,7 @@ func (daemon *Daemon) registerMountPoints(container *container.Container, hostCo
Driver: m.Driver, Driver: m.Driver,
Destination: m.Destination, Destination: m.Destination,
Propagation: m.Propagation, Propagation: m.Propagation,
Named: m.Named,
} }
if len(cp.Source) == 0 { if len(cp.Source) == 0 {
@ -126,6 +127,7 @@ func (daemon *Daemon) registerMountPoints(container *container.Container, hostCo
bind.Source = v.Path() bind.Source = v.Path()
// bind.Name is an already existing volume, we need to use that here // bind.Name is an already existing volume, we need to use that here
bind.Driver = v.DriverName() bind.Driver = v.DriverName()
bind.Named = true
bind = setBindModeIfNull(bind) bind = setBindModeIfNull(bind)
} }
if label.RelabelNeeded(bind.Mode) { if label.RelabelNeeded(bind.Mode) {

View file

@ -45,3 +45,17 @@ This command will delete all stopped containers. The command
`docker ps -a -q` will return all existing container IDs and pass them to `docker ps -a -q` will return all existing container IDs and pass them to
the `rm` command which will delete them. Any running containers will not be the `rm` command which will delete them. Any running containers will not be
deleted. deleted.
$ docker rm -v redis
redis
This command will remove the container and any volumes associated with it.
Note that if a volume was specified with a name, it will not be removed.
$ docker create -v awesome:/foo -v /bar --name hello redis
hello
$ docker rm -v hello
In this example, the volume for `/foo` will remain intact, but the volume for
`/bar` will be removed. The same behavior holds for volumes inherited with
`--volumes-from`.

View file

@ -590,7 +590,11 @@ the container exits**, you can add the `--rm` flag:
> **Note**: When you set the `--rm` flag, Docker also removes the volumes > **Note**: When you set the `--rm` flag, Docker also removes the volumes
associated with the container when the container is removed. This is similar associated with the container when the container is removed. This is similar
to running `docker rm -v my-container`. to running `docker rm -v my-container`. Only volumes that are specified without a
name are removed. For example, with
`docker run --rm -v /foo -v awesome:/bar busybox top`, the volume for `/foo` will be removed,
but the volume for `/bar` will not. Volumes inheritted via `--volumes-from` will be removed
with the same logic -- if the original volume was specified with a name it will **not** be removed.
## Security configuration ## Security configuration
--security-opt="label:user:USER" : Set the label user for the container --security-opt="label:user:USER" : Set the label user for the container

View file

@ -4137,3 +4137,42 @@ func (s *DockerSuite) TestRunNamedVolumeCopyImageData(c *check.C) {
out, _ := dockerCmd(c, "run", "-v", "foo:/foo", "busybox", "cat", "/foo/hello") out, _ := dockerCmd(c, "run", "-v", "foo:/foo", "busybox", "cat", "/foo/hello")
c.Assert(strings.TrimSpace(out), check.Equals, "hello") c.Assert(strings.TrimSpace(out), check.Equals, "hello")
} }
func (s *DockerSuite) TestRunNamedVolumeNotRemoved(c *check.C) {
prefix := ""
if daemonPlatform == "windows" {
prefix = "c:"
}
dockerCmd(c, "volume", "create", "--name", "test")
dockerCmd(c, "run", "--rm", "-v", "test:"+prefix+"/foo", "-v", prefix+"/bar", "busybox", "true")
dockerCmd(c, "volume", "inspect", "test")
out, _ := dockerCmd(c, "volume", "ls", "-q")
c.Assert(strings.TrimSpace(out), checker.Equals, "test")
dockerCmd(c, "run", "--name=test", "-v", "test:"+prefix+"/foo", "-v", prefix+"/bar", "busybox", "true")
dockerCmd(c, "rm", "-fv", "test")
dockerCmd(c, "volume", "inspect", "test")
out, _ = dockerCmd(c, "volume", "ls", "-q")
c.Assert(strings.TrimSpace(out), checker.Equals, "test")
}
func (s *DockerSuite) TestRunNamedVolumesFromNotRemoved(c *check.C) {
prefix := ""
if daemonPlatform == "windows" {
prefix = "c:"
}
dockerCmd(c, "volume", "create", "--name", "test")
dockerCmd(c, "run", "--name=parent", "-v", "test:"+prefix+"/foo", "-v", prefix+"/bar", "busybox", "true")
dockerCmd(c, "run", "--name=child", "--volumes-from=parent", "busybox", "true")
// Remove the parent so there are not other references to the volumes
dockerCmd(c, "rm", "-f", "parent")
// now remove the child and ensure the named volume (and only the named volume) still exists
dockerCmd(c, "rm", "-fv", "child")
dockerCmd(c, "volume", "inspect", "test")
out, _ := dockerCmd(c, "volume", "ls", "-q")
c.Assert(strings.TrimSpace(out), checker.Equals, "test")
}

View file

@ -228,6 +228,9 @@ func (s *DockerExternalVolumeSuite) TestExternalVolumeDriverNamed(c *check.C) {
c.Assert(err, checker.IsNil, check.Commentf(out)) c.Assert(err, checker.IsNil, check.Commentf(out))
c.Assert(out, checker.Contains, s.server.URL) c.Assert(out, checker.Contains, s.server.URL)
_, err = s.d.Cmd("volume", "rm", "external-volume-test")
c.Assert(err, checker.IsNil)
p := hostVolumePath("external-volume-test") p := hostVolumePath("external-volume-test")
_, err = os.Lstat(p) _, err = os.Lstat(p)
c.Assert(err, checker.NotNil) c.Assert(err, checker.NotNil)
@ -362,6 +365,9 @@ func (s *DockerExternalVolumeSuite) TestExternalVolumeDriverRetryNotImmediatelyE
c.Fatal("volume creates fail when plugin not immediately available") c.Fatal("volume creates fail when plugin not immediately available")
} }
_, err = s.d.Cmd("volume", "rm", "external-volume-test")
c.Assert(err, checker.IsNil)
c.Assert(s.ec.activations, checker.Equals, 1) c.Assert(s.ec.activations, checker.Equals, 1)
c.Assert(s.ec.creations, checker.Equals, 1) c.Assert(s.ec.creations, checker.Equals, 1)
c.Assert(s.ec.removals, checker.Equals, 1) c.Assert(s.ec.removals, checker.Equals, 1)
@ -385,7 +391,7 @@ func (s *DockerExternalVolumeSuite) TestExternalVolumeDriverBindExternalVolume(c
c.Assert(mounts[0].Driver, checker.Equals, "test-external-volume-driver") c.Assert(mounts[0].Driver, checker.Equals, "test-external-volume-driver")
} }
func (s *DockerExternalVolumeSuite) TestStartExternalVolumeDriverList(c *check.C) { func (s *DockerExternalVolumeSuite) TesttExternalVolumeDriverList(c *check.C) {
dockerCmd(c, "volume", "create", "-d", "test-external-volume-driver", "--name", "abc") dockerCmd(c, "volume", "create", "-d", "test-external-volume-driver", "--name", "abc")
out, _ := dockerCmd(c, "volume", "ls") out, _ := dockerCmd(c, "volume", "ls")
ls := strings.Split(strings.TrimSpace(out), "\n") ls := strings.Split(strings.TrimSpace(out), "\n")
@ -399,7 +405,7 @@ func (s *DockerExternalVolumeSuite) TestStartExternalVolumeDriverList(c *check.C
c.Assert(s.ec.lists, check.Equals, 1) c.Assert(s.ec.lists, check.Equals, 1)
} }
func (s *DockerExternalVolumeSuite) TestStartExternalVolumeDriverGet(c *check.C) { func (s *DockerExternalVolumeSuite) TestExternalVolumeDriverGet(c *check.C) {
out, _, err := dockerCmdWithError("volume", "inspect", "dummy") out, _, err := dockerCmdWithError("volume", "inspect", "dummy")
c.Assert(err, check.NotNil, check.Commentf(out)) c.Assert(err, check.NotNil, check.Commentf(out))
c.Assert(s.ec.gets, check.Equals, 1) c.Assert(s.ec.gets, check.Equals, 1)

View file

@ -48,6 +48,22 @@ command. The use that name as follows:
docker rm hopeful_morse docker rm hopeful_morse
## Removing a container and all associated volumes
$ docker rm -v redis
redis
This command will remove the container and any volumes associated with it.
Note that if a volume was specified with a name, it will not be removed.
$ docker create -v awesome:/foo -v /bar --name hello redis
hello
$ docker rm -v hello
In this example, the volume for `/foo` will remain in tact, but the volume for
`/bar` will be removed. The same behavior holds for volumes inherited with
`--volumes-from`.
# HISTORY # HISTORY
April 2014, Originally compiled by William Henry (whenry at redhat dot com) April 2014, Originally compiled by William Henry (whenry at redhat dot com)
based on docker.com source material and internal work. based on docker.com source material and internal work.

View file

@ -59,6 +59,7 @@ 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
} }
// 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