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