docker_cli_service_create_test.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  1. // +build !windows
  2. package main
  3. import (
  4. "encoding/json"
  5. "fmt"
  6. "path/filepath"
  7. "strings"
  8. "github.com/docker/docker/api/types"
  9. "github.com/docker/docker/api/types/mount"
  10. "github.com/docker/docker/api/types/swarm"
  11. "github.com/docker/docker/integration-cli/checker"
  12. "github.com/go-check/check"
  13. "gotest.tools/assert"
  14. )
  15. func (s *DockerSwarmSuite) TestServiceCreateMountVolume(c *testing.T) {
  16. d := s.AddDaemon(c, true, true)
  17. out, err := d.Cmd("service", "create", "--no-resolve-image", "--detach=true", "--mount", "type=volume,source=foo,target=/foo,volume-nocopy", "busybox", "top")
  18. assert.NilError(c, err, out)
  19. id := strings.TrimSpace(out)
  20. var tasks []swarm.Task
  21. waitAndAssert(c, defaultReconciliationTimeout, func(c *testing.T) (interface{}, check.CommentInterface) {
  22. tasks = d.GetServiceTasks(c, id)
  23. return len(tasks) > 0, nil
  24. }, checker.Equals, true)
  25. task := tasks[0]
  26. waitAndAssert(c, defaultReconciliationTimeout, func(c *testing.T) (interface{}, check.CommentInterface) {
  27. if task.NodeID == "" || task.Status.ContainerStatus == nil {
  28. task = d.GetTask(c, task.ID)
  29. }
  30. return task.NodeID != "" && task.Status.ContainerStatus != nil, nil
  31. }, checker.Equals, true)
  32. // check container mount config
  33. out, err = s.nodeCmd(c, task.NodeID, "inspect", "--format", "{{json .HostConfig.Mounts}}", task.Status.ContainerStatus.ContainerID)
  34. assert.NilError(c, err, out)
  35. var mountConfig []mount.Mount
  36. assert.Assert(c, json.Unmarshal([]byte(out), &mountConfig), checker.IsNil)
  37. assert.Assert(c, mountConfig, checker.HasLen, 1)
  38. assert.Assert(c, mountConfig[0].Source, checker.Equals, "foo")
  39. assert.Assert(c, mountConfig[0].Target, checker.Equals, "/foo")
  40. assert.Assert(c, mountConfig[0].Type, checker.Equals, mount.TypeVolume)
  41. assert.Assert(c, mountConfig[0].VolumeOptions, checker.NotNil)
  42. assert.Assert(c, mountConfig[0].VolumeOptions.NoCopy, checker.True)
  43. // check container mounts actual
  44. out, err = s.nodeCmd(c, task.NodeID, "inspect", "--format", "{{json .Mounts}}", task.Status.ContainerStatus.ContainerID)
  45. assert.NilError(c, err, out)
  46. var mounts []types.MountPoint
  47. assert.Assert(c, json.Unmarshal([]byte(out), &mounts), checker.IsNil)
  48. assert.Assert(c, mounts, checker.HasLen, 1)
  49. assert.Assert(c, mounts[0].Type, checker.Equals, mount.TypeVolume)
  50. assert.Assert(c, mounts[0].Name, checker.Equals, "foo")
  51. assert.Assert(c, mounts[0].Destination, checker.Equals, "/foo")
  52. assert.Assert(c, mounts[0].RW, checker.Equals, true)
  53. }
  54. func (s *DockerSwarmSuite) TestServiceCreateWithSecretSimple(c *testing.T) {
  55. d := s.AddDaemon(c, true, true)
  56. serviceName := "test-service-secret"
  57. testName := "test_secret"
  58. id := d.CreateSecret(c, swarm.SecretSpec{
  59. Annotations: swarm.Annotations{
  60. Name: testName,
  61. },
  62. Data: []byte("TESTINGDATA"),
  63. })
  64. assert.Assert(c, id, checker.Not(checker.Equals), "", check.Commentf("secrets: %s", id))
  65. out, err := d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", serviceName, "--secret", testName, "busybox", "top")
  66. assert.NilError(c, err, out)
  67. out, err = d.Cmd("service", "inspect", "--format", "{{ json .Spec.TaskTemplate.ContainerSpec.Secrets }}", serviceName)
  68. assert.NilError(c, err)
  69. var refs []swarm.SecretReference
  70. assert.Assert(c, json.Unmarshal([]byte(out), &refs), checker.IsNil)
  71. assert.Equal(c, len(refs), 1)
  72. assert.Assert(c, refs[0].SecretName, checker.Equals, testName)
  73. assert.Assert(c, refs[0].File != nil)
  74. assert.Assert(c, refs[0].File.Name, checker.Equals, testName)
  75. assert.Assert(c, refs[0].File.UID, checker.Equals, "0")
  76. assert.Assert(c, refs[0].File.GID, checker.Equals, "0")
  77. out, err = d.Cmd("service", "rm", serviceName)
  78. assert.NilError(c, err, out)
  79. d.DeleteSecret(c, testName)
  80. }
  81. func (s *DockerSwarmSuite) TestServiceCreateWithSecretSourceTargetPaths(c *testing.T) {
  82. d := s.AddDaemon(c, true, true)
  83. testPaths := map[string]string{
  84. "app": "/etc/secret",
  85. "test_secret": "test_secret",
  86. "relative_secret": "relative/secret",
  87. "escapes_in_container": "../secret",
  88. }
  89. var secretFlags []string
  90. for testName, testTarget := range testPaths {
  91. id := d.CreateSecret(c, swarm.SecretSpec{
  92. Annotations: swarm.Annotations{
  93. Name: testName,
  94. },
  95. Data: []byte("TESTINGDATA " + testName + " " + testTarget),
  96. })
  97. assert.Assert(c, id, checker.Not(checker.Equals), "", check.Commentf("secrets: %s", id))
  98. secretFlags = append(secretFlags, "--secret", fmt.Sprintf("source=%s,target=%s", testName, testTarget))
  99. }
  100. serviceName := "svc"
  101. serviceCmd := []string{"service", "create", "--detach", "--no-resolve-image", "--name", serviceName}
  102. serviceCmd = append(serviceCmd, secretFlags...)
  103. serviceCmd = append(serviceCmd, "busybox", "top")
  104. out, err := d.Cmd(serviceCmd...)
  105. assert.NilError(c, err, out)
  106. out, err = d.Cmd("service", "inspect", "--format", "{{ json .Spec.TaskTemplate.ContainerSpec.Secrets }}", serviceName)
  107. assert.NilError(c, err)
  108. var refs []swarm.SecretReference
  109. assert.Assert(c, json.Unmarshal([]byte(out), &refs), checker.IsNil)
  110. assert.Equal(c, len(refs), len(testPaths))
  111. var tasks []swarm.Task
  112. waitAndAssert(c, defaultReconciliationTimeout, func(c *testing.T) (interface{}, check.CommentInterface) {
  113. tasks = d.GetServiceTasks(c, serviceName)
  114. return len(tasks) > 0, nil
  115. }, checker.Equals, true)
  116. task := tasks[0]
  117. waitAndAssert(c, defaultReconciliationTimeout, func(c *testing.T) (interface{}, check.CommentInterface) {
  118. if task.NodeID == "" || task.Status.ContainerStatus == nil {
  119. task = d.GetTask(c, task.ID)
  120. }
  121. return task.NodeID != "" && task.Status.ContainerStatus != nil, nil
  122. }, checker.Equals, true)
  123. for testName, testTarget := range testPaths {
  124. path := testTarget
  125. if !filepath.IsAbs(path) {
  126. path = filepath.Join("/run/secrets", path)
  127. }
  128. out, err := d.Cmd("exec", task.Status.ContainerStatus.ContainerID, "cat", path)
  129. assert.NilError(c, err)
  130. assert.Equal(c, out, "TESTINGDATA "+testName+" "+testTarget)
  131. }
  132. out, err = d.Cmd("service", "rm", serviceName)
  133. assert.NilError(c, err, out)
  134. }
  135. func (s *DockerSwarmSuite) TestServiceCreateWithSecretReferencedTwice(c *testing.T) {
  136. d := s.AddDaemon(c, true, true)
  137. id := d.CreateSecret(c, swarm.SecretSpec{
  138. Annotations: swarm.Annotations{
  139. Name: "mysecret",
  140. },
  141. Data: []byte("TESTINGDATA"),
  142. })
  143. assert.Assert(c, id, checker.Not(checker.Equals), "", check.Commentf("secrets: %s", id))
  144. serviceName := "svc"
  145. out, err := d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", serviceName, "--secret", "source=mysecret,target=target1", "--secret", "source=mysecret,target=target2", "busybox", "top")
  146. assert.NilError(c, err, out)
  147. out, err = d.Cmd("service", "inspect", "--format", "{{ json .Spec.TaskTemplate.ContainerSpec.Secrets }}", serviceName)
  148. assert.NilError(c, err)
  149. var refs []swarm.SecretReference
  150. assert.Assert(c, json.Unmarshal([]byte(out), &refs), checker.IsNil)
  151. assert.Equal(c, len(refs), 2)
  152. var tasks []swarm.Task
  153. waitAndAssert(c, defaultReconciliationTimeout, func(c *testing.T) (interface{}, check.CommentInterface) {
  154. tasks = d.GetServiceTasks(c, serviceName)
  155. return len(tasks) > 0, nil
  156. }, checker.Equals, true)
  157. task := tasks[0]
  158. waitAndAssert(c, defaultReconciliationTimeout, func(c *testing.T) (interface{}, check.CommentInterface) {
  159. if task.NodeID == "" || task.Status.ContainerStatus == nil {
  160. task = d.GetTask(c, task.ID)
  161. }
  162. return task.NodeID != "" && task.Status.ContainerStatus != nil, nil
  163. }, checker.Equals, true)
  164. for _, target := range []string{"target1", "target2"} {
  165. assert.NilError(c, err, out)
  166. path := filepath.Join("/run/secrets", target)
  167. out, err := d.Cmd("exec", task.Status.ContainerStatus.ContainerID, "cat", path)
  168. assert.NilError(c, err)
  169. assert.Equal(c, out, "TESTINGDATA")
  170. }
  171. out, err = d.Cmd("service", "rm", serviceName)
  172. assert.NilError(c, err, out)
  173. }
  174. func (s *DockerSwarmSuite) TestServiceCreateWithConfigSimple(c *testing.T) {
  175. d := s.AddDaemon(c, true, true)
  176. serviceName := "test-service-config"
  177. testName := "test_config"
  178. id := d.CreateConfig(c, swarm.ConfigSpec{
  179. Annotations: swarm.Annotations{
  180. Name: testName,
  181. },
  182. Data: []byte("TESTINGDATA"),
  183. })
  184. assert.Assert(c, id, checker.Not(checker.Equals), "", check.Commentf("configs: %s", id))
  185. out, err := d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", serviceName, "--config", testName, "busybox", "top")
  186. assert.NilError(c, err, out)
  187. out, err = d.Cmd("service", "inspect", "--format", "{{ json .Spec.TaskTemplate.ContainerSpec.Configs }}", serviceName)
  188. assert.NilError(c, err)
  189. var refs []swarm.ConfigReference
  190. assert.Assert(c, json.Unmarshal([]byte(out), &refs), checker.IsNil)
  191. assert.Equal(c, len(refs), 1)
  192. assert.Assert(c, refs[0].ConfigName, checker.Equals, testName)
  193. assert.Assert(c, refs[0].File != nil)
  194. assert.Assert(c, refs[0].File.Name, checker.Equals, testName)
  195. assert.Assert(c, refs[0].File.UID, checker.Equals, "0")
  196. assert.Assert(c, refs[0].File.GID, checker.Equals, "0")
  197. out, err = d.Cmd("service", "rm", serviceName)
  198. assert.NilError(c, err, out)
  199. d.DeleteConfig(c, testName)
  200. }
  201. func (s *DockerSwarmSuite) TestServiceCreateWithConfigSourceTargetPaths(c *testing.T) {
  202. d := s.AddDaemon(c, true, true)
  203. testPaths := map[string]string{
  204. "app": "/etc/config",
  205. "test_config": "test_config",
  206. "relative_config": "relative/config",
  207. }
  208. var configFlags []string
  209. for testName, testTarget := range testPaths {
  210. id := d.CreateConfig(c, swarm.ConfigSpec{
  211. Annotations: swarm.Annotations{
  212. Name: testName,
  213. },
  214. Data: []byte("TESTINGDATA " + testName + " " + testTarget),
  215. })
  216. assert.Assert(c, id, checker.Not(checker.Equals), "", check.Commentf("configs: %s", id))
  217. configFlags = append(configFlags, "--config", fmt.Sprintf("source=%s,target=%s", testName, testTarget))
  218. }
  219. serviceName := "svc"
  220. serviceCmd := []string{"service", "create", "--detach", "--no-resolve-image", "--name", serviceName}
  221. serviceCmd = append(serviceCmd, configFlags...)
  222. serviceCmd = append(serviceCmd, "busybox", "top")
  223. out, err := d.Cmd(serviceCmd...)
  224. assert.NilError(c, err, out)
  225. out, err = d.Cmd("service", "inspect", "--format", "{{ json .Spec.TaskTemplate.ContainerSpec.Configs }}", serviceName)
  226. assert.NilError(c, err)
  227. var refs []swarm.ConfigReference
  228. assert.Assert(c, json.Unmarshal([]byte(out), &refs), checker.IsNil)
  229. assert.Equal(c, len(refs), len(testPaths))
  230. var tasks []swarm.Task
  231. waitAndAssert(c, defaultReconciliationTimeout, func(c *testing.T) (interface{}, check.CommentInterface) {
  232. tasks = d.GetServiceTasks(c, serviceName)
  233. return len(tasks) > 0, nil
  234. }, checker.Equals, true)
  235. task := tasks[0]
  236. waitAndAssert(c, defaultReconciliationTimeout, func(c *testing.T) (interface{}, check.CommentInterface) {
  237. if task.NodeID == "" || task.Status.ContainerStatus == nil {
  238. task = d.GetTask(c, task.ID)
  239. }
  240. return task.NodeID != "" && task.Status.ContainerStatus != nil, nil
  241. }, checker.Equals, true)
  242. for testName, testTarget := range testPaths {
  243. path := testTarget
  244. if !filepath.IsAbs(path) {
  245. path = filepath.Join("/", path)
  246. }
  247. out, err := d.Cmd("exec", task.Status.ContainerStatus.ContainerID, "cat", path)
  248. assert.NilError(c, err)
  249. assert.Equal(c, out, "TESTINGDATA "+testName+" "+testTarget)
  250. }
  251. out, err = d.Cmd("service", "rm", serviceName)
  252. assert.NilError(c, err, out)
  253. }
  254. func (s *DockerSwarmSuite) TestServiceCreateWithConfigReferencedTwice(c *testing.T) {
  255. d := s.AddDaemon(c, true, true)
  256. id := d.CreateConfig(c, swarm.ConfigSpec{
  257. Annotations: swarm.Annotations{
  258. Name: "myconfig",
  259. },
  260. Data: []byte("TESTINGDATA"),
  261. })
  262. assert.Assert(c, id, checker.Not(checker.Equals), "", check.Commentf("configs: %s", id))
  263. serviceName := "svc"
  264. out, err := d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", serviceName, "--config", "source=myconfig,target=target1", "--config", "source=myconfig,target=target2", "busybox", "top")
  265. assert.NilError(c, err, out)
  266. out, err = d.Cmd("service", "inspect", "--format", "{{ json .Spec.TaskTemplate.ContainerSpec.Configs }}", serviceName)
  267. assert.NilError(c, err)
  268. var refs []swarm.ConfigReference
  269. assert.Assert(c, json.Unmarshal([]byte(out), &refs), checker.IsNil)
  270. assert.Equal(c, len(refs), 2)
  271. var tasks []swarm.Task
  272. waitAndAssert(c, defaultReconciliationTimeout, func(c *testing.T) (interface{}, check.CommentInterface) {
  273. tasks = d.GetServiceTasks(c, serviceName)
  274. return len(tasks) > 0, nil
  275. }, checker.Equals, true)
  276. task := tasks[0]
  277. waitAndAssert(c, defaultReconciliationTimeout, func(c *testing.T) (interface{}, check.CommentInterface) {
  278. if task.NodeID == "" || task.Status.ContainerStatus == nil {
  279. task = d.GetTask(c, task.ID)
  280. }
  281. return task.NodeID != "" && task.Status.ContainerStatus != nil, nil
  282. }, checker.Equals, true)
  283. for _, target := range []string{"target1", "target2"} {
  284. assert.NilError(c, err, out)
  285. path := filepath.Join("/", target)
  286. out, err := d.Cmd("exec", task.Status.ContainerStatus.ContainerID, "cat", path)
  287. assert.NilError(c, err)
  288. assert.Equal(c, out, "TESTINGDATA")
  289. }
  290. out, err = d.Cmd("service", "rm", serviceName)
  291. assert.NilError(c, err, out)
  292. }
  293. func (s *DockerSwarmSuite) TestServiceCreateMountTmpfs(c *testing.T) {
  294. d := s.AddDaemon(c, true, true)
  295. out, err := d.Cmd("service", "create", "--no-resolve-image", "--detach=true", "--mount", "type=tmpfs,target=/foo,tmpfs-size=1MB", "busybox", "sh", "-c", "mount | grep foo; tail -f /dev/null")
  296. assert.NilError(c, err, out)
  297. id := strings.TrimSpace(out)
  298. var tasks []swarm.Task
  299. waitAndAssert(c, defaultReconciliationTimeout, func(c *testing.T) (interface{}, check.CommentInterface) {
  300. tasks = d.GetServiceTasks(c, id)
  301. return len(tasks) > 0, nil
  302. }, checker.Equals, true)
  303. task := tasks[0]
  304. waitAndAssert(c, defaultReconciliationTimeout, func(c *testing.T) (interface{}, check.CommentInterface) {
  305. if task.NodeID == "" || task.Status.ContainerStatus == nil {
  306. task = d.GetTask(c, task.ID)
  307. }
  308. return task.NodeID != "" && task.Status.ContainerStatus != nil, nil
  309. }, checker.Equals, true)
  310. // check container mount config
  311. out, err = s.nodeCmd(c, task.NodeID, "inspect", "--format", "{{json .HostConfig.Mounts}}", task.Status.ContainerStatus.ContainerID)
  312. assert.NilError(c, err, out)
  313. var mountConfig []mount.Mount
  314. assert.Assert(c, json.Unmarshal([]byte(out), &mountConfig), checker.IsNil)
  315. assert.Assert(c, mountConfig, checker.HasLen, 1)
  316. assert.Assert(c, mountConfig[0].Source, checker.Equals, "")
  317. assert.Assert(c, mountConfig[0].Target, checker.Equals, "/foo")
  318. assert.Assert(c, mountConfig[0].Type, checker.Equals, mount.TypeTmpfs)
  319. assert.Assert(c, mountConfig[0].TmpfsOptions, checker.NotNil)
  320. assert.Assert(c, mountConfig[0].TmpfsOptions.SizeBytes, checker.Equals, int64(1048576))
  321. // check container mounts actual
  322. out, err = s.nodeCmd(c, task.NodeID, "inspect", "--format", "{{json .Mounts}}", task.Status.ContainerStatus.ContainerID)
  323. assert.NilError(c, err, out)
  324. var mounts []types.MountPoint
  325. assert.Assert(c, json.Unmarshal([]byte(out), &mounts), checker.IsNil)
  326. assert.Assert(c, mounts, checker.HasLen, 1)
  327. assert.Assert(c, mounts[0].Type, checker.Equals, mount.TypeTmpfs)
  328. assert.Assert(c, mounts[0].Name, checker.Equals, "")
  329. assert.Assert(c, mounts[0].Destination, checker.Equals, "/foo")
  330. assert.Assert(c, mounts[0].RW, checker.Equals, true)
  331. out, err = s.nodeCmd(c, task.NodeID, "logs", task.Status.ContainerStatus.ContainerID)
  332. assert.NilError(c, err, out)
  333. assert.Assert(c, strings.HasPrefix(strings.TrimSpace(out), "tmpfs on /foo type tmpfs"))
  334. assert.Assert(c, strings.Contains(strings.TrimSpace(out), "size=1024k"))
  335. }
  336. func (s *DockerSwarmSuite) TestServiceCreateWithNetworkAlias(c *testing.T) {
  337. d := s.AddDaemon(c, true, true)
  338. out, err := d.Cmd("network", "create", "--scope=swarm", "test_swarm_br")
  339. assert.NilError(c, err, out)
  340. out, err = d.Cmd("service", "create", "--no-resolve-image", "--detach=true", "--network=name=test_swarm_br,alias=srv_alias", "--name=alias_tst_container", "busybox", "top")
  341. assert.NilError(c, err, out)
  342. id := strings.TrimSpace(out)
  343. var tasks []swarm.Task
  344. waitAndAssert(c, defaultReconciliationTimeout, func(c *testing.T) (interface{}, check.CommentInterface) {
  345. tasks = d.GetServiceTasks(c, id)
  346. return len(tasks) > 0, nil
  347. }, checker.Equals, true)
  348. task := tasks[0]
  349. waitAndAssert(c, defaultReconciliationTimeout, func(c *testing.T) (interface{}, check.CommentInterface) {
  350. if task.NodeID == "" || task.Status.ContainerStatus == nil {
  351. task = d.GetTask(c, task.ID)
  352. }
  353. return task.NodeID != "" && task.Status.ContainerStatus != nil, nil
  354. }, checker.Equals, true)
  355. // check container alias config
  356. out, err = s.nodeCmd(c, task.NodeID, "inspect", "--format", "{{json .NetworkSettings.Networks.test_swarm_br.Aliases}}", task.Status.ContainerStatus.ContainerID)
  357. assert.NilError(c, err, out)
  358. // Make sure the only alias seen is the container-id
  359. var aliases []string
  360. assert.Assert(c, json.Unmarshal([]byte(out), &aliases), checker.IsNil)
  361. assert.Assert(c, aliases, checker.HasLen, 1)
  362. assert.Assert(c, task.Status.ContainerStatus.ContainerID, checker.Contains, aliases[0])
  363. }