integration-cli/docker_cli_volume_test.go
b3b7eb27
 package main
 
 import (
9ce8aac5
 	"fmt"
a488ad1a
 	"io/ioutil"
 	"os"
b3b7eb27
 	"os/exec"
a488ad1a
 	"path/filepath"
b3b7eb27
 	"strings"
 
0fd5a654
 	"github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/api/types/mount"
 	"github.com/docker/docker/api/types/network"
 	"github.com/docker/docker/client"
33968e6c
 	"github.com/docker/docker/integration-cli/checker"
50c4475d
 	"github.com/docker/docker/integration-cli/cli/build"
b3b7eb27
 	"github.com/go-check/check"
92427b3a
 	"github.com/gotestyourself/gotestyourself/icmd"
0fd5a654
 	"golang.org/x/net/context"
b3b7eb27
 )
 
7fb7a477
 func (s *DockerSuite) TestVolumeCLICreate(c *check.C) {
b3b7eb27
 	dockerCmd(c, "volume", "create")
 
def13fa2
 	_, _, err := dockerCmdWithError("volume", "create", "-d", "nosuchdriver")
 	c.Assert(err, check.NotNil)
b3b7eb27
 
ba3f0bf0
 	// test using hidden --name option
b3b7eb27
 	out, _ := dockerCmd(c, "volume", "create", "--name=test")
 	name := strings.TrimSpace(out)
 	c.Assert(name, check.Equals, "test")
ba3f0bf0
 
 	out, _ = dockerCmd(c, "volume", "create", "test2")
 	name = strings.TrimSpace(out)
 	c.Assert(name, check.Equals, "test2")
b3b7eb27
 }
 
7fb7a477
 func (s *DockerSuite) TestVolumeCLIInspect(c *check.C) {
b3b7eb27
 	c.Assert(
39bcaee4
 		exec.Command(dockerBinary, "volume", "inspect", "doesnotexist").Run(),
b3b7eb27
 		check.Not(check.IsNil),
899caaca
 		check.Commentf("volume inspect should error on non-existent volume"),
b3b7eb27
 	)
 
 	out, _ := dockerCmd(c, "volume", "create")
 	name := strings.TrimSpace(out)
667dcb0e
 	out, _ = dockerCmd(c, "volume", "inspect", "--format={{ .Name }}", name)
b3b7eb27
 	c.Assert(strings.TrimSpace(out), check.Equals, name)
 
ba3f0bf0
 	dockerCmd(c, "volume", "create", "test")
667dcb0e
 	out, _ = dockerCmd(c, "volume", "inspect", "--format={{ .Name }}", "test")
b3b7eb27
 	c.Assert(strings.TrimSpace(out), check.Equals, "test")
 }
 
7fb7a477
 func (s *DockerSuite) TestVolumeCLIInspectMulti(c *check.C) {
ba3f0bf0
 	dockerCmd(c, "volume", "create", "test1")
 	dockerCmd(c, "volume", "create", "test2")
fb1f22b0
 	dockerCmd(c, "volume", "create", "test3")
62953450
 
39bcaee4
 	result := dockerCmdWithResult("volume", "inspect", "--format={{ .Name }}", "test1", "test2", "doesnotexist", "test3")
92427b3a
 	result.Assert(c, icmd.Expected{
d7022f2b
 		ExitCode: 1,
39bcaee4
 		Err:      "No such volume: doesnotexist",
d7022f2b
 	})
 
 	out := result.Stdout()
57b67963
 	c.Assert(out, checker.Contains, "test1")
 	c.Assert(out, checker.Contains, "test2")
fb1f22b0
 	c.Assert(out, checker.Contains, "test3")
62953450
 }
 
7fb7a477
 func (s *DockerSuite) TestVolumeCLILs(c *check.C) {
382c96ee
 	prefix, _ := getPrefixAndSlashFromDaemonPlatform()
ba3f0bf0
 	dockerCmd(c, "volume", "create", "aaa")
b3b7eb27
 
ba3f0bf0
 	dockerCmd(c, "volume", "create", "test")
60ffd6c8
 
ba3f0bf0
 	dockerCmd(c, "volume", "create", "soo")
60ffd6c8
 	dockerCmd(c, "run", "-v", "soo:"+prefix+"/foo", "busybox", "ls", "/")
b3b7eb27
 
b7d8d2c4
 	out, _ := dockerCmd(c, "volume", "ls", "-q")
 	assertVolumesInList(c, out, []string{"aaa", "soo", "test"})
60ffd6c8
 }
 
