docker_cli_volume_test.go 24 KB

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