github.com/zhouyu0/docker-note@v0.0.0-20190722021225-b8d3825084db/integration-cli/docker_cli_volume_test.go (about)

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