docker_cli_update_unix_test.go 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. //go:build !windows
  2. package main
  3. import (
  4. "context"
  5. "encoding/json"
  6. "fmt"
  7. "os/exec"
  8. "strings"
  9. "testing"
  10. "time"
  11. "github.com/creack/pty"
  12. "github.com/docker/docker/api/types"
  13. "github.com/docker/docker/client"
  14. "github.com/docker/docker/integration-cli/cli"
  15. "github.com/docker/docker/testutil"
  16. "github.com/docker/docker/testutil/request"
  17. "gotest.tools/v3/assert"
  18. )
  19. func (s *DockerCLIUpdateSuite) TearDownTest(ctx context.Context, c *testing.T) {
  20. s.ds.TearDownTest(ctx, c)
  21. }
  22. func (s *DockerCLIUpdateSuite) OnTimeout(c *testing.T) {
  23. s.ds.OnTimeout(c)
  24. }
  25. func (s *DockerCLIUpdateSuite) TestUpdateRunningContainer(c *testing.T) {
  26. testRequires(c, DaemonIsLinux)
  27. testRequires(c, memoryLimitSupport)
  28. const name = "test-update-container"
  29. cli.DockerCmd(c, "run", "-d", "--name", name, "-m", "300M", "busybox", "top")
  30. cli.DockerCmd(c, "update", "-m", "500M", name)
  31. assert.Equal(c, inspectField(c, name, "HostConfig.Memory"), "524288000")
  32. const file = "/sys/fs/cgroup/memory/memory.limit_in_bytes"
  33. out := cli.DockerCmd(c, "exec", name, "cat", file).Stdout()
  34. assert.Equal(c, strings.TrimSpace(out), "524288000")
  35. }
  36. func (s *DockerCLIUpdateSuite) TestUpdateRunningContainerWithRestart(c *testing.T) {
  37. testRequires(c, DaemonIsLinux)
  38. testRequires(c, memoryLimitSupport)
  39. const name = "test-update-container"
  40. cli.DockerCmd(c, "run", "-d", "--name", name, "-m", "300M", "busybox", "top")
  41. cli.DockerCmd(c, "update", "-m", "500M", name)
  42. cli.DockerCmd(c, "restart", name)
  43. assert.Equal(c, inspectField(c, name, "HostConfig.Memory"), "524288000")
  44. const file = "/sys/fs/cgroup/memory/memory.limit_in_bytes"
  45. out := cli.DockerCmd(c, "exec", name, "cat", file).Stdout()
  46. assert.Equal(c, strings.TrimSpace(out), "524288000")
  47. }
  48. func (s *DockerCLIUpdateSuite) TestUpdateStoppedContainer(c *testing.T) {
  49. testRequires(c, DaemonIsLinux)
  50. testRequires(c, memoryLimitSupport)
  51. const name = "test-update-container"
  52. const file = "/sys/fs/cgroup/memory/memory.limit_in_bytes"
  53. cli.DockerCmd(c, "run", "--name", name, "-m", "300M", "busybox", "cat", file)
  54. cli.DockerCmd(c, "update", "-m", "500M", name)
  55. assert.Equal(c, inspectField(c, name, "HostConfig.Memory"), "524288000")
  56. out := cli.DockerCmd(c, "start", "-a", name).Stdout()
  57. assert.Equal(c, strings.TrimSpace(out), "524288000")
  58. }
  59. func (s *DockerCLIUpdateSuite) TestUpdatePausedContainer(c *testing.T) {
  60. testRequires(c, DaemonIsLinux)
  61. testRequires(c, cpuShare)
  62. const name = "test-update-container"
  63. cli.DockerCmd(c, "run", "-d", "--name", name, "--cpu-shares", "1000", "busybox", "top")
  64. cli.DockerCmd(c, "pause", name)
  65. cli.DockerCmd(c, "update", "--cpu-shares", "500", name)
  66. assert.Equal(c, inspectField(c, name, "HostConfig.CPUShares"), "500")
  67. cli.DockerCmd(c, "unpause", name)
  68. const file = "/sys/fs/cgroup/cpu/cpu.shares"
  69. out := cli.DockerCmd(c, "exec", name, "cat", file).Stdout()
  70. assert.Equal(c, strings.TrimSpace(out), "500")
  71. }
  72. func (s *DockerCLIUpdateSuite) TestUpdateWithUntouchedFields(c *testing.T) {
  73. testRequires(c, DaemonIsLinux)
  74. testRequires(c, memoryLimitSupport)
  75. testRequires(c, cpuShare)
  76. const name = "test-update-container"
  77. cli.DockerCmd(c, "run", "-d", "--name", name, "-m", "300M", "--cpu-shares", "800", "busybox", "top")
  78. cli.DockerCmd(c, "update", "-m", "500M", name)
  79. // Update memory and not touch cpus, `cpuset.cpus` should still have the old value
  80. out := inspectField(c, name, "HostConfig.CPUShares")
  81. assert.Equal(c, out, "800")
  82. const file = "/sys/fs/cgroup/cpu/cpu.shares"
  83. out = cli.DockerCmd(c, "exec", name, "cat", file).Stdout()
  84. assert.Equal(c, strings.TrimSpace(out), "800")
  85. }
  86. func (s *DockerCLIUpdateSuite) TestUpdateContainerInvalidValue(c *testing.T) {
  87. testRequires(c, DaemonIsLinux)
  88. testRequires(c, memoryLimitSupport)
  89. const name = "test-update-container"
  90. cli.DockerCmd(c, "run", "-d", "--name", name, "-m", "300M", "busybox", "true")
  91. out, _, err := dockerCmdWithError("update", "-m", "2M", name)
  92. assert.ErrorContains(c, err, "")
  93. expected := "Minimum memory limit allowed is 6MB"
  94. assert.Assert(c, strings.Contains(out, expected))
  95. }
  96. func (s *DockerCLIUpdateSuite) TestUpdateContainerWithoutFlags(c *testing.T) {
  97. testRequires(c, DaemonIsLinux)
  98. testRequires(c, memoryLimitSupport)
  99. const name = "test-update-container"
  100. cli.DockerCmd(c, "run", "-d", "--name", name, "-m", "300M", "busybox", "true")
  101. _, _, err := dockerCmdWithError("update", name)
  102. assert.ErrorContains(c, err, "")
  103. }
  104. func (s *DockerCLIUpdateSuite) TestUpdateSwapMemoryOnly(c *testing.T) {
  105. testRequires(c, DaemonIsLinux)
  106. testRequires(c, memoryLimitSupport)
  107. testRequires(c, swapMemorySupport)
  108. const name = "test-update-container"
  109. cli.DockerCmd(c, "run", "-d", "--name", name, "--memory", "300M", "--memory-swap", "500M", "busybox", "top")
  110. cli.DockerCmd(c, "update", "--memory-swap", "600M", name)
  111. assert.Equal(c, inspectField(c, name, "HostConfig.MemorySwap"), "629145600")
  112. const file = "/sys/fs/cgroup/memory/memory.memsw.limit_in_bytes"
  113. out := cli.DockerCmd(c, "exec", name, "cat", file).Stdout()
  114. assert.Equal(c, strings.TrimSpace(out), "629145600")
  115. }
  116. func (s *DockerCLIUpdateSuite) TestUpdateInvalidSwapMemory(c *testing.T) {
  117. testRequires(c, DaemonIsLinux)
  118. testRequires(c, memoryLimitSupport)
  119. testRequires(c, swapMemorySupport)
  120. const name = "test-update-container"
  121. cli.DockerCmd(c, "run", "-d", "--name", name, "--memory", "300M", "--memory-swap", "500M", "busybox", "top")
  122. _, _, err := dockerCmdWithError("update", "--memory-swap", "200M", name)
  123. // Update invalid swap memory should fail.
  124. // This will pass docker config validation, but failed at kernel validation
  125. assert.ErrorContains(c, err, "")
  126. // Update invalid swap memory with failure should not change HostConfig
  127. assert.Equal(c, inspectField(c, name, "HostConfig.Memory"), "314572800")
  128. assert.Equal(c, inspectField(c, name, "HostConfig.MemorySwap"), "524288000")
  129. cli.DockerCmd(c, "update", "--memory-swap", "600M", name)
  130. assert.Equal(c, inspectField(c, name, "HostConfig.MemorySwap"), "629145600")
  131. const file = "/sys/fs/cgroup/memory/memory.memsw.limit_in_bytes"
  132. out := cli.DockerCmd(c, "exec", name, "cat", file).Stdout()
  133. assert.Equal(c, strings.TrimSpace(out), "629145600")
  134. }
  135. func (s *DockerCLIUpdateSuite) TestUpdateStats(c *testing.T) {
  136. testRequires(c, DaemonIsLinux)
  137. testRequires(c, memoryLimitSupport)
  138. testRequires(c, cpuCfsQuota)
  139. const name = "foo"
  140. cli.DockerCmd(c, "run", "-d", "-ti", "--name", name, "-m", "500m", "busybox")
  141. cli.WaitRun(c, name)
  142. getMemLimit := func(id string) uint64 {
  143. resp, body, err := request.Get(testutil.GetContext(c), fmt.Sprintf("/containers/%s/stats?stream=false", id))
  144. assert.NilError(c, err)
  145. assert.Equal(c, resp.Header.Get("Content-Type"), "application/json")
  146. var v *types.Stats
  147. err = json.NewDecoder(body).Decode(&v)
  148. assert.NilError(c, err)
  149. body.Close()
  150. return v.MemoryStats.Limit
  151. }
  152. preMemLimit := getMemLimit(name)
  153. cli.DockerCmd(c, "update", "--cpu-quota", "2000", name)
  154. curMemLimit := getMemLimit(name)
  155. assert.Equal(c, preMemLimit, curMemLimit)
  156. }
  157. func (s *DockerCLIUpdateSuite) TestUpdateMemoryWithSwapMemory(c *testing.T) {
  158. testRequires(c, DaemonIsLinux)
  159. testRequires(c, memoryLimitSupport)
  160. testRequires(c, swapMemorySupport)
  161. const name = "test-update-container"
  162. cli.DockerCmd(c, "run", "-d", "--name", name, "--memory", "300M", "busybox", "top")
  163. out, _, err := dockerCmdWithError("update", "--memory", "800M", name)
  164. assert.ErrorContains(c, err, "")
  165. assert.Assert(c, strings.Contains(out, "Memory limit should be smaller than already set memoryswap limit"))
  166. cli.DockerCmd(c, "update", "--memory", "800M", "--memory-swap", "1000M", name)
  167. }
  168. func (s *DockerCLIUpdateSuite) TestUpdateNotAffectMonitorRestartPolicy(c *testing.T) {
  169. testRequires(c, DaemonIsLinux, cpuShare)
  170. id := cli.DockerCmd(c, "run", "-tid", "--restart=always", "busybox", "sh").Stdout()
  171. id = strings.TrimSpace(id)
  172. cli.DockerCmd(c, "update", "--cpu-shares", "512", id)
  173. cpty, tty, err := pty.Open()
  174. assert.NilError(c, err)
  175. defer cpty.Close()
  176. cmd := exec.Command(dockerBinary, "attach", id)
  177. cmd.Stdin = tty
  178. assert.NilError(c, cmd.Start())
  179. defer cmd.Process.Kill()
  180. _, err = cpty.Write([]byte("exit\n"))
  181. assert.NilError(c, err)
  182. assert.NilError(c, cmd.Wait())
  183. // container should restart again and keep running
  184. err = waitInspect(id, "{{.RestartCount}}", "1", 30*time.Second)
  185. assert.NilError(c, err)
  186. cli.WaitRun(c, id)
  187. }
  188. func (s *DockerCLIUpdateSuite) TestUpdateWithNanoCPUs(c *testing.T) {
  189. testRequires(c, cpuCfsQuota, cpuCfsPeriod)
  190. const file1 = "/sys/fs/cgroup/cpu/cpu.cfs_quota_us"
  191. const file2 = "/sys/fs/cgroup/cpu/cpu.cfs_period_us"
  192. out := cli.DockerCmd(c, "run", "-d", "--cpus", "0.5", "--name", "top", "busybox", "top").Stdout()
  193. assert.Assert(c, strings.TrimSpace(out) != "")
  194. out = cli.DockerCmd(c, "exec", "top", "sh", "-c", fmt.Sprintf("cat %s && cat %s", file1, file2)).Combined()
  195. assert.Equal(c, strings.TrimSpace(out), "50000\n100000")
  196. clt, err := client.NewClientWithOpts(client.FromEnv)
  197. assert.NilError(c, err)
  198. inspect, err := clt.ContainerInspect(testutil.GetContext(c), "top")
  199. assert.NilError(c, err)
  200. assert.Equal(c, inspect.HostConfig.NanoCPUs, int64(500000000))
  201. out = inspectField(c, "top", "HostConfig.CpuQuota")
  202. assert.Equal(c, out, "0", "CPU CFS quota should be 0")
  203. out = inspectField(c, "top", "HostConfig.CpuPeriod")
  204. assert.Equal(c, out, "0", "CPU CFS period should be 0")
  205. out, _, err = dockerCmdWithError("update", "--cpu-quota", "80000", "top")
  206. assert.ErrorContains(c, err, "")
  207. assert.Assert(c, strings.Contains(out, "Conflicting options: CPU Quota cannot be updated as NanoCPUs has already been set"))
  208. cli.DockerCmd(c, "update", "--cpus", "0.8", "top")
  209. inspect, err = clt.ContainerInspect(testutil.GetContext(c), "top")
  210. assert.NilError(c, err)
  211. assert.Equal(c, inspect.HostConfig.NanoCPUs, int64(800000000))
  212. out = inspectField(c, "top", "HostConfig.CpuQuota")
  213. assert.Equal(c, out, "0", "CPU CFS quota should be 0")
  214. out = inspectField(c, "top", "HostConfig.CpuPeriod")
  215. assert.Equal(c, out, "0", "CPU CFS period should be 0")
  216. out = cli.DockerCmd(c, "exec", "top", "sh", "-c", fmt.Sprintf("cat %s && cat %s", file1, file2)).Combined()
  217. assert.Equal(c, strings.TrimSpace(out), "80000\n100000")
  218. }