a488ad1a
 func (s *DockerSuite) TestVolumeLsFormat(c *check.C) {
ba3f0bf0
 	dockerCmd(c, "volume", "create", "aaa")
 	dockerCmd(c, "volume", "create", "test")
 	dockerCmd(c, "volume", "create", "soo")
a488ad1a
 
 	out, _ := dockerCmd(c, "volume", "ls", "--format", "{{.Name}}")
b7d8d2c4
 	assertVolumesInList(c, out, []string{"aaa", "soo", "test"})
a488ad1a
 }
 
 func (s *DockerSuite) TestVolumeLsFormatDefaultFormat(c *check.C) {
ba3f0bf0
 	dockerCmd(c, "volume", "create", "aaa")
 	dockerCmd(c, "volume", "create", "test")
 	dockerCmd(c, "volume", "create", "soo")
a488ad1a
 
 	config := `{
 		"volumesFormat": "{{ .Name }} default"
 }`
 	d, err := ioutil.TempDir("", "integration-cli-")
 	c.Assert(err, checker.IsNil)
 	defer os.RemoveAll(d)
 
 	err = ioutil.WriteFile(filepath.Join(d, "config.json"), []byte(config), 0644)
 	c.Assert(err, checker.IsNil)
 
 	out, _ := dockerCmd(c, "--config", d, "volume", "ls")
b7d8d2c4
 	assertVolumesInList(c, out, []string{"aaa default", "soo default", "test default"})
a488ad1a
 }
 
60ffd6c8
 // assertVolList checks volume retrieved with ls command
 // equals to expected volume list
 // note: out should be `volume ls [option]` result
 func assertVolList(c *check.C, out string, expectVols []string) {
 	lines := strings.Split(out, "\n")
 	var volList []string
 	for _, line := range lines[1 : len(lines)-1] {
 		volFields := strings.Fields(line)
 		// wrap all volume name in volList
 		volList = append(volList, volFields[1])
 	}
 
 	// volume ls should contains all expected volumes
 	c.Assert(volList, checker.DeepEquals, expectVols)
b3b7eb27
 }
 
b7d8d2c4
 func assertVolumesInList(c *check.C, out string, expected []string) {
 	lines := strings.Split(strings.TrimSpace(string(out)), "\n")
 	for _, expect := range expected {
 		found := false
 		for _, v := range lines {
 			found = v == expect
 			if found {
 				break
 			}
 		}
 		c.Assert(found, checker.Equals, true, check.Commentf("Expected volume not found: %v, got: %v", expect, lines))
 	}
 }
 
