1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
|
package fs2
import (
"os"
"path/filepath"
"strings"
"testing"
"github.com/opencontainers/cgroups"
)
const exampleMemoryStatData = `anon 790425600
file 6502666240
kernel_stack 7012352
pagetables 8867840
percpu 2445520
sock 40960
shmem 6721536
file_mapped 656187392
file_dirty 1122304
file_writeback 0
swapcached 10
anon_thp 438304768
file_thp 0
shmem_thp 0
inactive_anon 892223488
active_anon 2973696
inactive_file 5307346944
active_file 1179316224
unevictable 31477760
slab_reclaimable 348866240
slab_unreclaimable 10099808
slab 358966048
workingset_refault_anon 0
workingset_refault_file 0
workingset_activate_anon 0
workingset_activate_file 0
workingset_restore_anon 0
workingset_restore_file 0
workingset_nodereclaim 0
pgfault 103216687
pgmajfault 6879
pgrefill 0
pgscan 0
pgsteal 0
pgactivate 1110217
pgdeactivate 292
pglazyfree 267
pglazyfreed 0
thp_fault_alloc 57411
thp_collapse_alloc 443`
func TestStatMemoryPodCgroupNotFound(t *testing.T) {
// We're using a fake cgroupfs.
cgroups.TestMode = true
fakeCgroupDir := t.TempDir()
// only write memory.stat to ensure pod cgroup usage
// still reads memory.current.
statPath := filepath.Join(fakeCgroupDir, "memory.stat")
if err := os.WriteFile(statPath, []byte(exampleMemoryStatData), 0o644); err != nil {
t.Fatal(err)
}
gotStats := cgroups.NewStats()
// use a fake root path to mismatch the file we wrote.
// this triggers the non-root path which should fail to find memory.current.
err := statMemory(fakeCgroupDir, gotStats)
if err == nil {
t.Errorf("expected error when statting memory for cgroupv2 root, but was nil")
}
if !strings.Contains(err.Error(), "memory.current: no such file or directory") {
t.Errorf("expected error to contain 'memory.current: no such file or directory', but was %s", err.Error())
}
}
func TestStatMemoryPodCgroup(t *testing.T) {
// We're using a fake cgroupfs.
cgroups.TestMode = true
fakeCgroupDir := t.TempDir()
statPath := filepath.Join(fakeCgroupDir, "memory.stat")
if err := os.WriteFile(statPath, []byte(exampleMemoryStatData), 0o644); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(fakeCgroupDir, "memory.current"), []byte("123456789"), 0o644); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(fakeCgroupDir, "memory.max"), []byte("999999999"), 0o644); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(fakeCgroupDir, "memory.peak"), []byte("987654321"), 0o644); err != nil {
t.Fatal(err)
}
gotStats := cgroups.NewStats()
// use a fake root path to trigger the pod cgroup lookup.
err := statMemory(fakeCgroupDir, gotStats)
if err != nil {
t.Errorf("expected no error when statting memory for cgroupv2 root, but got %#+v", err)
}
// result should be "memory.current"
var expectedUsageBytes uint64 = 123456789
if gotStats.MemoryStats.Usage.Usage != expectedUsageBytes {
t.Errorf("parsed cgroupv2 memory.stat doesn't match expected result: \ngot %#v\nexpected %#v\n", gotStats.MemoryStats.Usage.Usage, expectedUsageBytes)
}
// result should be "memory.max"
var expectedLimitBytes uint64 = 999999999
if gotStats.MemoryStats.Usage.Limit != expectedLimitBytes {
t.Errorf("parsed cgroupv2 memory.stat doesn't match expected result: \ngot %#v\nexpected %#v\n", gotStats.MemoryStats.Usage.Limit, expectedLimitBytes)
}
// result should be "memory.peak"
var expectedMaxUsageBytes uint64 = 987654321
if gotStats.MemoryStats.Usage.MaxUsage != expectedMaxUsageBytes {
t.Errorf("parsed cgroupv2 memory.stat doesn't match expected result: \ngot %#v\nexpected %#v\n", gotStats.MemoryStats.Usage.MaxUsage, expectedMaxUsageBytes)
}
}
func TestRootStatsFromMeminfo(t *testing.T) {
stats := &cgroups.Stats{
MemoryStats: cgroups.MemoryStats{
Stats: map[string]uint64{
"anon": 790425600,
"file": 6502666240,
},
},
}
if err := rootStatsFromMeminfo(stats); err != nil {
t.Fatal(err)
}
// result is anon + file
var expectedUsageBytes uint64 = 7293091840
if stats.MemoryStats.Usage.Usage != expectedUsageBytes {
t.Errorf("parsed cgroupv2 memory.stat doesn't match expected result: \ngot %d\nexpected %d\n", stats.MemoryStats.Usage.Usage, expectedUsageBytes)
}
// swap is adjusted to mem+swap
if stats.MemoryStats.SwapUsage.Usage < stats.MemoryStats.Usage.Usage {
t.Errorf("swap usage %d should be at least mem usage %d", stats.MemoryStats.SwapUsage.Usage, stats.MemoryStats.Usage.Usage)
}
if stats.MemoryStats.SwapUsage.Limit < stats.MemoryStats.Usage.Limit {
t.Errorf("swap limit %d should be at least mem limit %d", stats.MemoryStats.SwapUsage.Limit, stats.MemoryStats.Usage.Limit)
}
}
|