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