7fb7a477
 func (s *DockerSuite) TestVolumeCLILsFilterDangling(c *check.C) {
382c96ee
 	prefix, _ := getPrefixAndSlashFromDaemonPlatform()
ba3f0bf0
 	dockerCmd(c, "volume", "create", "testnotinuse1")
 	dockerCmd(c, "volume", "create", "testisinuse1")
 	dockerCmd(c, "volume", "create", "testisinuse2")
1cbf9047
 
 	// Make sure both "created" (but not started), and started
 	// containers are included in reference counting
2af5034c
 	dockerCmd(c, "run", "--name", "volume-test1", "-v", "testisinuse1:"+prefix+"/foo", "busybox", "true")
 	dockerCmd(c, "create", "--name", "volume-test2", "-v", "testisinuse2:"+prefix+"/foo", "busybox", "true")
1cbf9047
 
 	out, _ := dockerCmd(c, "volume", "ls")
 
 	// No filter, all volumes should show
 	c.Assert(out, checker.Contains, "testnotinuse1\n", check.Commentf("expected volume 'testnotinuse1' in output"))
 	c.Assert(out, checker.Contains, "testisinuse1\n", check.Commentf("expected volume 'testisinuse1' in output"))
 	c.Assert(out, checker.Contains, "testisinuse2\n", check.Commentf("expected volume 'testisinuse2' in output"))
 
 	out, _ = dockerCmd(c, "volume", "ls", "--filter", "dangling=false")
 
1431b623
 	// Explicitly disabling dangling
 	c.Assert(out, check.Not(checker.Contains), "testnotinuse1\n", check.Commentf("expected volume 'testnotinuse1' in output"))
1cbf9047
 	c.Assert(out, checker.Contains, "testisinuse1\n", check.Commentf("expected volume 'testisinuse1' in output"))
 	c.Assert(out, checker.Contains, "testisinuse2\n", check.Commentf("expected volume 'testisinuse2' in output"))
 
 	out, _ = dockerCmd(c, "volume", "ls", "--filter", "dangling=true")
 
927b334e
 	// Filter "dangling" volumes; only "dangling" (unused) volumes should be in the output
1cbf9047
 	c.Assert(out, checker.Contains, "testnotinuse1\n", check.Commentf("expected volume 'testnotinuse1' in output"))
 	c.Assert(out, check.Not(checker.Contains), "testisinuse1\n", check.Commentf("volume 'testisinuse1' in output, but not expected"))
 	c.Assert(out, check.Not(checker.Contains), "testisinuse2\n", check.Commentf("volume 'testisinuse2' in output, but not expected"))
66c253cb
 
 	out, _ = dockerCmd(c, "volume", "ls", "--filter", "dangling=1")
 	// Filter "dangling" volumes; only "dangling" (unused) volumes should be in the output, dangling also accept 1
 	c.Assert(out, checker.Contains, "testnotinuse1\n", check.Commentf("expected volume 'testnotinuse1' in output"))
 	c.Assert(out, check.Not(checker.Contains), "testisinuse1\n", check.Commentf("volume 'testisinuse1' in output, but not expected"))
 	c.Assert(out, check.Not(checker.Contains), "testisinuse2\n", check.Commentf("volume 'testisinuse2' in output, but not expected"))
 
 	out, _ = dockerCmd(c, "volume", "ls", "--filter", "dangling=0")
 	// dangling=0 is same as dangling=false case
1431b623
 	c.Assert(out, check.Not(checker.Contains), "testnotinuse1\n", check.Commentf("expected volume 'testnotinuse1' in output"))
66c253cb
 	c.Assert(out, checker.Contains, "testisinuse1\n", check.Commentf("expected volume 'testisinuse1' in output"))
 	c.Assert(out, checker.Contains, "testisinuse2\n", check.Commentf("expected volume 'testisinuse2' in output"))
8e9305ef
 
 	out, _ = dockerCmd(c, "volume", "ls", "--filter", "name=testisin")
 	c.Assert(out, check.Not(checker.Contains), "testnotinuse1\n", check.Commentf("expected volume 'testnotinuse1' in output"))
39bcaee4
 	c.Assert(out, checker.Contains, "testisinuse1\n", check.Commentf("expected volume 'testisinuse1' in output"))
8e9305ef
 	c.Assert(out, checker.Contains, "testisinuse2\n", check.Commentf("expected volume 'testisinuse2' in output"))
66c253cb
 }
 
7fb7a477
 func (s *DockerSuite) TestVolumeCLILsErrorWithInvalidFilterName(c *check.C) {
66c253cb
 	out, _, err := dockerCmdWithError("volume", "ls", "-f", "FOO=123")
 	c.Assert(err, checker.NotNil)
 	c.Assert(out, checker.Contains, "Invalid filter")
 }
 
7fb7a477
 func (s *DockerSuite) TestVolumeCLILsWithIncorrectFilterValue(c *check.C) {
66c253cb
 	out, _, err := dockerCmdWithError("volume", "ls", "-f", "dangling=invalid")
 	c.Assert(err, check.NotNil)
 	c.Assert(out, checker.Contains, "Invalid filter")
1cbf9047
 }
 
