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 }