docker_cli_volume_test.go 25 KB

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