7fb7a477
 func (s *DockerSuite) TestVolumeCLIRm(c *check.C) {
382c96ee
 	prefix, _ := getPrefixAndSlashFromDaemonPlatform()
b3b7eb27
 	out, _ := dockerCmd(c, "volume", "create")
 	id := strings.TrimSpace(out)
 
ba3f0bf0
 	dockerCmd(c, "volume", "create", "test")
b3b7eb27
 	dockerCmd(c, "volume", "rm", id)
 	dockerCmd(c, "volume", "rm", "test")
 
 	volumeID := "testing"
2af5034c
 	dockerCmd(c, "run", "-v", volumeID+":"+prefix+"/foo", "--name=test", "busybox", "sh", "-c", "echo hello > /foo/bar")
87e3fcfe
 
 	icmd.RunCommand(dockerBinary, "volume", "rm", "testing").Assert(c, icmd.Expected{
 		ExitCode: 1,
303b1d20
 		Error:    "exit status 1",
87e3fcfe
 	})
b3b7eb27
 
 	out, _ = dockerCmd(c, "run", "--volumes-from=test", "--name=test2", "busybox", "sh", "-c", "cat /foo/bar")
 	c.Assert(strings.TrimSpace(out), check.Equals, "hello")
 	dockerCmd(c, "rm", "-fv", "test2")
 	dockerCmd(c, "volume", "inspect", volumeID)
 	dockerCmd(c, "rm", "-f", "test")
 
2af5034c
 	out, _ = dockerCmd(c, "run", "--name=test2", "-v", volumeID+":"+prefix+"/foo", "busybox", "sh", "-c", "cat /foo/bar")
b3b7eb27
 	c.Assert(strings.TrimSpace(out), check.Equals, "hello", check.Commentf("volume data was removed"))
 	dockerCmd(c, "rm", "test2")
 
 	dockerCmd(c, "volume", "rm", volumeID)
 	c.Assert(
39bcaee4
 		exec.Command("volume", "rm", "doesnotexist").Run(),
b3b7eb27
 		check.Not(check.IsNil),
899caaca
 		check.Commentf("volume rm should fail with non-existent volume"),
b3b7eb27
 	)
 }
38da4318
 
def13fa2
 // FIXME(vdemeester) should be a unit test in cli/command/volume package
7fb7a477
 func (s *DockerSuite) TestVolumeCLINoArgs(c *check.C) {
38da4318
 	out, _ := dockerCmd(c, "volume")
2feebd95
 	// no args should produce the cmd usage output
667dcb0e
 	usage := "Usage:	docker volume COMMAND"
2feebd95
 	c.Assert(out, checker.Contains, usage)
38da4318
 
 	// invalid arg should error and show the command usage on stderr
def13fa2
 	icmd.RunCommand(dockerBinary, "volume", "somearg").Assert(c, icmd.Expected{
 		ExitCode: 1,
 		Error:    "exit status 1",
 		Err:      usage,
 	})
2feebd95
 
 	// invalid flag should error and show the flag error and cmd usage
def13fa2
 	result := icmd.RunCommand(dockerBinary, "volume", "--no-such-flag")
 	result.Assert(c, icmd.Expected{
 		ExitCode: 125,
 		Error:    "exit status 125",
 		Err:      usage,
 	})
 	c.Assert(result.Stderr(), checker.Contains, "unknown flag: --no-such-flag")
38da4318
 }
b9d30280
 
7fb7a477
 func (s *DockerSuite) TestVolumeCLIInspectTmplError(c *check.C) {
b9d30280
 	out, _ := dockerCmd(c, "volume", "create")
 	name := strings.TrimSpace(out)
 
 	out, exitCode, err := dockerCmdWithError("volume", "inspect", "--format='{{ .FooBar }}'", name)
 	c.Assert(err, checker.NotNil, check.Commentf("Output: %s", out))
 	c.Assert(exitCode, checker.Equals, 1, check.Commentf("Output: %s", out))
 	c.Assert(out, checker.Contains, "Template parsing error")
 }
b05b2370
 
7fb7a477
 func (s *DockerSuite) TestVolumeCLICreateWithOpts(c *check.C) {
b05b2370
 	testRequires(c, DaemonIsLinux)
 
ba3f0bf0
 	dockerCmd(c, "volume", "create", "-d", "local", "test", "--opt=type=tmpfs", "--opt=device=tmpfs", "--opt=o=size=1m,uid=1000")
b05b2370
 	out, _ := dockerCmd(c, "run", "-v", "test:/foo", "busybox", "mount")
 
 	mounts := strings.Split(out, "\n")
 	var found bool
 	for _, m := range mounts {
 		if strings.Contains(m, "/foo") {
 			found = true
 			info := strings.Fields(m)
 			// tmpfs on <path> type tmpfs (rw,relatime,size=1024k,uid=1000)
 			c.Assert(info[0], checker.Equals, "tmpfs")
 			c.Assert(info[2], checker.Equals, "/foo")
 			c.Assert(info[4], checker.Equals, "tmpfs")
 			c.Assert(info[5], checker.Contains, "uid=1000")
 			c.Assert(info[5], checker.Contains, "size=1024k")
feadc517
 			break
b05b2370
 		}
 	}
 	c.Assert(found, checker.Equals, true)
 }
fc214b44
 
