Skip to content

Commit ebc0fb9

Browse files
committed
fix: nerdctl stats on a container without a memory limit returns host memory limit
Signed-off-by: Cezar Rata <ceradev@amazon.com> Signed-off-by: Cezar Rata <ceradev@amazon.com> Signed-off-by: Cezar Rata <ceradev@amazon.com> Signed-off-by: Cezar Rata <ceradev@amazon.com>
1 parent da6b454 commit ebc0fb9

File tree

2 files changed

+64
-4
lines changed

2 files changed

+64
-4
lines changed

cmd/nerdctl/container_stats_linux_test.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,31 @@ func TestStats(t *testing.T) {
4545
base.Cmd("container", "stats", "--no-stream").AssertOutContains(testContainerName)
4646
base.Cmd("container", "stats", "--no-stream", testContainerName).AssertOK()
4747
}
48+
49+
func TestStatsMemoryLimitNotSet(t *testing.T) {
50+
if rootlessutil.IsRootless() && infoutil.CgroupsVersion() == "1" {
51+
t.Skip("test skipped for rootless containers on cgroup v1")
52+
}
53+
testContainerName := testutil.Identifier(t)[:12]
54+
55+
base := testutil.NewBase(t)
56+
defer base.Cmd("rm", "-f", testContainerName).Run()
57+
58+
base.Cmd("run", "-d", "--name", testContainerName, testutil.AlpineImage, "sleep", "10").AssertOK()
59+
base.Cmd("stats", "--no-stream").AssertOutNotContains("16EiB")
60+
base.Cmd("stats", "--no-stream", testContainerName).AssertOK()
61+
}
62+
63+
func TestStatsMemoryLimitSet(t *testing.T) {
64+
if rootlessutil.IsRootless() && infoutil.CgroupsVersion() == "1" {
65+
t.Skip("test skipped for rootless containers on cgroup v1")
66+
}
67+
testContainerName := testutil.Identifier(t)[:12]
68+
69+
base := testutil.NewBase(t)
70+
defer base.Cmd("rm", "-f", testContainerName).Run()
71+
72+
base.Cmd("run", "-d", "--name", testContainerName, "--memory", "1g", testutil.AlpineImage, "sleep", "10").AssertOK()
73+
base.Cmd("stats", "--no-stream").AssertOutContains("1GiB")
74+
base.Cmd("stats", "--no-stream", testContainerName).AssertOK()
75+
}

pkg/statsutil/stats_linux.go

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@
1717
package statsutil
1818

1919
import (
20+
"bufio"
21+
"os"
22+
"strconv"
23+
"strings"
2024
"time"
2125

2226
"github.com/vishvananda/netlink"
@@ -26,11 +30,10 @@ import (
2630
)
2731

2832
func SetCgroupStatsFields(previousStats *ContainerStats, data *v1.Metrics, links []netlink.Link) (StatsEntry, error) {
29-
3033
cpuPercent := calculateCgroupCPUPercent(previousStats, data)
3134
blkRead, blkWrite := calculateCgroupBlockIO(data)
3235
mem := calculateCgroupMemUsage(data)
33-
memLimit := float64(data.Memory.Usage.Limit)
36+
memLimit := getCgroupMemLimit(float64(data.Memory.Usage.Limit))
3437
memPercent := calculateMemPercent(memLimit, mem)
3538
pidsStatsCurrent := data.Pids.Current
3639
netRx, netTx := calculateCgroupNetwork(links)
@@ -50,11 +53,10 @@ func SetCgroupStatsFields(previousStats *ContainerStats, data *v1.Metrics, links
5053
}
5154

5255
func SetCgroup2StatsFields(previousStats *ContainerStats, metrics *v2.Metrics, links []netlink.Link) (StatsEntry, error) {
53-
5456
cpuPercent := calculateCgroup2CPUPercent(previousStats, metrics)
5557
blkRead, blkWrite := calculateCgroup2IO(metrics)
5658
mem := calculateCgroup2MemUsage(metrics)
57-
memLimit := float64(metrics.Memory.UsageLimit)
59+
memLimit := getCgroupMemLimit(float64(metrics.Memory.UsageLimit))
5860
memPercent := calculateMemPercent(memLimit, mem)
5961
pidsStatsCurrent := metrics.Pids.Current
6062
netRx, netTx := calculateCgroupNetwork(links)
@@ -73,6 +75,36 @@ func SetCgroup2StatsFields(previousStats *ContainerStats, metrics *v2.Metrics, l
7375

7476
}
7577

78+
func getCgroupMemLimit(memLimit float64) float64 {
79+
if memLimit == float64(^uint64(0)) {
80+
return getHostMemLimit()
81+
}
82+
return memLimit
83+
}
84+
85+
func getHostMemLimit() float64 {
86+
file, err := os.Open("/proc/meminfo")
87+
if err != nil {
88+
return float64(^uint64(0))
89+
}
90+
defer file.Close()
91+
92+
scanner := bufio.NewScanner(file)
93+
for scanner.Scan() {
94+
if strings.HasPrefix(scanner.Text(), "MemTotal:") {
95+
fields := strings.Fields(scanner.Text())
96+
if len(fields) >= 2 {
97+
memKb, err := strconv.ParseUint(fields[1], 10, 64)
98+
if err == nil {
99+
return float64(memKb * 1024) // kB to bytes
100+
}
101+
}
102+
break
103+
}
104+
}
105+
return float64(^uint64(0))
106+
}
107+
76108
func calculateCgroupCPUPercent(previousStats *ContainerStats, metrics *v1.Metrics) float64 {
77109
var (
78110
cpuPercent = 0.0

0 commit comments

Comments
 (0)