docker_cli_volume_test.go 25 KB

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