github.com/OpenFlowLabs/moby@v17.12.1-ce-rc2+incompatible/integration-cli/docker_cli_volume_test.go (about)

     1  package main
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"os"
     7  	"os/exec"
     8  	"path/filepath"
     9  	"strings"
    10  
    11  	"github.com/docker/docker/api/types/container"
    12  	"github.com/docker/docker/api/types/mount"
    13  	"github.com/docker/docker/api/types/network"
    14  	"github.com/docker/docker/client"
    15  	"github.com/docker/docker/integration-cli/checker"
    16  	"github.com/docker/docker/integration-cli/cli/build"
    17  	"github.com/go-check/check"
    18  	"github.com/gotestyourself/gotestyourself/icmd"
    19  	"golang.org/x/net/context"
    20  )
    21  
    22  func (s *DockerSuite) TestVolumeCLICreate(c *check.C) {
    23  	dockerCmd(c, "volume", "create")
    24  
    25  	_, _, err := dockerCmdWithError("volume", "create", "-d", "nosuchdriver")
    26  	c.Assert(err, check.NotNil)
    27  
    28  	// test using hidden --name option
    29  	out, _ := dockerCmd(c, "volume", "create", "--name=test")
    30  	name := strings.TrimSpace(out)
    31  	c.Assert(name, check.Equals, "test")
    32  
    33  	out, _ = dockerCmd(c, "volume", "create", "test2")
    34  	name = strings.TrimSpace(out)
    35  	c.Assert(name, check.Equals, "test2")
    36  }
    37  
    38  func (s *DockerSuite) TestVolumeCLIInspect(c *check.C) {
    39  	c.Assert(
    40  		exec.Command(dockerBinary, "volume", "inspect", "doesnotexist").Run(),
    41  		check.Not(check.IsNil),
    42  		check.Commentf("volume inspect should error on non-existent volume"),
    43  	)
    44  
    45  	out, _ := dockerCmd(c, "volume", "create")
    46  	name := strings.TrimSpace(out)
    47  	out, _ = dockerCmd(c, "volume", "inspect", "--format={{ .Name }}", name)
    48  	c.Assert(strings.TrimSpace(out), check.Equals, name)
    49  
    50  	dockerCmd(c, "volume", "create", "test")
    51  	out, _ = dockerCmd(c, "volume", "inspect", "--format={{ .Name }}", "test")
    52  	c.Assert(strings.TrimSpace(out), check.Equals, "test")
    53  }
    54  
    55  func (s *DockerSuite) TestVolumeCLIInspectMulti(c *check.C) {
    56  	dockerCmd(c, "volume", "create", "test1")
    57  	dockerCmd(c, "volume", "create", "test2")
    58  	dockerCmd(c, "volume", "create", "test3")
    59  
    60  	result := dockerCmdWithResult("volume", "inspect", "--format={{ .Name }}", "test1", "test2", "doesnotexist", "test3")
    61  	result.Assert(c, icmd.Expected{
    62  		ExitCode: 1,
    63  		Err:      "No such volume: doesnotexist",
    64  	})
    65  
    66  	out := result.Stdout()
    67  	c.Assert(out, checker.Contains, "test1")
    68  	c.Assert(out, checker.Contains, "test2")
    69  	c.Assert(out, checker.Contains, "test3")
    70  }
    71  
    72  func (s *DockerSuite) TestVolumeCLILs(c *check.C) {
    73  	prefix, _ := getPrefixAndSlashFromDaemonPlatform()
    74  	dockerCmd(c, "volume", "create", "aaa")
    75  
    76  	dockerCmd(c, "volume", "create", "test")
    77  
    78  	dockerCmd(c, "volume", "create", "soo")
    79  	dockerCmd(c, "run", "-v", "soo:"+prefix+"/foo", "busybox", "ls", "/")
    80  
    81  	out, _ := dockerCmd(c, "volume", "ls", "-q")
    82  	assertVolumesInList(c, out, []string{"aaa", "soo", "test"})
    83  }
    84  
    85  func (s *DockerSuite) TestVolumeLsFormat(c *check.C) {
    86  	dockerCmd(c, "volume", "create", "aaa")
    87  	dockerCmd(c, "volume", "create", "test")
    88  	dockerCmd(c, "volume", "create", "soo")
    89  
    90  	out, _ := dockerCmd(c, "volume", "ls", "--format", "{{.Name}}")
    91  	assertVolumesInList(c, out, []string{"aaa", "soo", "test"})
    92  }
    93  
    94  func (s *DockerSuite) TestVolumeLsFormatDefaultFormat(c *check.C) {
    95  	dockerCmd(c, "volume", "create", "aaa")
    96  	dockerCmd(c, "volume", "create", "test")
    97  	dockerCmd(c, "volume", "create", "soo")
    98  
    99  	config := `{
   100  		"volumesFormat": "{{ .Name }} default"
   101  }`
   102  	d, err := ioutil.TempDir("", "integration-cli-")
   103  	c.Assert(err, checker.IsNil)
   104  	defer os.RemoveAll(d)
   105  
   106  	err = ioutil.WriteFile(filepath.Join(d, "config.json"), []byte(config), 0644)
   107  	c.Assert(err, checker.IsNil)
   108  
   109  	out, _ := dockerCmd(c, "--config", d, "volume", "ls")
   110  	assertVolumesInList(c, out, []string{"aaa default", "soo default", "test default"})
   111  }
   112  
   113  // assertVolList checks volume retrieved with ls command
   114  // equals to expected volume list
   115  // note: out should be `volume ls [option]` result
   116  func assertVolList(c *check.C, out string, expectVols []string) {
   117  	lines := strings.Split(out, "\n")
   118  	var volList []string
   119  	for _, line := range lines[1 : len(lines)-1] {
   120  		volFields := strings.Fields(line)
   121  		// wrap all volume name in volList
   122  		volList = append(volList, volFields[1])
   123  	}
   124  
   125  	// volume ls should contains all expected volumes
   126  	c.Assert(volList, checker.DeepEquals, expectVols)
   127  }
   128  
   129  func assertVolumesInList(c *check.C, out string, expected []string) {
   130  	lines := strings.Split(strings.TrimSpace(string(out)), "\n")
   131  	for _, expect := range expected {
   132  		found := false
   133  		for _, v := range lines {
   134  			found = v == expect
   135  			if found {
   136  				break
   137  			}
   138  		}
   139  		c.Assert(found, checker.Equals, true, check.Commentf("Expected volume not found: %v, got: %v", expect, lines))
   140  	}
   141  }
   142  
   143  func (s *DockerSuite) TestVolumeCLILsFilterDangling(c *check.C) {
   144  	prefix, _ := getPrefixAndSlashFromDaemonPlatform()
   145  	dockerCmd(c, "volume", "create", "testnotinuse1")
   146  	dockerCmd(c, "volume", "create", "testisinuse1")
   147  	dockerCmd(c, "volume", "create", "testisinuse2")
   148  
   149  	// Make sure both "created" (but not started), and started
   150  	// containers are included in reference counting
   151  	dockerCmd(c, "run", "--name", "volume-test1", "-v", "testisinuse1:"+prefix+"/foo", "busybox", "true")
   152  	dockerCmd(c, "create", "--name", "volume-test2", "-v", "testisinuse2:"+prefix+"/foo", "busybox", "true")
   153  
   154  	out, _ := dockerCmd(c, "volume", "ls")
   155  
   156  	// No filter, all volumes should show
   157  	c.Assert(out, checker.Contains, "testnotinuse1\n", check.Commentf("expected volume 'testnotinuse1' in output"))
   158  	c.Assert(out, checker.Contains, "testisinuse1\n", check.Commentf("expected volume 'testisinuse1' in output"))
   159  	c.Assert(out, checker.Contains, "testisinuse2\n", check.Commentf("expected volume 'testisinuse2' in output"))
   160  
   161  	out, _ = dockerCmd(c, "volume", "ls", "--filter", "dangling=false")
   162  
   163  	// Explicitly disabling dangling
   164  	c.Assert(out, check.Not(checker.Contains), "testnotinuse1\n", check.Commentf("expected volume 'testnotinuse1' in output"))
   165  	c.Assert(out, checker.Contains, "testisinuse1\n", check.Commentf("expected volume 'testisinuse1' in output"))
   166  	c.Assert(out, checker.Contains, "testisinuse2\n", check.Commentf("expected volume 'testisinuse2' in output"))
   167  
   168  	out, _ = dockerCmd(c, "volume", "ls", "--filter", "dangling=true")
   169  
   170  	// Filter "dangling" volumes; only "dangling" (unused) volumes should be in the output
   171  	c.Assert(out, checker.Contains, "testnotinuse1\n", check.Commentf("expected volume 'testnotinuse1' in output"))
   172  	c.Assert(out, check.Not(checker.Contains), "testisinuse1\n", check.Commentf("volume 'testisinuse1' in output, but not expected"))
   173  	c.Assert(out, check.Not(checker.Contains), "testisinuse2\n", check.Commentf("volume 'testisinuse2' in output, but not expected"))
   174  
   175  	out, _ = dockerCmd(c, "volume", "ls", "--filter", "dangling=1")
   176  	// Filter "dangling" volumes; only "dangling" (unused) volumes should be in the output, dangling also accept 1
   177  	c.Assert(out, checker.Contains, "testnotinuse1\n", check.Commentf("expected volume 'testnotinuse1' in output"))
   178  	c.Assert(out, check.Not(checker.Contains), "testisinuse1\n", check.Commentf("volume 'testisinuse1' in output, but not expected"))
   179  	c.Assert(out, check.Not(checker.Contains), "testisinuse2\n", check.Commentf("volume 'testisinuse2' in output, but not expected"))
   180  
   181  	out, _ = dockerCmd(c, "volume", "ls", "--filter", "dangling=0")
   182  	// dangling=0 is same as dangling=false case
   183  	c.Assert(out, check.Not(checker.Contains), "testnotinuse1\n", check.Commentf("expected volume 'testnotinuse1' in output"))
   184  	c.Assert(out, checker.Contains, "testisinuse1\n", check.Commentf("expected volume 'testisinuse1' in output"))
   185  	c.Assert(out, checker.Contains, "testisinuse2\n", check.Commentf("expected volume 'testisinuse2' in output"))
   186  
   187  	out, _ = dockerCmd(c, "volume", "ls", "--filter", "name=testisin")
   188  	c.Assert(out, check.Not(checker.Contains), "testnotinuse1\n", check.Commentf("expected volume 'testnotinuse1' in output"))
   189  	c.Assert(out, checker.Contains, "testisinuse1\n", check.Commentf("expected volume 'testisinuse1' in output"))
   190  	c.Assert(out, checker.Contains, "testisinuse2\n", check.Commentf("expected volume 'testisinuse2' in output"))
   191  }
   192  
   193  func (s *DockerSuite) TestVolumeCLILsErrorWithInvalidFilterName(c *check.C) {
   194  	out, _, err := dockerCmdWithError("volume", "ls", "-f", "FOO=123")
   195  	c.Assert(err, checker.NotNil)
   196  	c.Assert(out, checker.Contains, "Invalid filter")
   197  }
   198  
   199  func (s *DockerSuite) TestVolumeCLILsWithIncorrectFilterValue(c *check.C) {
   200  	out, _, err := dockerCmdWithError("volume", "ls", "-f", "dangling=invalid")
   201  	c.Assert(err, check.NotNil)
   202  	c.Assert(out, checker.Contains, "Invalid filter")
   203  }
   204  
   205  func (s *DockerSuite) TestVolumeCLIRm(c *check.C) {
   206  	prefix, _ := getPrefixAndSlashFromDaemonPlatform()
   207  	out, _ := dockerCmd(c, "volume", "create")
   208  	id := strings.TrimSpace(out)
   209  
   210  	dockerCmd(c, "volume", "create", "test")
   211  	dockerCmd(c, "volume", "rm", id)
   212  	dockerCmd(c, "volume", "rm", "test")
   213  
   214  	volumeID := "testing"
   215  	dockerCmd(c, "run", "-v", volumeID+":"+prefix+"/foo", "--name=test", "busybox", "sh", "-c", "echo hello > /foo/bar")
   216  
   217  	icmd.RunCommand(dockerBinary, "volume", "rm", "testing").Assert(c, icmd.Expected{
   218  		ExitCode: 1,
   219  		Error:    "exit status 1",
   220  	})
   221  
   222  	out, _ = dockerCmd(c, "run", "--volumes-from=test", "--name=test2", "busybox", "sh", "-c", "cat /foo/bar")
   223  	c.Assert(strings.TrimSpace(out), check.Equals, "hello")
   224  	dockerCmd(c, "rm", "-fv", "test2")
   225  	dockerCmd(c, "volume", "inspect", volumeID)
   226  	dockerCmd(c, "rm", "-f", "test")
   227  
   228  	out, _ = dockerCmd(c, "run", "--name=test2", "-v", volumeID+":"+prefix+"/foo", "busybox", "sh", "-c", "cat /foo/bar")
   229  	c.Assert(strings.TrimSpace(out), check.Equals, "hello", check.Commentf("volume data was removed"))
   230  	dockerCmd(c, "rm", "test2")
   231  
   232  	dockerCmd(c, "volume", "rm", volumeID)
   233  	c.Assert(
   234  		exec.Command("volume", "rm", "doesnotexist").Run(),
   235  		check.Not(check.IsNil),
   236  		check.Commentf("volume rm should fail with non-existent volume"),
   237  	)
   238  }
   239  
   240  // FIXME(vdemeester) should be a unit test in cli/command/volume package
   241  func (s *DockerSuite) TestVolumeCLINoArgs(c *check.C) {
   242  	out, _ := dockerCmd(c, "volume")
   243  	// no args should produce the cmd usage output
   244  	usage := "Usage:	docker volume COMMAND"
   245  	c.Assert(out, checker.Contains, usage)
   246  
   247  	// invalid arg should error and show the command usage on stderr
   248  	icmd.RunCommand(dockerBinary, "volume", "somearg").Assert(c, icmd.Expected{
   249  		ExitCode: 1,
   250  		Error:    "exit status 1",
   251  		Err:      usage,
   252  	})
   253  
   254  	// invalid flag should error and show the flag error and cmd usage
   255  	result := icmd.RunCommand(dockerBinary, "volume", "--no-such-flag")
   256  	result.Assert(c, icmd.Expected{
   257  		ExitCode: 125,
   258  		Error:    "exit status 125",
   259  		Err:      usage,
   260  	})
   261  	c.Assert(result.Stderr(), checker.Contains, "unknown flag: --no-such-flag")
   262  }
   263  
   264  func (s *DockerSuite) TestVolumeCLIInspectTmplError(c *check.C) {
   265  	out, _ := dockerCmd(c, "volume", "create")
   266  	name := strings.TrimSpace(out)
   267  
   268  	out, exitCode, err := dockerCmdWithError("volume", "inspect", "--format='{{ .FooBar }}'", name)
   269  	c.Assert(err, checker.NotNil, check.Commentf("Output: %s", out))
   270  	c.Assert(exitCode, checker.Equals, 1, check.Commentf("Output: %s", out))
   271  	c.Assert(out, checker.Contains, "Template parsing error")
   272  }
   273  
   274  func (s *DockerSuite) TestVolumeCLICreateWithOpts(c *check.C) {
   275  	testRequires(c, DaemonIsLinux)
   276  
   277  	dockerCmd(c, "volume", "create", "-d", "local", "test", "--opt=type=tmpfs", "--opt=device=tmpfs", "--opt=o=size=1m,uid=1000")
   278  	out, _ := dockerCmd(c, "run", "-v", "test:/foo", "busybox", "mount")
   279  
   280  	mounts := strings.Split(out, "\n")
   281  	var found bool
   282  	for _, m := range mounts {
   283  		if strings.Contains(m, "/foo") {
   284  			found = true
   285  			info := strings.Fields(m)
   286  			// tmpfs on <path> type tmpfs (rw,relatime,size=1024k,uid=1000)
   287  			c.Assert(info[0], checker.Equals, "tmpfs")
   288  			c.Assert(info[2], checker.Equals, "/foo")
   289  			c.Assert(info[4], checker.Equals, "tmpfs")
   290  			c.Assert(info[5], checker.Contains, "uid=1000")
   291  			c.Assert(info[5], checker.Contains, "size=1024k")
   292  			break
   293  		}
   294  	}
   295  	c.Assert(found, checker.Equals, true)
   296  }
   297  
   298  func (s *DockerSuite) TestVolumeCLICreateLabel(c *check.C) {
   299  	testVol := "testvolcreatelabel"
   300  	testLabel := "foo"
   301  	testValue := "bar"
   302  
   303  	out, _, err := dockerCmdWithError("volume", "create", "--label", testLabel+"="+testValue, testVol)
   304  	c.Assert(err, check.IsNil)
   305  
   306  	out, _ = dockerCmd(c, "volume", "inspect", "--format={{ .Labels."+testLabel+" }}", testVol)
   307  	c.Assert(strings.TrimSpace(out), check.Equals, testValue)
   308  }
   309  
   310  func (s *DockerSuite) TestVolumeCLICreateLabelMultiple(c *check.C) {
   311  	testVol := "testvolcreatelabel"
   312  
   313  	testLabels := map[string]string{
   314  		"foo": "bar",
   315  		"baz": "foo",
   316  	}
   317  
   318  	args := []string{
   319  		"volume",
   320  		"create",
   321  		testVol,
   322  	}
   323  
   324  	for k, v := range testLabels {
   325  		args = append(args, "--label", k+"="+v)
   326  	}
   327  
   328  	out, _, err := dockerCmdWithError(args...)
   329  	c.Assert(err, check.IsNil)
   330  
   331  	for k, v := range testLabels {
   332  		out, _ = dockerCmd(c, "volume", "inspect", "--format={{ .Labels."+k+" }}", testVol)
   333  		c.Assert(strings.TrimSpace(out), check.Equals, v)
   334  	}
   335  }
   336  
   337  func (s *DockerSuite) TestVolumeCLILsFilterLabels(c *check.C) {
   338  	testVol1 := "testvolcreatelabel-1"
   339  	out, _, err := dockerCmdWithError("volume", "create", "--label", "foo=bar1", testVol1)
   340  	c.Assert(err, check.IsNil)
   341  
   342  	testVol2 := "testvolcreatelabel-2"
   343  	out, _, err = dockerCmdWithError("volume", "create", "--label", "foo=bar2", testVol2)
   344  	c.Assert(err, check.IsNil)
   345  
   346  	out, _ = dockerCmd(c, "volume", "ls", "--filter", "label=foo")
   347  
   348  	// filter with label=key
   349  	c.Assert(out, checker.Contains, "testvolcreatelabel-1\n", check.Commentf("expected volume 'testvolcreatelabel-1' in output"))
   350  	c.Assert(out, checker.Contains, "testvolcreatelabel-2\n", check.Commentf("expected volume 'testvolcreatelabel-2' in output"))
   351  
   352  	out, _ = dockerCmd(c, "volume", "ls", "--filter", "label=foo=bar1")
   353  
   354  	// filter with label=key=value
   355  	c.Assert(out, checker.Contains, "testvolcreatelabel-1\n", check.Commentf("expected volume 'testvolcreatelabel-1' in output"))
   356  	c.Assert(out, check.Not(checker.Contains), "testvolcreatelabel-2\n", check.Commentf("expected volume 'testvolcreatelabel-2 in output"))
   357  
   358  	out, _ = dockerCmd(c, "volume", "ls", "--filter", "label=non-exist")
   359  	outArr := strings.Split(strings.TrimSpace(out), "\n")
   360  	c.Assert(len(outArr), check.Equals, 1, check.Commentf("\n%s", out))
   361  
   362  	out, _ = dockerCmd(c, "volume", "ls", "--filter", "label=foo=non-exist")
   363  	outArr = strings.Split(strings.TrimSpace(out), "\n")
   364  	c.Assert(len(outArr), check.Equals, 1, check.Commentf("\n%s", out))
   365  }
   366  
   367  func (s *DockerSuite) TestVolumeCLILsFilterDrivers(c *check.C) {
   368  	// using default volume driver local to create volumes
   369  	testVol1 := "testvol-1"
   370  	out, _, err := dockerCmdWithError("volume", "create", testVol1)
   371  	c.Assert(err, check.IsNil)
   372  
   373  	testVol2 := "testvol-2"
   374  	out, _, err = dockerCmdWithError("volume", "create", testVol2)
   375  	c.Assert(err, check.IsNil)
   376  
   377  	// filter with driver=local
   378  	out, _ = dockerCmd(c, "volume", "ls", "--filter", "driver=local")
   379  	c.Assert(out, checker.Contains, "testvol-1\n", check.Commentf("expected volume 'testvol-1' in output"))
   380  	c.Assert(out, checker.Contains, "testvol-2\n", check.Commentf("expected volume 'testvol-2' in output"))
   381  
   382  	// filter with driver=invaliddriver
   383  	out, _ = dockerCmd(c, "volume", "ls", "--filter", "driver=invaliddriver")
   384  	outArr := strings.Split(strings.TrimSpace(out), "\n")
   385  	c.Assert(len(outArr), check.Equals, 1, check.Commentf("\n%s", out))
   386  
   387  	// filter with driver=loca
   388  	out, _ = dockerCmd(c, "volume", "ls", "--filter", "driver=loca")
   389  	outArr = strings.Split(strings.TrimSpace(out), "\n")
   390  	c.Assert(len(outArr), check.Equals, 1, check.Commentf("\n%s", out))
   391  
   392  	// filter with driver=
   393  	out, _ = dockerCmd(c, "volume", "ls", "--filter", "driver=")
   394  	outArr = strings.Split(strings.TrimSpace(out), "\n")
   395  	c.Assert(len(outArr), check.Equals, 1, check.Commentf("\n%s", out))
   396  }
   397  
   398  func (s *DockerSuite) TestVolumeCLIRmForceUsage(c *check.C) {
   399  	out, _ := dockerCmd(c, "volume", "create")
   400  	id := strings.TrimSpace(out)
   401  
   402  	dockerCmd(c, "volume", "rm", "-f", id)
   403  	dockerCmd(c, "volume", "rm", "--force", "nonexist")
   404  }
   405  
   406  func (s *DockerSuite) TestVolumeCLIRmForce(c *check.C) {
   407  	testRequires(c, SameHostDaemon, DaemonIsLinux)
   408  
   409  	name := "test"
   410  	out, _ := dockerCmd(c, "volume", "create", name)
   411  	id := strings.TrimSpace(out)
   412  	c.Assert(id, checker.Equals, name)
   413  
   414  	out, _ = dockerCmd(c, "volume", "inspect", "--format", "{{.Mountpoint}}", name)
   415  	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
   416  	// Mountpoint is in the form of "/var/lib/docker/volumes/.../_data", removing `/_data`
   417  	path := strings.TrimSuffix(strings.TrimSpace(out), "/_data")
   418  	icmd.RunCommand("rm", "-rf", path).Assert(c, icmd.Success)
   419  
   420  	dockerCmd(c, "volume", "rm", "-f", name)
   421  	out, _ = dockerCmd(c, "volume", "ls")
   422  	c.Assert(out, checker.Not(checker.Contains), name)
   423  	dockerCmd(c, "volume", "create", name)
   424  	out, _ = dockerCmd(c, "volume", "ls")
   425  	c.Assert(out, checker.Contains, name)
   426  }
   427  
   428  // TestVolumeCLIRmForceInUse verifies that repeated `docker volume rm -f` calls does not remove a volume
   429  // if it is in use. Test case for https://github.com/docker/docker/issues/31446
   430  func (s *DockerSuite) TestVolumeCLIRmForceInUse(c *check.C) {
   431  	name := "testvolume"
   432  	out, _ := dockerCmd(c, "volume", "create", name)
   433  	id := strings.TrimSpace(out)
   434  	c.Assert(id, checker.Equals, name)
   435  
   436  	prefix, slash := getPrefixAndSlashFromDaemonPlatform()
   437  	out, e := dockerCmd(c, "create", "-v", "testvolume:"+prefix+slash+"foo", "busybox")
   438  	cid := strings.TrimSpace(out)
   439  
   440  	_, _, err := dockerCmdWithError("volume", "rm", "-f", name)
   441  	c.Assert(err, check.NotNil)
   442  	c.Assert(err.Error(), checker.Contains, "volume is in use")
   443  	out, _ = dockerCmd(c, "volume", "ls")
   444  	c.Assert(out, checker.Contains, name)
   445  
   446  	// The original issue did not _remove_ the volume from the list
   447  	// the first time. But a second call to `volume rm` removed it.
   448  	// Calling `volume rm` a second time to confirm it's not removed
   449  	// when calling twice.
   450  	_, _, err = dockerCmdWithError("volume", "rm", "-f", name)
   451  	c.Assert(err, check.NotNil)
   452  	c.Assert(err.Error(), checker.Contains, "volume is in use")
   453  	out, _ = dockerCmd(c, "volume", "ls")
   454  	c.Assert(out, checker.Contains, name)
   455  
   456  	// Verify removing the volume after the container is removed works
   457  	_, e = dockerCmd(c, "rm", cid)
   458  	c.Assert(e, check.Equals, 0)
   459  
   460  	_, e = dockerCmd(c, "volume", "rm", "-f", name)
   461  	c.Assert(e, check.Equals, 0)
   462  
   463  	out, e = dockerCmd(c, "volume", "ls")
   464  	c.Assert(e, check.Equals, 0)
   465  	c.Assert(out, checker.Not(checker.Contains), name)
   466  }
   467  
   468  func (s *DockerSuite) TestVolumeCliInspectWithVolumeOpts(c *check.C) {
   469  	testRequires(c, DaemonIsLinux)
   470  
   471  	// Without options
   472  	name := "test1"
   473  	dockerCmd(c, "volume", "create", "-d", "local", name)
   474  	out, _ := dockerCmd(c, "volume", "inspect", "--format={{ .Options }}", name)
   475  	c.Assert(strings.TrimSpace(out), checker.Contains, "map[]")
   476  
   477  	// With options
   478  	name = "test2"
   479  	k1, v1 := "type", "tmpfs"
   480  	k2, v2 := "device", "tmpfs"
   481  	k3, v3 := "o", "size=1m,uid=1000"
   482  	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))
   483  	out, _ = dockerCmd(c, "volume", "inspect", "--format={{ .Options }}", name)
   484  	c.Assert(strings.TrimSpace(out), checker.Contains, fmt.Sprintf("%s:%s", k1, v1))
   485  	c.Assert(strings.TrimSpace(out), checker.Contains, fmt.Sprintf("%s:%s", k2, v2))
   486  	c.Assert(strings.TrimSpace(out), checker.Contains, fmt.Sprintf("%s:%s", k3, v3))
   487  }
   488  
   489  // Test case (1) for 21845: duplicate targets for --volumes-from
   490  func (s *DockerSuite) TestDuplicateMountpointsForVolumesFrom(c *check.C) {
   491  	testRequires(c, DaemonIsLinux)
   492  
   493  	image := "vimage"
   494  	buildImageSuccessfully(c, image, build.WithDockerfile(`
   495  		FROM busybox
   496  		VOLUME ["/tmp/data"]`))
   497  
   498  	dockerCmd(c, "run", "--name=data1", image, "true")
   499  	dockerCmd(c, "run", "--name=data2", image, "true")
   500  
   501  	out, _ := dockerCmd(c, "inspect", "--format", "{{(index .Mounts 0).Name}}", "data1")
   502  	data1 := strings.TrimSpace(out)
   503  	c.Assert(data1, checker.Not(checker.Equals), "")
   504  
   505  	out, _ = dockerCmd(c, "inspect", "--format", "{{(index .Mounts 0).Name}}", "data2")
   506  	data2 := strings.TrimSpace(out)
   507  	c.Assert(data2, checker.Not(checker.Equals), "")
   508  
   509  	// Both volume should exist
   510  	out, _ = dockerCmd(c, "volume", "ls", "-q")
   511  	c.Assert(strings.TrimSpace(out), checker.Contains, data1)
   512  	c.Assert(strings.TrimSpace(out), checker.Contains, data2)
   513  
   514  	out, _, err := dockerCmdWithError("run", "--name=app", "--volumes-from=data1", "--volumes-from=data2", "-d", "busybox", "top")
   515  	c.Assert(err, checker.IsNil, check.Commentf("Out: %s", out))
   516  
   517  	// Only the second volume will be referenced, this is backward compatible
   518  	out, _ = dockerCmd(c, "inspect", "--format", "{{(index .Mounts 0).Name}}", "app")
   519  	c.Assert(strings.TrimSpace(out), checker.Equals, data2)
   520  
   521  	dockerCmd(c, "rm", "-f", "-v", "app")
   522  	dockerCmd(c, "rm", "-f", "-v", "data1")
   523  	dockerCmd(c, "rm", "-f", "-v", "data2")
   524  
   525  	// Both volume should not exist
   526  	out, _ = dockerCmd(c, "volume", "ls", "-q")
   527  	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), data1)
   528  	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), data2)
   529  }
   530  
   531  // Test case (2) for 21845: duplicate targets for --volumes-from and -v (bind)
   532  func (s *DockerSuite) TestDuplicateMountpointsForVolumesFromAndBind(c *check.C) {
   533  	testRequires(c, DaemonIsLinux)
   534  
   535  	image := "vimage"
   536  	buildImageSuccessfully(c, image, build.WithDockerfile(`
   537                  FROM busybox
   538                  VOLUME ["/tmp/data"]`))
   539  
   540  	dockerCmd(c, "run", "--name=data1", image, "true")
   541  	dockerCmd(c, "run", "--name=data2", image, "true")
   542  
   543  	out, _ := dockerCmd(c, "inspect", "--format", "{{(index .Mounts 0).Name}}", "data1")
   544  	data1 := strings.TrimSpace(out)
   545  	c.Assert(data1, checker.Not(checker.Equals), "")
   546  
   547  	out, _ = dockerCmd(c, "inspect", "--format", "{{(index .Mounts 0).Name}}", "data2")
   548  	data2 := strings.TrimSpace(out)
   549  	c.Assert(data2, checker.Not(checker.Equals), "")
   550  
   551  	// Both volume should exist
   552  	out, _ = dockerCmd(c, "volume", "ls", "-q")
   553  	c.Assert(strings.TrimSpace(out), checker.Contains, data1)
   554  	c.Assert(strings.TrimSpace(out), checker.Contains, data2)
   555  
   556  	// /tmp/data is automatically created, because we are not using the modern mount API here
   557  	out, _, err := dockerCmdWithError("run", "--name=app", "--volumes-from=data1", "--volumes-from=data2", "-v", "/tmp/data:/tmp/data", "-d", "busybox", "top")
   558  	c.Assert(err, checker.IsNil, check.Commentf("Out: %s", out))
   559  
   560  	// No volume will be referenced (mount is /tmp/data), this is backward compatible
   561  	out, _ = dockerCmd(c, "inspect", "--format", "{{(index .Mounts 0).Name}}", "app")
   562  	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), data1)
   563  	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), data2)
   564  
   565  	dockerCmd(c, "rm", "-f", "-v", "app")
   566  	dockerCmd(c, "rm", "-f", "-v", "data1")
   567  	dockerCmd(c, "rm", "-f", "-v", "data2")
   568  
   569  	// Both volume should not exist
   570  	out, _ = dockerCmd(c, "volume", "ls", "-q")
   571  	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), data1)
   572  	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), data2)
   573  }
   574  
   575  // Test case (3) for 21845: duplicate targets for --volumes-from and `Mounts` (API only)
   576  func (s *DockerSuite) TestDuplicateMountpointsForVolumesFromAndMounts(c *check.C) {
   577  	testRequires(c, SameHostDaemon, DaemonIsLinux)
   578  
   579  	image := "vimage"
   580  	buildImageSuccessfully(c, image, build.WithDockerfile(`
   581                  FROM busybox
   582                  VOLUME ["/tmp/data"]`))
   583  
   584  	dockerCmd(c, "run", "--name=data1", image, "true")
   585  	dockerCmd(c, "run", "--name=data2", image, "true")
   586  
   587  	out, _ := dockerCmd(c, "inspect", "--format", "{{(index .Mounts 0).Name}}", "data1")
   588  	data1 := strings.TrimSpace(out)
   589  	c.Assert(data1, checker.Not(checker.Equals), "")
   590  
   591  	out, _ = dockerCmd(c, "inspect", "--format", "{{(index .Mounts 0).Name}}", "data2")
   592  	data2 := strings.TrimSpace(out)
   593  	c.Assert(data2, checker.Not(checker.Equals), "")
   594  
   595  	// Both volume should exist
   596  	out, _ = dockerCmd(c, "volume", "ls", "-q")
   597  	c.Assert(strings.TrimSpace(out), checker.Contains, data1)
   598  	c.Assert(strings.TrimSpace(out), checker.Contains, data2)
   599  
   600  	err := os.MkdirAll("/tmp/data", 0755)
   601  	c.Assert(err, checker.IsNil)
   602  	// Mounts is available in API
   603  	cli, err := client.NewEnvClient()
   604  	c.Assert(err, checker.IsNil)
   605  	defer cli.Close()
   606  
   607  	config := container.Config{
   608  		Cmd:   []string{"top"},
   609  		Image: "busybox",
   610  	}
   611  
   612  	hostConfig := container.HostConfig{
   613  		VolumesFrom: []string{"data1", "data2"},
   614  		Mounts: []mount.Mount{
   615  			{
   616  				Type:   "bind",
   617  				Source: "/tmp/data",
   618  				Target: "/tmp/data",
   619  			},
   620  		},
   621  	}
   622  	_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &network.NetworkingConfig{}, "app")
   623  
   624  	c.Assert(err, checker.IsNil)
   625  
   626  	// No volume will be referenced (mount is /tmp/data), this is backward compatible
   627  	out, _ = dockerCmd(c, "inspect", "--format", "{{(index .Mounts 0).Name}}", "app")
   628  	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), data1)
   629  	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), data2)
   630  
   631  	dockerCmd(c, "rm", "-f", "-v", "app")
   632  	dockerCmd(c, "rm", "-f", "-v", "data1")
   633  	dockerCmd(c, "rm", "-f", "-v", "data2")
   634  
   635  	// Both volume should not exist
   636  	out, _ = dockerCmd(c, "volume", "ls", "-q")
   637  	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), data1)
   638  	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), data2)
   639  }