github.com/lacework-dev/go-moby@v20.10.12+incompatible/integration/container/update_linux_test.go (about) 1 package container // import "github.com/docker/docker/integration/container" 2 3 import ( 4 "context" 5 "strconv" 6 "strings" 7 "testing" 8 "time" 9 10 containertypes "github.com/docker/docker/api/types/container" 11 "github.com/docker/docker/client" 12 "github.com/docker/docker/integration/internal/container" 13 "github.com/docker/docker/testutil/request" 14 "gotest.tools/v3/assert" 15 is "gotest.tools/v3/assert/cmp" 16 "gotest.tools/v3/poll" 17 "gotest.tools/v3/skip" 18 ) 19 20 func TestUpdateMemory(t *testing.T) { 21 skip.If(t, testEnv.DaemonInfo.OSType == "windows") 22 skip.If(t, testEnv.DaemonInfo.CgroupDriver == "none") 23 skip.If(t, !testEnv.DaemonInfo.MemoryLimit) 24 skip.If(t, !testEnv.DaemonInfo.SwapLimit) 25 26 defer setupTest(t)() 27 client := testEnv.APIClient() 28 ctx := context.Background() 29 30 cID := container.Run(ctx, t, client, func(c *container.TestContainerConfig) { 31 c.HostConfig.Resources = containertypes.Resources{ 32 Memory: 200 * 1024 * 1024, 33 } 34 }) 35 36 poll.WaitOn(t, container.IsInState(ctx, client, cID, "running"), poll.WithDelay(100*time.Millisecond)) 37 38 const ( 39 setMemory int64 = 314572800 40 setMemorySwap int64 = 524288000 41 ) 42 43 _, err := client.ContainerUpdate(ctx, cID, containertypes.UpdateConfig{ 44 Resources: containertypes.Resources{ 45 Memory: setMemory, 46 MemorySwap: setMemorySwap, 47 }, 48 }) 49 assert.NilError(t, err) 50 51 inspect, err := client.ContainerInspect(ctx, cID) 52 assert.NilError(t, err) 53 assert.Check(t, is.Equal(setMemory, inspect.HostConfig.Memory)) 54 assert.Check(t, is.Equal(setMemorySwap, inspect.HostConfig.MemorySwap)) 55 56 memoryFile := "/sys/fs/cgroup/memory/memory.limit_in_bytes" 57 if testEnv.DaemonInfo.CgroupVersion == "2" { 58 memoryFile = "/sys/fs/cgroup/memory.max" 59 } 60 res, err := container.Exec(ctx, client, cID, 61 []string{"cat", memoryFile}) 62 assert.NilError(t, err) 63 assert.Assert(t, is.Len(res.Stderr(), 0)) 64 assert.Equal(t, 0, res.ExitCode) 65 assert.Check(t, is.Equal(strconv.FormatInt(setMemory, 10), strings.TrimSpace(res.Stdout()))) 66 67 // see ConvertMemorySwapToCgroupV2Value() for the convention: 68 // https://github.com/opencontainers/runc/commit/c86be8a2c118ca7bad7bbe9eaf106c659a83940d 69 if testEnv.DaemonInfo.CgroupVersion == "2" { 70 res, err = container.Exec(ctx, client, cID, 71 []string{"cat", "/sys/fs/cgroup/memory.swap.max"}) 72 assert.NilError(t, err) 73 assert.Assert(t, is.Len(res.Stderr(), 0)) 74 assert.Equal(t, 0, res.ExitCode) 75 assert.Check(t, is.Equal(strconv.FormatInt(setMemorySwap-setMemory, 10), strings.TrimSpace(res.Stdout()))) 76 } else { 77 res, err = container.Exec(ctx, client, cID, 78 []string{"cat", "/sys/fs/cgroup/memory/memory.memsw.limit_in_bytes"}) 79 assert.NilError(t, err) 80 assert.Assert(t, is.Len(res.Stderr(), 0)) 81 assert.Equal(t, 0, res.ExitCode) 82 assert.Check(t, is.Equal(strconv.FormatInt(setMemorySwap, 10), strings.TrimSpace(res.Stdout()))) 83 } 84 } 85 86 func TestUpdateCPUQuota(t *testing.T) { 87 skip.If(t, testEnv.DaemonInfo.CgroupDriver == "none") 88 defer setupTest(t)() 89 client := testEnv.APIClient() 90 ctx := context.Background() 91 92 cID := container.Run(ctx, t, client) 93 94 for _, test := range []struct { 95 desc string 96 update int64 97 }{ 98 {desc: "some random value", update: 15000}, 99 {desc: "a higher value", update: 20000}, 100 {desc: "a lower value", update: 10000}, 101 {desc: "unset value", update: -1}, 102 } { 103 if testEnv.DaemonInfo.CgroupVersion == "2" { 104 // On v2, specifying CPUQuota without CPUPeriod is currently broken: 105 // https://github.com/opencontainers/runc/issues/2456 106 // As a workaround we set them together. 107 _, err := client.ContainerUpdate(ctx, cID, containertypes.UpdateConfig{ 108 Resources: containertypes.Resources{ 109 CPUQuota: test.update, 110 CPUPeriod: 100000, 111 }, 112 }) 113 assert.NilError(t, err) 114 } else { 115 _, err := client.ContainerUpdate(ctx, cID, containertypes.UpdateConfig{ 116 Resources: containertypes.Resources{ 117 CPUQuota: test.update, 118 }, 119 }) 120 assert.NilError(t, err) 121 } 122 123 inspect, err := client.ContainerInspect(ctx, cID) 124 assert.NilError(t, err) 125 assert.Check(t, is.Equal(test.update, inspect.HostConfig.CPUQuota)) 126 127 if testEnv.DaemonInfo.CgroupVersion == "2" { 128 res, err := container.Exec(ctx, client, cID, 129 []string{"/bin/cat", "/sys/fs/cgroup/cpu.max"}) 130 assert.NilError(t, err) 131 assert.Assert(t, is.Len(res.Stderr(), 0)) 132 assert.Equal(t, 0, res.ExitCode) 133 134 quotaPeriodPair := strings.Fields(res.Stdout()) 135 quota := quotaPeriodPair[0] 136 if test.update == -1 { 137 assert.Check(t, is.Equal("max", quota)) 138 } else { 139 assert.Check(t, is.Equal(strconv.FormatInt(test.update, 10), quota)) 140 } 141 } else { 142 res, err := container.Exec(ctx, client, cID, 143 []string{"/bin/cat", "/sys/fs/cgroup/cpu/cpu.cfs_quota_us"}) 144 assert.NilError(t, err) 145 assert.Assert(t, is.Len(res.Stderr(), 0)) 146 assert.Equal(t, 0, res.ExitCode) 147 148 assert.Check(t, is.Equal(strconv.FormatInt(test.update, 10), strings.TrimSpace(res.Stdout()))) 149 } 150 } 151 } 152 153 func TestUpdatePidsLimit(t *testing.T) { 154 skip.If(t, testEnv.DaemonInfo.OSType == "windows") 155 skip.If(t, testEnv.DaemonInfo.CgroupDriver == "none") 156 skip.If(t, !testEnv.DaemonInfo.PidsLimit) 157 158 defer setupTest(t)() 159 apiClient := testEnv.APIClient() 160 oldAPIclient := request.NewAPIClient(t, client.WithVersion("1.24")) 161 ctx := context.Background() 162 163 intPtr := func(i int64) *int64 { 164 return &i 165 } 166 167 for _, test := range []struct { 168 desc string 169 oldAPI bool 170 initial *int64 171 update *int64 172 expect int64 173 expectCg string 174 }{ 175 {desc: "update from none", update: intPtr(32), expect: 32, expectCg: "32"}, 176 {desc: "no change", initial: intPtr(32), expect: 32, expectCg: "32"}, 177 {desc: "update lower", initial: intPtr(32), update: intPtr(16), expect: 16, expectCg: "16"}, 178 {desc: "update on old api ignores value", oldAPI: true, initial: intPtr(32), update: intPtr(16), expect: 32, expectCg: "32"}, 179 {desc: "unset limit with zero", initial: intPtr(32), update: intPtr(0), expect: 0, expectCg: "max"}, 180 {desc: "unset limit with minus one", initial: intPtr(32), update: intPtr(-1), expect: 0, expectCg: "max"}, 181 {desc: "unset limit with minus two", initial: intPtr(32), update: intPtr(-2), expect: 0, expectCg: "max"}, 182 } { 183 c := apiClient 184 if test.oldAPI { 185 c = oldAPIclient 186 } 187 188 t.Run(test.desc, func(t *testing.T) { 189 // Using "network=host" to speed up creation (13.96s vs 6.54s) 190 cID := container.Run(ctx, t, apiClient, container.WithPidsLimit(test.initial), container.WithNetworkMode("host")) 191 192 _, err := c.ContainerUpdate(ctx, cID, containertypes.UpdateConfig{ 193 Resources: containertypes.Resources{ 194 PidsLimit: test.update, 195 }, 196 }) 197 assert.NilError(t, err) 198 199 inspect, err := c.ContainerInspect(ctx, cID) 200 assert.NilError(t, err) 201 assert.Assert(t, inspect.HostConfig.Resources.PidsLimit != nil) 202 assert.Equal(t, *inspect.HostConfig.Resources.PidsLimit, test.expect) 203 204 ctx, cancel := context.WithTimeout(ctx, 60*time.Second) 205 defer cancel() 206 207 pidsFile := "/sys/fs/cgroup/pids/pids.max" 208 if testEnv.DaemonInfo.CgroupVersion == "2" { 209 pidsFile = "/sys/fs/cgroup/pids.max" 210 } 211 res, err := container.Exec(ctx, c, cID, []string{"cat", pidsFile}) 212 assert.NilError(t, err) 213 assert.Assert(t, is.Len(res.Stderr(), 0)) 214 215 out := strings.TrimSpace(res.Stdout()) 216 assert.Equal(t, out, test.expectCg) 217 }) 218 } 219 }