docker_cli_volume_test.go 25 KB

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