7fb7a477
 func (s *DockerSuite) TestVolumeCLICreateLabel(c *check.C) {
fc214b44
 	testVol := "testvolcreatelabel"
 	testLabel := "foo"
 	testValue := "bar"
 
ba3f0bf0
 	out, _, err := dockerCmdWithError("volume", "create", "--label", testLabel+"="+testValue, testVol)
fc214b44
 	c.Assert(err, check.IsNil)
 
667dcb0e
 	out, _ = dockerCmd(c, "volume", "inspect", "--format={{ .Labels."+testLabel+" }}", testVol)
fc214b44
 	c.Assert(strings.TrimSpace(out), check.Equals, testValue)
 }
 
7fb7a477
 func (s *DockerSuite) TestVolumeCLICreateLabelMultiple(c *check.C) {
fc214b44
 	testVol := "testvolcreatelabel"
 
 	testLabels := map[string]string{
 		"foo": "bar",
 		"baz": "foo",
 	}
 
 	args := []string{
 		"volume",
 		"create",
 		testVol,
 	}
 
 	for k, v := range testLabels {
 		args = append(args, "--label", k+"="+v)
 	}
 
 	out, _, err := dockerCmdWithError(args...)
 	c.Assert(err, check.IsNil)
 
 	for k, v := range testLabels {
667dcb0e
 		out, _ = dockerCmd(c, "volume", "inspect", "--format={{ .Labels."+k+" }}", testVol)
fc214b44
 		c.Assert(strings.TrimSpace(out), check.Equals, v)
 	}
 }
1a72934c
 
7fb7a477
 func (s *DockerSuite) TestVolumeCLILsFilterLabels(c *check.C) {
1a72934c
 	testVol1 := "testvolcreatelabel-1"
ba3f0bf0
 	out, _, err := dockerCmdWithError("volume", "create", "--label", "foo=bar1", testVol1)
1a72934c
 	c.Assert(err, check.IsNil)
 
 	testVol2 := "testvolcreatelabel-2"
ba3f0bf0
 	out, _, err = dockerCmdWithError("volume", "create", "--label", "foo=bar2", testVol2)
1a72934c
 	c.Assert(err, check.IsNil)
 
 	out, _ = dockerCmd(c, "volume", "ls", "--filter", "label=foo")
 
 	// filter with label=key
 	c.Assert(out, checker.Contains, "testvolcreatelabel-1\n", check.Commentf("expected volume 'testvolcreatelabel-1' in output"))
 	c.Assert(out, checker.Contains, "testvolcreatelabel-2\n", check.Commentf("expected volume 'testvolcreatelabel-2' in output"))
 
 	out, _ = dockerCmd(c, "volume", "ls", "--filter", "label=foo=bar1")
 
 	// filter with label=key=value
 	c.Assert(out, checker.Contains, "testvolcreatelabel-1\n", check.Commentf("expected volume 'testvolcreatelabel-1' in output"))
 	c.Assert(out, check.Not(checker.Contains), "testvolcreatelabel-2\n", check.Commentf("expected volume 'testvolcreatelabel-2 in output"))
 
 	out, _ = dockerCmd(c, "volume", "ls", "--filter", "label=non-exist")
 	outArr := strings.Split(strings.TrimSpace(out), "\n")
 	c.Assert(len(outArr), check.Equals, 1, check.Commentf("\n%s", out))
 
 	out, _ = dockerCmd(c, "volume", "ls", "--filter", "label=foo=non-exist")
 	outArr = strings.Split(strings.TrimSpace(out), "\n")
 	c.Assert(len(outArr), check.Equals, 1, check.Commentf("\n%s", out))
 }
6c5c34d5
 
