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