docker_cli_volume_test.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424
  1. package main
  2. import (
  3. "io/ioutil"
  4. "os"
  5. "os/exec"
  6. "path/filepath"
  7. "strings"
  8. "github.com/docker/docker/pkg/integration/checker"
  9. icmd "github.com/docker/docker/pkg/integration/cmd"
  10. "github.com/go-check/check"
  11. )
  12. func (s *DockerSuite) TestVolumeCliCreate(c *check.C) {
  13. dockerCmd(c, "volume", "create")
  14. _, err := runCommand(exec.Command(dockerBinary, "volume", "create", "-d", "nosuchdriver"))
  15. c.Assert(err, check.Not(check.IsNil))
  16. // test using hidden --name option
  17. out, _ := dockerCmd(c, "volume", "create", "--name=test")
  18. name := strings.TrimSpace(out)
  19. c.Assert(name, check.Equals, "test")
  20. out, _ = dockerCmd(c, "volume", "create", "test2")
  21. name = strings.TrimSpace(out)
  22. c.Assert(name, check.Equals, "test2")
  23. }
  24. func (s *DockerSuite) TestVolumeCliCreateOptionConflict(c *check.C) {
  25. dockerCmd(c, "volume", "create", "test")
  26. out, _, err := dockerCmdWithError("volume", "create", "test", "--driver", "nosuchdriver")
  27. c.Assert(err, check.NotNil, check.Commentf("volume create exception name already in use with another driver"))
  28. c.Assert(out, checker.Contains, "A volume named test already exists")
  29. out, _ = dockerCmd(c, "volume", "inspect", "--format={{ .Driver }}", "test")
  30. _, _, err = dockerCmdWithError("volume", "create", "test", "--driver", strings.TrimSpace(out))
  31. c.Assert(err, check.IsNil)
  32. // make sure hidden --name option conflicts with positional arg name
  33. out, _, err = dockerCmdWithError("volume", "create", "--name", "test2", "test2")
  34. c.Assert(err, check.NotNil, check.Commentf("Conflicting options: either specify --name or provide positional arg, not both"))
  35. }
  36. func (s *DockerSuite) TestVolumeCliInspect(c *check.C) {
  37. c.Assert(
  38. exec.Command(dockerBinary, "volume", "inspect", "doesntexist").Run(),
  39. check.Not(check.IsNil),
  40. check.Commentf("volume inspect should error on non-existent volume"),
  41. )
  42. out, _ := dockerCmd(c, "volume", "create")
  43. name := strings.TrimSpace(out)
  44. out, _ = dockerCmd(c, "volume", "inspect", "--format={{ .Name }}", name)
  45. c.Assert(strings.TrimSpace(out), check.Equals, name)
  46. dockerCmd(c, "volume", "create", "test")
  47. out, _ = dockerCmd(c, "volume", "inspect", "--format={{ .Name }}", "test")
  48. c.Assert(strings.TrimSpace(out), check.Equals, "test")
  49. }
  50. func (s *DockerSuite) TestVolumeCliInspectMulti(c *check.C) {
  51. dockerCmd(c, "volume", "create", "test1")
  52. dockerCmd(c, "volume", "create", "test2")
  53. dockerCmd(c, "volume", "create", "not-shown")
  54. result := dockerCmdWithResult("volume", "inspect", "--format={{ .Name }}", "test1", "test2", "doesntexist", "not-shown")
  55. c.Assert(result, icmd.Matches, icmd.Expected{
  56. ExitCode: 1,
  57. Err: "No such volume: doesntexist",
  58. })
  59. out := result.Stdout()
  60. outArr := strings.Split(strings.TrimSpace(out), "\n")
  61. c.Assert(len(outArr), check.Equals, 2, check.Commentf("\n%s", out))
  62. c.Assert(out, checker.Contains, "test1")
  63. c.Assert(out, checker.Contains, "test2")
  64. c.Assert(out, checker.Not(checker.Contains), "not-shown")
  65. }
  66. func (s *DockerSuite) TestVolumeCliLs(c *check.C) {
  67. prefix, _ := getPrefixAndSlashFromDaemonPlatform()
  68. dockerCmd(c, "volume", "create", "aaa")
  69. dockerCmd(c, "volume", "create", "test")
  70. dockerCmd(c, "volume", "create", "soo")
  71. dockerCmd(c, "run", "-v", "soo:"+prefix+"/foo", "busybox", "ls", "/")
  72. out, _ := dockerCmd(c, "volume", "ls")
  73. outArr := strings.Split(strings.TrimSpace(out), "\n")
  74. c.Assert(len(outArr), check.Equals, 4, check.Commentf("\n%s", out))
  75. assertVolList(c, out, []string{"aaa", "soo", "test"})
  76. }
  77. func (s *DockerSuite) TestVolumeLsFormat(c *check.C) {
  78. dockerCmd(c, "volume", "create", "aaa")
  79. dockerCmd(c, "volume", "create", "test")
  80. dockerCmd(c, "volume", "create", "soo")
  81. out, _ := dockerCmd(c, "volume", "ls", "--format", "{{.Name}}")
  82. lines := strings.Split(strings.TrimSpace(string(out)), "\n")
  83. expected := []string{"aaa", "soo", "test"}
  84. var names []string
  85. for _, l := range lines {
  86. names = append(names, l)
  87. }
  88. c.Assert(expected, checker.DeepEquals, names, check.Commentf("Expected array with truncated names: %v, got: %v", expected, names))
  89. }
  90. func (s *DockerSuite) TestVolumeLsFormatDefaultFormat(c *check.C) {
  91. dockerCmd(c, "volume", "create", "aaa")
  92. dockerCmd(c, "volume", "create", "test")
  93. dockerCmd(c, "volume", "create", "soo")
  94. config := `{
  95. "volumesFormat": "{{ .Name }} default"
  96. }`
  97. d, err := ioutil.TempDir("", "integration-cli-")
  98. c.Assert(err, checker.IsNil)
  99. defer os.RemoveAll(d)
  100. err = ioutil.WriteFile(filepath.Join(d, "config.json"), []byte(config), 0644)
  101. c.Assert(err, checker.IsNil)
  102. out, _ := dockerCmd(c, "--config", d, "volume", "ls")
  103. lines := strings.Split(strings.TrimSpace(string(out)), "\n")
  104. expected := []string{"aaa default", "soo default", "test default"}
  105. var names []string
  106. for _, l := range lines {
  107. names = append(names, l)
  108. }
  109. c.Assert(expected, checker.DeepEquals, names, check.Commentf("Expected array with truncated names: %v, got: %v", expected, names))
  110. }
  111. // assertVolList checks volume retrieved with ls command
  112. // equals to expected volume list
  113. // note: out should be `volume ls [option]` result
  114. func assertVolList(c *check.C, out string, expectVols []string) {
  115. lines := strings.Split(out, "\n")
  116. var volList []string
  117. for _, line := range lines[1 : len(lines)-1] {
  118. volFields := strings.Fields(line)
  119. // wrap all volume name in volList
  120. volList = append(volList, volFields[1])
  121. }
  122. // volume ls should contains all expected volumes
  123. c.Assert(volList, checker.DeepEquals, expectVols)
  124. }
  125. func (s *DockerSuite) TestVolumeCliLsFilterDangling(c *check.C) {
  126. prefix, _ := getPrefixAndSlashFromDaemonPlatform()
  127. dockerCmd(c, "volume", "create", "testnotinuse1")
  128. dockerCmd(c, "volume", "create", "testisinuse1")
  129. dockerCmd(c, "volume", "create", "testisinuse2")
  130. // Make sure both "created" (but not started), and started
  131. // containers are included in reference counting
  132. dockerCmd(c, "run", "--name", "volume-test1", "-v", "testisinuse1:"+prefix+"/foo", "busybox", "true")
  133. dockerCmd(c, "create", "--name", "volume-test2", "-v", "testisinuse2:"+prefix+"/foo", "busybox", "true")
  134. out, _ := dockerCmd(c, "volume", "ls")
  135. // No filter, all volumes should show
  136. c.Assert(out, checker.Contains, "testnotinuse1\n", check.Commentf("expected volume 'testnotinuse1' in output"))
  137. c.Assert(out, checker.Contains, "testisinuse1\n", check.Commentf("expected volume 'testisinuse1' in output"))
  138. c.Assert(out, checker.Contains, "testisinuse2\n", check.Commentf("expected volume 'testisinuse2' in output"))
  139. out, _ = dockerCmd(c, "volume", "ls", "--filter", "dangling=false")
  140. // Explicitly disabling dangling
  141. c.Assert(out, check.Not(checker.Contains), "testnotinuse1\n", check.Commentf("expected volume 'testnotinuse1' in output"))
  142. c.Assert(out, checker.Contains, "testisinuse1\n", check.Commentf("expected volume 'testisinuse1' in output"))
  143. c.Assert(out, checker.Contains, "testisinuse2\n", check.Commentf("expected volume 'testisinuse2' in output"))
  144. out, _ = dockerCmd(c, "volume", "ls", "--filter", "dangling=true")
  145. // Filter "dangling" volumes; only "dangling" (unused) volumes should be in the output
  146. c.Assert(out, checker.Contains, "testnotinuse1\n", check.Commentf("expected volume 'testnotinuse1' in output"))
  147. c.Assert(out, check.Not(checker.Contains), "testisinuse1\n", check.Commentf("volume 'testisinuse1' in output, but not expected"))
  148. c.Assert(out, check.Not(checker.Contains), "testisinuse2\n", check.Commentf("volume 'testisinuse2' in output, but not expected"))
  149. out, _ = dockerCmd(c, "volume", "ls", "--filter", "dangling=1")
  150. // Filter "dangling" volumes; only "dangling" (unused) volumes should be in the output, dangling also accept 1
  151. c.Assert(out, checker.Contains, "testnotinuse1\n", check.Commentf("expected volume 'testnotinuse1' in output"))
  152. c.Assert(out, check.Not(checker.Contains), "testisinuse1\n", check.Commentf("volume 'testisinuse1' in output, but not expected"))
  153. c.Assert(out, check.Not(checker.Contains), "testisinuse2\n", check.Commentf("volume 'testisinuse2' in output, but not expected"))
  154. out, _ = dockerCmd(c, "volume", "ls", "--filter", "dangling=0")
  155. // dangling=0 is same as dangling=false case
  156. c.Assert(out, check.Not(checker.Contains), "testnotinuse1\n", check.Commentf("expected volume 'testnotinuse1' in output"))
  157. c.Assert(out, checker.Contains, "testisinuse1\n", check.Commentf("expected volume 'testisinuse1' in output"))
  158. c.Assert(out, checker.Contains, "testisinuse2\n", check.Commentf("expected volume 'testisinuse2' in output"))
  159. out, _ = dockerCmd(c, "volume", "ls", "--filter", "name=testisin")
  160. c.Assert(out, check.Not(checker.Contains), "testnotinuse1\n", check.Commentf("expected volume 'testnotinuse1' in output"))
  161. c.Assert(out, checker.Contains, "testisinuse1\n", check.Commentf("execpeted volume 'testisinuse1' in output"))
  162. c.Assert(out, checker.Contains, "testisinuse2\n", check.Commentf("expected volume 'testisinuse2' in output"))
  163. out, _ = dockerCmd(c, "volume", "ls", "--filter", "driver=invalidDriver")
  164. outArr := strings.Split(strings.TrimSpace(out), "\n")
  165. c.Assert(len(outArr), check.Equals, 1, check.Commentf("%s\n", out))
  166. out, _ = dockerCmd(c, "volume", "ls", "--filter", "driver=local")
  167. outArr = strings.Split(strings.TrimSpace(out), "\n")
  168. c.Assert(len(outArr), check.Equals, 4, check.Commentf("\n%s", out))
  169. out, _ = dockerCmd(c, "volume", "ls", "--filter", "driver=loc")
  170. outArr = strings.Split(strings.TrimSpace(out), "\n")
  171. c.Assert(len(outArr), check.Equals, 4, check.Commentf("\n%s", out))
  172. }
  173. func (s *DockerSuite) TestVolumeCliLsErrorWithInvalidFilterName(c *check.C) {
  174. out, _, err := dockerCmdWithError("volume", "ls", "-f", "FOO=123")
  175. c.Assert(err, checker.NotNil)
  176. c.Assert(out, checker.Contains, "Invalid filter")
  177. }
  178. func (s *DockerSuite) TestVolumeCliLsWithIncorrectFilterValue(c *check.C) {
  179. out, _, err := dockerCmdWithError("volume", "ls", "-f", "dangling=invalid")
  180. c.Assert(err, check.NotNil)
  181. c.Assert(out, checker.Contains, "Invalid filter")
  182. }
  183. func (s *DockerSuite) TestVolumeCliRm(c *check.C) {
  184. prefix, _ := getPrefixAndSlashFromDaemonPlatform()
  185. out, _ := dockerCmd(c, "volume", "create")
  186. id := strings.TrimSpace(out)
  187. dockerCmd(c, "volume", "create", "test")
  188. dockerCmd(c, "volume", "rm", id)
  189. dockerCmd(c, "volume", "rm", "test")
  190. out, _ = dockerCmd(c, "volume", "ls")
  191. outArr := strings.Split(strings.TrimSpace(out), "\n")
  192. c.Assert(len(outArr), check.Equals, 1, check.Commentf("%s\n", out))
  193. volumeID := "testing"
  194. dockerCmd(c, "run", "-v", volumeID+":"+prefix+"/foo", "--name=test", "busybox", "sh", "-c", "echo hello > /foo/bar")
  195. out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "volume", "rm", "testing"))
  196. c.Assert(
  197. err,
  198. check.Not(check.IsNil),
  199. check.Commentf("Should not be able to remove volume that is in use by a container\n%s", out))
  200. out, _ = dockerCmd(c, "run", "--volumes-from=test", "--name=test2", "busybox", "sh", "-c", "cat /foo/bar")
  201. c.Assert(strings.TrimSpace(out), check.Equals, "hello")
  202. dockerCmd(c, "rm", "-fv", "test2")
  203. dockerCmd(c, "volume", "inspect", volumeID)
  204. dockerCmd(c, "rm", "-f", "test")
  205. out, _ = dockerCmd(c, "run", "--name=test2", "-v", volumeID+":"+prefix+"/foo", "busybox", "sh", "-c", "cat /foo/bar")
  206. c.Assert(strings.TrimSpace(out), check.Equals, "hello", check.Commentf("volume data was removed"))
  207. dockerCmd(c, "rm", "test2")
  208. dockerCmd(c, "volume", "rm", volumeID)
  209. c.Assert(
  210. exec.Command("volume", "rm", "doesntexist").Run(),
  211. check.Not(check.IsNil),
  212. check.Commentf("volume rm should fail with non-existent volume"),
  213. )
  214. }
  215. func (s *DockerSuite) TestVolumeCliNoArgs(c *check.C) {
  216. out, _ := dockerCmd(c, "volume")
  217. // no args should produce the cmd usage output
  218. usage := "Usage: docker volume COMMAND"
  219. c.Assert(out, checker.Contains, usage)
  220. // invalid arg should error and show the command usage on stderr
  221. _, stderr, _, err := runCommandWithStdoutStderr(exec.Command(dockerBinary, "volume", "somearg"))
  222. c.Assert(err, check.NotNil, check.Commentf(stderr))
  223. c.Assert(stderr, checker.Contains, usage)
  224. // invalid flag should error and show the flag error and cmd usage
  225. _, stderr, _, err = runCommandWithStdoutStderr(exec.Command(dockerBinary, "volume", "--no-such-flag"))
  226. c.Assert(err, check.NotNil, check.Commentf(stderr))
  227. c.Assert(stderr, checker.Contains, usage)
  228. c.Assert(stderr, checker.Contains, "unknown flag: --no-such-flag")
  229. }
  230. func (s *DockerSuite) TestVolumeCliInspectTmplError(c *check.C) {
  231. out, _ := dockerCmd(c, "volume", "create")
  232. name := strings.TrimSpace(out)
  233. out, exitCode, err := dockerCmdWithError("volume", "inspect", "--format='{{ .FooBar }}'", name)
  234. c.Assert(err, checker.NotNil, check.Commentf("Output: %s", out))
  235. c.Assert(exitCode, checker.Equals, 1, check.Commentf("Output: %s", out))
  236. c.Assert(out, checker.Contains, "Template parsing error")
  237. }
  238. func (s *DockerSuite) TestVolumeCliCreateWithOpts(c *check.C) {
  239. testRequires(c, DaemonIsLinux)
  240. dockerCmd(c, "volume", "create", "-d", "local", "test", "--opt=type=tmpfs", "--opt=device=tmpfs", "--opt=o=size=1m,uid=1000")
  241. out, _ := dockerCmd(c, "run", "-v", "test:/foo", "busybox", "mount")
  242. mounts := strings.Split(out, "\n")
  243. var found bool
  244. for _, m := range mounts {
  245. if strings.Contains(m, "/foo") {
  246. found = true
  247. info := strings.Fields(m)
  248. // tmpfs on <path> type tmpfs (rw,relatime,size=1024k,uid=1000)
  249. c.Assert(info[0], checker.Equals, "tmpfs")
  250. c.Assert(info[2], checker.Equals, "/foo")
  251. c.Assert(info[4], checker.Equals, "tmpfs")
  252. c.Assert(info[5], checker.Contains, "uid=1000")
  253. c.Assert(info[5], checker.Contains, "size=1024k")
  254. }
  255. }
  256. c.Assert(found, checker.Equals, true)
  257. }
  258. func (s *DockerSuite) TestVolumeCliCreateLabel(c *check.C) {
  259. testVol := "testvolcreatelabel"
  260. testLabel := "foo"
  261. testValue := "bar"
  262. out, _, err := dockerCmdWithError("volume", "create", "--label", testLabel+"="+testValue, testVol)
  263. c.Assert(err, check.IsNil)
  264. out, _ = dockerCmd(c, "volume", "inspect", "--format={{ .Labels."+testLabel+" }}", testVol)
  265. c.Assert(strings.TrimSpace(out), check.Equals, testValue)
  266. }
  267. func (s *DockerSuite) TestVolumeCliCreateLabelMultiple(c *check.C) {
  268. testVol := "testvolcreatelabel"
  269. testLabels := map[string]string{
  270. "foo": "bar",
  271. "baz": "foo",
  272. }
  273. args := []string{
  274. "volume",
  275. "create",
  276. testVol,
  277. }
  278. for k, v := range testLabels {
  279. args = append(args, "--label", k+"="+v)
  280. }
  281. out, _, err := dockerCmdWithError(args...)
  282. c.Assert(err, check.IsNil)
  283. for k, v := range testLabels {
  284. out, _ = dockerCmd(c, "volume", "inspect", "--format={{ .Labels."+k+" }}", testVol)
  285. c.Assert(strings.TrimSpace(out), check.Equals, v)
  286. }
  287. }
  288. func (s *DockerSuite) TestVolumeCliLsFilterLabels(c *check.C) {
  289. testVol1 := "testvolcreatelabel-1"
  290. out, _, err := dockerCmdWithError("volume", "create", "--label", "foo=bar1", testVol1)
  291. c.Assert(err, check.IsNil)
  292. testVol2 := "testvolcreatelabel-2"
  293. out, _, err = dockerCmdWithError("volume", "create", "--label", "foo=bar2", testVol2)
  294. c.Assert(err, check.IsNil)
  295. out, _ = dockerCmd(c, "volume", "ls", "--filter", "label=foo")
  296. // filter with label=key
  297. c.Assert(out, checker.Contains, "testvolcreatelabel-1\n", check.Commentf("expected volume 'testvolcreatelabel-1' in output"))
  298. c.Assert(out, checker.Contains, "testvolcreatelabel-2\n", check.Commentf("expected volume 'testvolcreatelabel-2' in output"))
  299. out, _ = dockerCmd(c, "volume", "ls", "--filter", "label=foo=bar1")
  300. // filter with label=key=value
  301. c.Assert(out, checker.Contains, "testvolcreatelabel-1\n", check.Commentf("expected volume 'testvolcreatelabel-1' in output"))
  302. c.Assert(out, check.Not(checker.Contains), "testvolcreatelabel-2\n", check.Commentf("expected volume 'testvolcreatelabel-2 in output"))
  303. out, _ = dockerCmd(c, "volume", "ls", "--filter", "label=non-exist")
  304. outArr := strings.Split(strings.TrimSpace(out), "\n")
  305. c.Assert(len(outArr), check.Equals, 1, check.Commentf("\n%s", out))
  306. out, _ = dockerCmd(c, "volume", "ls", "--filter", "label=foo=non-exist")
  307. outArr = strings.Split(strings.TrimSpace(out), "\n")
  308. c.Assert(len(outArr), check.Equals, 1, check.Commentf("\n%s", out))
  309. }
  310. func (s *DockerSuite) TestVolumeCliRmForceUsage(c *check.C) {
  311. out, _ := dockerCmd(c, "volume", "create")
  312. id := strings.TrimSpace(out)
  313. dockerCmd(c, "volume", "rm", "-f", id)
  314. dockerCmd(c, "volume", "rm", "--force", "nonexist")
  315. out, _ = dockerCmd(c, "volume", "ls")
  316. outArr := strings.Split(strings.TrimSpace(out), "\n")
  317. c.Assert(len(outArr), check.Equals, 1, check.Commentf("%s\n", out))
  318. }
  319. func (s *DockerSuite) TestVolumeCliRmForce(c *check.C) {
  320. testRequires(c, SameHostDaemon, DaemonIsLinux)
  321. name := "test"
  322. out, _ := dockerCmd(c, "volume", "create", name)
  323. id := strings.TrimSpace(out)
  324. c.Assert(id, checker.Equals, name)
  325. out, _ = dockerCmd(c, "volume", "inspect", "--format", "{{.Mountpoint}}", name)
  326. c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
  327. // Mountpoint is in the form of "/var/lib/docker/volumes/.../_data", removing `/_data`
  328. path := strings.TrimSuffix(strings.TrimSpace(out), "/_data")
  329. out, _, err := runCommandWithOutput(exec.Command("rm", "-rf", path))
  330. c.Assert(err, check.IsNil)
  331. dockerCmd(c, "volume", "rm", "-f", "test")
  332. out, _ = dockerCmd(c, "volume", "ls")
  333. c.Assert(out, checker.Not(checker.Contains), name)
  334. dockerCmd(c, "volume", "create", "test")
  335. out, _ = dockerCmd(c, "volume", "ls")
  336. c.Assert(out, checker.Contains, name)
  337. }