0fdab496
 func (s *DockerSuite) TestVolumeCLILsFilterDrivers(c *check.C) {
 	// using default volume driver local to create volumes
 	testVol1 := "testvol-1"
 	out, _, err := dockerCmdWithError("volume", "create", testVol1)
 	c.Assert(err, check.IsNil)
 
 	testVol2 := "testvol-2"
 	out, _, err = dockerCmdWithError("volume", "create", testVol2)
 	c.Assert(err, check.IsNil)
 
 	// filter with driver=local
 	out, _ = dockerCmd(c, "volume", "ls", "--filter", "driver=local")
 	c.Assert(out, checker.Contains, "testvol-1\n", check.Commentf("expected volume 'testvol-1' in output"))
 	c.Assert(out, checker.Contains, "testvol-2\n", check.Commentf("expected volume 'testvol-2' in output"))
 
 	// filter with driver=invaliddriver
 	out, _ = dockerCmd(c, "volume", "ls", "--filter", "driver=invaliddriver")
 	outArr := strings.Split(strings.TrimSpace(out), "\n")
 	c.Assert(len(outArr), check.Equals, 1, check.Commentf("\n%s", out))
 
 	// filter with driver=loca
 	out, _ = dockerCmd(c, "volume", "ls", "--filter", "driver=loca")
 	outArr = strings.Split(strings.TrimSpace(out), "\n")
 	c.Assert(len(outArr), check.Equals, 1, check.Commentf("\n%s", out))
 
 	// filter with driver=
 	out, _ = dockerCmd(c, "volume", "ls", "--filter", "driver=")
 	outArr = strings.Split(strings.TrimSpace(out), "\n")
 	c.Assert(len(outArr), check.Equals, 1, check.Commentf("\n%s", out))
 }
 
7fb7a477
 func (s *DockerSuite) TestVolumeCLIRmForceUsage(c *check.C) {
6c5c34d5
 	out, _ := dockerCmd(c, "volume", "create")
 	id := strings.TrimSpace(out)
 
 	dockerCmd(c, "volume", "rm", "-f", id)
 	dockerCmd(c, "volume", "rm", "--force", "nonexist")
 }
 
7fb7a477
 func (s *DockerSuite) TestVolumeCLIRmForce(c *check.C) {
6c5c34d5
 	testRequires(c, SameHostDaemon, DaemonIsLinux)
 
 	name := "test"
ba3f0bf0
 	out, _ := dockerCmd(c, "volume", "create", name)
6c5c34d5
 	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")
87e3fcfe
 	icmd.RunCommand("rm", "-rf", path).Assert(c, icmd.Success)
6c5c34d5
 
9d521a4d
 	dockerCmd(c, "volume", "rm", "-f", name)
6c5c34d5
 	out, _ = dockerCmd(c, "volume", "ls")
 	c.Assert(out, checker.Not(checker.Contains), name)
9d521a4d
 	dockerCmd(c, "volume", "create", name)
6c5c34d5
 	out, _ = dockerCmd(c, "volume", "ls")
 	c.Assert(out, checker.Contains, name)
 }
9ce8aac5
 
9d521a4d
 // TestVolumeCLIRmForceInUse verifies that repeated `docker volume rm -f` calls does not remove a volume
 // if it is in use. Test case for https://github.com/docker/docker/issues/31446
 func (s *DockerSuite) TestVolumeCLIRmForceInUse(c *check.C) {
 	name := "testvolume"
 	out, _ := dockerCmd(c, "volume", "create", name)
 	id := strings.TrimSpace(out)
 	c.Assert(id, checker.Equals, name)
 
 	prefix, slash := getPrefixAndSlashFromDaemonPlatform()
 	out, e := dockerCmd(c, "create", "-v", "testvolume:"+prefix+slash+"foo", "busybox")
 	cid := strings.TrimSpace(out)
 
 	_, _, err := dockerCmdWithError("volume", "rm", "-f", name)
 	c.Assert(err, check.NotNil)
 	c.Assert(err.Error(), checker.Contains, "volume is in use")
 	out, _ = dockerCmd(c, "volume", "ls")
 	c.Assert(out, checker.Contains, name)
 
 	// The original issue did not _remove_ the volume from the list
 	// the first time. But a second call to `volume rm` removed it.
 	// Calling `volume rm` a second time to confirm it's not removed
 	// when calling twice.
 	_, _, err = dockerCmdWithError("volume", "rm", "-f", name)
 	c.Assert(err, check.NotNil)
 	c.Assert(err.Error(), checker.Contains, "volume is in use")
 	out, _ = dockerCmd(c, "volume", "ls")
 	c.Assert(out, checker.Contains, name)
 
 	// Verify removing the volume after the container is removed works
 	_, e = dockerCmd(c, "rm", cid)
 	c.Assert(e, check.Equals, 0)
 
 	_, e = dockerCmd(c, "volume", "rm", "-f", name)
 	c.Assert(e, check.Equals, 0)
 
 	out, e = dockerCmd(c, "volume", "ls")
 	c.Assert(e, check.Equals, 0)
 	c.Assert(out, checker.Not(checker.Contains), name)
 }
 
