Skip to content

Commit

Permalink
Merge pull request moby#23436 from yongtang/23367-out-of-band-volume-…
Browse files Browse the repository at this point in the history
…driver-deletion

Add `--force` in `docker volume rm` to fix out-of-band volume driver deletion
  • Loading branch information
vdemeester authored Aug 19, 2016
2 parents 80798d2 + 7fd2c80 commit 09b9290
Show file tree
Hide file tree
Showing 11 changed files with 82 additions and 13 deletions.
26 changes: 20 additions & 6 deletions api/client/volume/remove.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,27 +10,41 @@ import (
"github.com/spf13/cobra"
)

type removeOptions struct {
force bool

volumes []string
}

func newRemoveCommand(dockerCli *client.DockerCli) *cobra.Command {
return &cobra.Command{
Use: "rm VOLUME [VOLUME...]",
var opts removeOptions

cmd := &cobra.Command{
Use: "rm [OPTIONS] VOLUME [VOLUME]...",
Aliases: []string{"remove"},
Short: "Remove one or more volumes",
Long: removeDescription,
Example: removeExample,
Args: cli.RequiresMinArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
return runRemove(dockerCli, args)
opts.volumes = args
return runRemove(dockerCli, &opts)
},
}

flags := cmd.Flags()
flags.BoolVarP(&opts.force, "force", "f", false, "Force the removal of one or more volumes")

return cmd
}

