github.com/rawahars/moby@v24.0.4+incompatible/integration-cli/docker_cli_volume_test.go (about)

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