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