func runRemove(dockerCli *client.DockerCli, volumes []string) error {
func runRemove(dockerCli *client.DockerCli, opts *removeOptions) error {
client := dockerCli.Client()
ctx := context.Background()
status := 0

for _, name := range volumes {
if err := client.VolumeRemove(ctx, name, false); err != nil {
for _, name := range opts.volumes {
if err := client.VolumeRemove(ctx, name, opts.force); err != nil {
fmt.Fprintf(dockerCli.Err(), "%s\n", err)
status = 1
continue
Expand Down
2 changes: 1 addition & 1 deletion api/server/router/volume/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@ type Backend interface {
Volumes(filter string) ([]*types.Volume, []string, error)
VolumeInspect(name string) (*types.Volume, error)
VolumeCreate(name, driverName string, opts, labels map[string]string) (*types.Volume, error)
VolumeRm(name string) error
VolumeRm(name string, force bool) error
}
3 changes: 2 additions & 1 deletion api/server/router/volume/volume_routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ func (v *volumeRouter) deleteVolumes(ctx context.Context, w http.ResponseWriter,
if err := httputils.ParseForm(r); err != nil {
return err
}
if err := v.backend.VolumeRm(vars["name"]); err != nil {
force := httputils.BoolValue(r, "force")
if err := v.backend.VolumeRm(vars["name"], force); err != nil {
return err
}
w.WriteHeader(http.StatusNoContent)
Expand Down
2 changes: 1 addition & 1 deletion contrib/completion/bash/docker
Original file line number Diff line number Diff line change
Expand Up @@ -2853,7 +2853,7 @@ _docker_volume_ls() {
_docker_volume_rm() {
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "--help" -- "$cur" ) )
COMPREPLY=( $( compgen -W "--force -f --help" -- "$cur" ) )
;;
*)
__docker_complete_volumes
Expand Down
1 change: 1 addition & 0 deletions contrib/completion/zsh/_docker
Original file line number Diff line number Diff line change
Expand Up @@ -1360,6 +1360,7 @@ __docker_volume_subcommand() {
(rm)
_arguments $(__docker_arguments) \
$opts_help \
"($help -f --force)"{-f,--force}"[Force the removal of one or more volumes]" \
"($help -):volume:__docker_volumes" && ret=0
;;
(help)
Expand Down
11 changes: 10 additions & 1 deletion daemon/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,16 @@ func (daemon *Daemon) cleanupContainer(container *container.Container, forceRemo
// VolumeRm removes the volume with the given name.
// If the volume is referenced by a container it is not removed
// This is called directly from the remote API
func (daemon *Daemon) VolumeRm(name string) error {
func (daemon *Daemon) VolumeRm(name string, force bool) error {
err := daemon.volumeRm(name)
if err == nil || force {
daemon.volumes.Purge(name)
return nil
}
return err
}

func (daemon *Daemon) volumeRm(name string) error {
v, err := daemon.volumes.Get(name)
if err != nil {
return err
Expand Down
1 change: 1 addition & 0 deletions docs/reference/api/docker_remote_api.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ This section lists each version from latest to oldest. Each listing includes a
* `POST /containers/create` now takes `AutoRemove` in HostConfig, to enable auto-removal of the container on daemon side when the container's process exits.
* `GET /containers/json` and `GET /containers/(id or name)/json` now return `"removing"` as a value for the `State.Status` field if the container is being removed. Previously, "exited" was returned as status.
* `GET /containers/json` now accepts `removing` as a valid value for the `status` filter.
* `DELETE /volumes/(name)` now accepts a `force` query parameter to force removal of volumes that were already removed out of band by the volume driver plugin.

### v1.24 API changes

Expand Down
5 changes: 5 additions & 0 deletions docs/reference/api/docker_remote_api_v1.25.md
Original file line number Diff line number Diff line change
Expand Up @@ -3062,6 +3062,11 @@ Instruct the driver to remove the volume (`name`).
HTTP/1.1 204 No Content
**Query Parameters**:
- **force** - 1/True/true or 0/False/false, Force the removal of the volume.
Default `false`.
**Status codes**:
- **204** - no error
Expand Down
3 changes: 2 additions & 1 deletion docs/reference/commandline/volume_rm.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,15 @@ parent = "smn_cli"
# volume rm

```markdown
Usage: docker volume rm VOLUME [VOLUME...]
Usage: docker volume rm [OPTIONS] VOLUME [VOLUME...]

Remove one or more volumes

Aliases:
rm, remove

Options:
-f, --force Force the removal of one or more volumes
--help Print usage
```

Expand Down
35 changes: 35 additions & 0 deletions integration-cli/docker_cli_volume_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -374,3 +374,38 @@ func (s *DockerSuite) TestVolumeCliLsFilterLabels(c *check.C) {
outArr = strings.Split(strings.TrimSpace(out), "\n")
c.Assert(len(outArr), check.Equals, 1, check.Commentf("\n%s", out))
}

func (s *DockerSuite) TestVolumeCliRmForceUsage(c *check.C) {
out, _ := dockerCmd(c, "volume", "create")
id := strings.TrimSpace(out)

dockerCmd(c, "volume", "rm", "-f", id)
dockerCmd(c, "volume", "rm", "--force", "nonexist")

out, _ = dockerCmd(c, "volume", "ls")
outArr := strings.Split(strings.TrimSpace(out), "\n")
c.Assert(len(outArr), check.Equals, 1, check.Commentf("%s\n", out))
}

func (s *DockerSuite) TestVolumeCliRmForce(c *check.C) {
testRequires(c, SameHostDaemon, DaemonIsLinux)

name := "test"
out, _ := dockerCmd(c, "volume", "create", "--name", name)
id := strings.TrimSpace(out)
c.Assert(id, checker.Equals, name)

out, _ = dockerCmd(c, "volume", "inspect", "--format", "{{.Mountpoint}}", name)
c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
// Mountpoint is in the form of "/var/lib/docker/volumes/.../_data", removing `/_data`
path := strings.TrimSuffix(strings.TrimSpace(out), "/_data")
out, _, err := runCommandWithOutput(exec.Command("rm", "-rf", path))
c.Assert(err, check.IsNil)

dockerCmd(c, "volume", "rm", "-f", "test")
out, _ = dockerCmd(c, "volume", "ls")
c.Assert(out, checker.Not(checker.Contains), name)
dockerCmd(c, "volume", "create", "--name", "test")
out, _ = dockerCmd(c, "volume", "ls")
c.Assert(out, checker.Contains, name)
}
6 changes: 4 additions & 2 deletions volume/store/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,9 @@ func (s *VolumeStore) setNamed(v volume.Volume, ref string) {
s.globalLock.Unlock()
}

func (s *VolumeStore) purge(name string) {
// Purge allows the cleanup of internal data on docker in case
// the internal data is out of sync with volumes driver plugins.
func (s *VolumeStore) Purge(name string) {
s.globalLock.Lock()
delete(s.names, name)
delete(s.refs, name)
Expand Down Expand Up @@ -425,7 +427,7 @@ func (s *VolumeStore) Remove(v volume.Volume) error {
return &OpErr{Err: err, Name: name, Op: "remove"}
}

s.purge(name)
s.Purge(name)
return nil
}

Expand Down

0 comments on commit 09b9290

Please sign in to comment.