This repository has no description
1//go:build linux
2
3package microvm
4
5import (
6 "log/slog"
7 "os"
8 "testing"
9
10 cgroups "github.com/containerd/cgroups/v3"
11)
12
13func TestSanitizeCgroupName(t *testing.T) {
14 cases := []struct {
15 in string
16 want string
17 }{
18 {"workflow-abc123", "workflow-abc123"},
19 {"a/b:c", "a-b-c"},
20 {"--lead--", "lead"},
21 {"a__b..c", "a-b-c"},
22 {"keep.dots_and-dashes", "keep.dots_and-dashes"},
23 {"", ""},
24 {"///", ""},
25 }
26 for _, tc := range cases {
27 if got := sanitizeCgroupName(tc.in); got != tc.want {
28 t.Errorf("sanitizeCgroupName(%q) = %q, want %q", tc.in, got, tc.want)
29 }
30 }
31}
32
33// regression test for the cgroup-namespace-root case: at a populated
34// namespace root the engine must vacate the parent before enabling
35// subtree controllers.
36//
37// run with:
38//
39// go test -c -o microvm.test ./spindle/engines/microvm/
40// podman run --rm --cap-add SYS_ADMIN --security-opt seccomp=unconfined \
41// -v $PWD/microvm.test:/t:Z -e SPINDLE_CGROUP_INTEGRATION=1 \
42// --entrypoint /bin/sh docker.io/library/golang:1.25 \
43// -c "mount -t cgroup2 cgroup2 /sys/fs/cgroup && exec /t -test.run TestCgroupParentVacatesPopulatedNamespaceRoot"
44func TestCgroupParentVacatesPopulatedNamespaceRoot(t *testing.T) {
45 if os.Getenv("SPINDLE_CGROUP_INTEGRATION") != "1" {
46 t.Skip("see test doc comment on how to run")
47 }
48 if cgroups.Mode() != cgroups.Unified {
49 t.Skip("requires cgroup v2 unified mode")
50 }
51
52 group, err := selfCgroupV2Path()
53 if err != nil {
54 t.Fatal(err)
55 }
56 if group != "/" {
57 t.Skipf("only meaningful at a cgroup namespace root, self cgroup is %q", group)
58 }
59
60 logger := slog.Default()
61 parent, err := initCgroupParent(cgroupParentSelf, 0, logger)
62 if err != nil {
63 t.Fatalf("initCgroupParent at a cgroup namespace root: %v", err)
64 }
65
66 procs, err := parent.root.Procs(false)
67 if err != nil {
68 t.Fatalf("list parent cgroup processes: %v", err)
69 }
70 if len(procs) != 0 {
71 t.Errorf("namespace root still holds %d processes after initCgroupParent; "+
72 "enabling subtree controllers for microVM cgroups would fail EBUSY", len(procs))
73 }
74
75 handle, err := prepareCgroup(CgroupLimits{
76 Enabled: true,
77 Parent: parent,
78 Name: "cgtest-nsroot",
79 MemoryMaxMiB: 64,
80 PidsMax: 256,
81 }, logger)
82 if err != nil {
83 t.Fatalf("create controller-enabled child at namespace root: %v", err)
84 }
85 t.Cleanup(func() { _ = handle.Close() })
86}
87
88func TestCgroupResourcesSwapOnlyStillSetsMemory(t *testing.T) {
89 swap := int64(8)
90 r := cgroupResources(CgroupLimits{SwapMaxMiB: &swap})
91 if r.Memory == nil {
92 t.Fatal("a swap limit alone should still produce a memory controller config")
93 }
94 if r.Memory.Max != nil {
95 t.Errorf("memory max should be unset when only swap is limited, got %v", *r.Memory.Max)
96 }
97 if r.Memory.Swap == nil || *r.Memory.Swap != 8*1024*1024 {
98 t.Errorf("swap = %v, want %d bytes", r.Memory.Swap, 8*1024*1024)
99 }
100}