9ce8aac5
 func (s *DockerSuite) TestVolumeCliInspectWithVolumeOpts(c *check.C) {
 	testRequires(c, DaemonIsLinux)
 
 	// Without options
 	name := "test1"
 	dockerCmd(c, "volume", "create", "-d", "local", name)
 	out, _ := dockerCmd(c, "volume", "inspect", "--format={{ .Options }}", name)
 	c.Assert(strings.TrimSpace(out), checker.Contains, "map[]")
 
 	// With options
 	name = "test2"
 	k1, v1 := "type", "tmpfs"
 	k2, v2 := "device", "tmpfs"
 	k3, v3 := "o", "size=1m,uid=1000"
 	dockerCmd(c, "volume", "create", "-d", "local", name, "--opt", fmt.Sprintf("%s=%s", k1, v1), "--opt", fmt.Sprintf("%s=%s", k2, v2), "--opt", fmt.Sprintf("%s=%s", k3, v3))
 	out, _ = dockerCmd(c, "volume", "inspect", "--format={{ .Options }}", name)
 	c.Assert(strings.TrimSpace(out), checker.Contains, fmt.Sprintf("%s:%s", k1, v1))
 	c.Assert(strings.TrimSpace(out), checker.Contains, fmt.Sprintf("%s:%s", k2, v2))
 	c.Assert(strings.TrimSpace(out), checker.Contains, fmt.Sprintf("%s:%s", k3, v3))
 }
