From 60ef4ad1c873e99825dd08c9300cffb82f3b7aee Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Thu, 15 Sep 2022 00:52:30 +0200 Subject: [PATCH] stats: cap memory limit to the available memory Docker compatibility: cap the memory limit reported by the cgroup to the maximum available memory. Closes: https://github.com/containers/podman/issues/15765 Signed-off-by: Giuseppe Scrivano --- pkg/api/handlers/compat/containers_stats.go | 11 ++++++++++ test/apiv2/20-containers.at | 11 ++++++++++ test/apiv2/test-apiv2 | 24 ++++++++++++++++++++- 3 files changed, 45 insertions(+), 1 deletion(-) diff --git a/pkg/api/handlers/compat/containers_stats.go b/pkg/api/handlers/compat/containers_stats.go index 16311ef1e6..5196616754 100644 --- a/pkg/api/handlers/compat/containers_stats.go +++ b/pkg/api/handlers/compat/containers_stats.go @@ -11,6 +11,7 @@ import ( "github.com/containers/podman/v4/libpod/define" "github.com/containers/podman/v4/pkg/api/handlers/utils" api "github.com/containers/podman/v4/pkg/api/types" + "github.com/containers/storage/pkg/system" docker "github.com/docker/docker/api/types" "github.com/gorilla/schema" runccgroups "github.com/opencontainers/runc/libcontainer/cgroups" @@ -139,6 +140,16 @@ streamLabel: // A label to flatten the scope memoryLimit = uint64(*cfg.Spec.Linux.Resources.Memory.Limit) } + memInfo, err := system.ReadMemInfo() + if err != nil { + logrus.Errorf("Unable to get cgroup stats: %v", err) + return + } + // cap the memory limit to the available memory. + if memInfo.MemTotal > 0 && memoryLimit > uint64(memInfo.MemTotal) { + memoryLimit = uint64(memInfo.MemTotal) + } + systemUsage, _ := cgroups.GetSystemCPUUsage() s := StatsJSON{ Stats: Stats{ diff --git a/test/apiv2/20-containers.at b/test/apiv2/20-containers.at index e581b10d10..cc238e27e2 100644 --- a/test/apiv2/20-containers.at +++ b/test/apiv2/20-containers.at @@ -117,6 +117,17 @@ if root; then podman rm -f $CTRNAME fi +# Issue #15765: make sure the memory limit is capped +if root; then + CTRNAME=ctr-with-limit + podman run --name $CTRNAME -d -m 512m -v /tmp:/tmp $IMAGE top + + t GET libpod/containers/$CTRNAME/stats?stream=false 200 \ + .memory_stats.limit!=18446744073709552000 + + podman rm -f $CTRNAME +fi + # Issue #6799: it should be possible to start a container, even w/o args. t POST libpod/containers/create?name=test_noargs Image=${IMAGE} 201 \ .Id~[0-9a-f]\\{64\\} diff --git a/test/apiv2/test-apiv2 b/test/apiv2/test-apiv2 index aca7db0dde..b762cff9e7 100755 --- a/test/apiv2/test-apiv2 +++ b/test/apiv2/test-apiv2 @@ -107,6 +107,22 @@ function is() { _show_ok 0 "$testname" "$expect" "$actual" } +############ +# is_not # Simple disequality +############ +function is_not() { + local actual=$1 + local expect_not=$2 + local testname=$3 + + if [ "$actual" != "$expect_not" ]; then + # On success, include expected value; this helps readers understand + _show_ok 1 "$testname!=$expect" + return + fi + _show_ok 0 "$testname" "!= $expect" "$actual" +} + ########## # like # Compare, but allowing patterns ########## @@ -377,7 +393,13 @@ function t() { fi for i; do - if expr "$i" : "[^=~]\+=.*" >/dev/null; then + if expr "$i" : '[^\!]\+\!=.\+' >/dev/null; then + # Disequality on json field + json_field=$(expr "$i" : '\([^!]*\)!') + expect_not=$(expr "$i" : '[^\!]*\!=\(.*\)') + actual=$(jq -r "$json_field" <<<"$output") + is_not "$actual" "$expect_not" "$testname : $json_field" + elif expr "$i" : "[^=~]\+=.*" >/dev/null; then # Exact match on json field json_field=$(expr "$i" : "\([^=]*\)=") expect=$(expr "$i" : '[^=]*=\(.*\)')