9526e5c6
 
 // Test case (1) for 21845: duplicate targets for --volumes-from
 func (s *DockerSuite) TestDuplicateMountpointsForVolumesFrom(c *check.C) {
 	testRequires(c, DaemonIsLinux)
 
 	image := "vimage"
50c4475d
 	buildImageSuccessfully(c, image, build.WithDockerfile(`
9526e5c6
 		FROM busybox
 		VOLUME ["/tmp/data"]`))
 
 	dockerCmd(c, "run", "--name=data1", image, "true")
 	dockerCmd(c, "run", "--name=data2", image, "true")
 
 	out, _ := dockerCmd(c, "inspect", "--format", "{{(index .Mounts 0).Name}}", "data1")
 	data1 := strings.TrimSpace(out)
 	c.Assert(data1, checker.Not(checker.Equals), "")
 
 	out, _ = dockerCmd(c, "inspect", "--format", "{{(index .Mounts 0).Name}}", "data2")
 	data2 := strings.TrimSpace(out)
 	c.Assert(data2, checker.Not(checker.Equals), "")
 
 	// Both volume should exist
 	out, _ = dockerCmd(c, "volume", "ls", "-q")
 	c.Assert(strings.TrimSpace(out), checker.Contains, data1)
 	c.Assert(strings.TrimSpace(out), checker.Contains, data2)
 
 	out, _, err := dockerCmdWithError("run", "--name=app", "--volumes-from=data1", "--volumes-from=data2", "-d", "busybox", "top")
 	c.Assert(err, checker.IsNil, check.Commentf("Out: %s", out))
 
 	// Only the second volume will be referenced, this is backward compatible
 	out, _ = dockerCmd(c, "inspect", "--format", "{{(index .Mounts 0).Name}}", "app")
 	c.Assert(strings.TrimSpace(out), checker.Equals, data2)
 
 	dockerCmd(c, "rm", "-f", "-v", "app")
 	dockerCmd(c, "rm", "-f", "-v", "data1")
 	dockerCmd(c, "rm", "-f", "-v", "data2")
 
 	// Both volume should not exist
 	out, _ = dockerCmd(c, "volume", "ls", "-q")
 	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), data1)
 	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), data2)
 }
 
 // Test case (2) for 21845: duplicate targets for --volumes-from and -v (bind)
 func (s *DockerSuite) TestDuplicateMountpointsForVolumesFromAndBind(c *check.C) {
 	testRequires(c, DaemonIsLinux)
 
 	image := "vimage"
50c4475d
 	buildImageSuccessfully(c, image, build.WithDockerfile(`
9526e5c6
                 FROM busybox
                 VOLUME ["/tmp/data"]`))
 
 	dockerCmd(c, "run", "--name=data1", image, "true")
 	dockerCmd(c, "run", "--name=data2", image, "true")
 
 	out, _ := dockerCmd(c, "inspect", "--format", "{{(index .Mounts 0).Name}}", "data1")
 	data1 := strings.TrimSpace(out)
 	c.Assert(data1, checker.Not(checker.Equals), "")
 
 	out, _ = dockerCmd(c, "inspect", "--format", "{{(index .Mounts 0).Name}}", "data2")
 	data2 := strings.TrimSpace(out)
 	c.Assert(data2, checker.Not(checker.Equals), "")
 
 	// Both volume should exist
 	out, _ = dockerCmd(c, "volume", "ls", "-q")
 	c.Assert(strings.TrimSpace(out), checker.Contains, data1)
 	c.Assert(strings.TrimSpace(out), checker.Contains, data2)
 
d610d3f6
 	// /tmp/data is automatically created, because we are not using the modern mount API here
9526e5c6
 	out, _, err := dockerCmdWithError("run", "--name=app", "--volumes-from=data1", "--volumes-from=data2", "-v", "/tmp/data:/tmp/data", "-d", "busybox", "top")
 	c.Assert(err, checker.IsNil, check.Commentf("Out: %s", out))
 
 	// No volume will be referenced (mount is /tmp/data), this is backward compatible
 	out, _ = dockerCmd(c, "inspect", "--format", "{{(index .Mounts 0).Name}}", "app")
 	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), data1)
 	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), data2)
 
 	dockerCmd(c, "rm", "-f", "-v", "app")
 	dockerCmd(c, "rm", "-f", "-v", "data1")
 	dockerCmd(c, "rm", "-f", "-v", "data2")
 
 	// Both volume should not exist
 	out, _ = dockerCmd(c, "volume", "ls", "-q")
 	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), data1)
 	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), data2)
 }
 
 // Test case (3) for 21845: duplicate targets for --volumes-from and `Mounts` (API only)
 func (s *DockerSuite) TestDuplicateMountpointsForVolumesFromAndMounts(c *check.C) {
d610d3f6
 	testRequires(c, SameHostDaemon, DaemonIsLinux)
9526e5c6
 
 	image := "vimage"
50c4475d
 	buildImageSuccessfully(c, image, build.WithDockerfile(`
9526e5c6
                 FROM busybox
                 VOLUME ["/tmp/data"]`))
 
 	dockerCmd(c, "run", "--name=data1", image, "true")
 	dockerCmd(c, "run", "--name=data2", image, "true")
 
 	out, _ := dockerCmd(c, "inspect", "--format", "{{(index .Mounts 0).Name}}", "data1")
 	data1 := strings.TrimSpace(out)
 	c.Assert(data1, checker.Not(checker.Equals), "")
 
 	out, _ = dockerCmd(c, "inspect", "--format", "{{(index .Mounts 0).Name}}", "data2")
 	data2 := strings.TrimSpace(out)
 	c.Assert(data2, checker.Not(checker.Equals), "")
 
 	// Both volume should exist
 	out, _ = dockerCmd(c, "volume", "ls", "-q")
 	c.Assert(strings.TrimSpace(out), checker.Contains, data1)
 	c.Assert(strings.TrimSpace(out), checker.Contains, data2)
 
d610d3f6
 	err := os.MkdirAll("/tmp/data", 0755)
 	c.Assert(err, checker.IsNil)
9526e5c6
 	// Mounts is available in API
0fd5a654
 	cli, err := client.NewEnvClient()
 	c.Assert(err, checker.IsNil)
 	defer cli.Close()
 
 	config := container.Config{
 		Cmd:   []string{"top"},
 		Image: "busybox",
 	}
 
 	hostConfig := container.HostConfig{
 		VolumesFrom: []string{"data1", "data2"},
 		Mounts: []mount.Mount{
 			{
 				Type:   "bind",
 				Source: "/tmp/data",
 				Target: "/tmp/data",
9526e5c6
 			},
0fd5a654
 		},
 	}
 	_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &network.NetworkingConfig{}, "app")
 
 	c.Assert(err, checker.IsNil)
9526e5c6
 
 	// No volume will be referenced (mount is /tmp/data), this is backward compatible
 	out, _ = dockerCmd(c, "inspect", "--format", "{{(index .Mounts 0).Name}}", "app")
 	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), data1)
 	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), data2)
 
 	dockerCmd(c, "rm", "-f", "-v", "app")
 	dockerCmd(c, "rm", "-f", "-v", "data1")
 	dockerCmd(c, "rm", "-f", "-v", "data2")
 
 	// Both volume should not exist
 	out, _ = dockerCmd(c, "volume", "ls", "-q")
 	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), data1)
 	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), data2)
 }