carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

1package cgroup
2
3import (
4 "errors"
5 "os"
6 "os/exec"
7 "path/filepath"
8 "strconv"
9 "testing"
10)
11
12// writeProcs makes dir a fixture cgroup listing pids.
13func writeProcs(t *testing.T, dir string, pids ...int) {
14 t.Helper()
15 if err := os.MkdirAll(dir, 0o755); err != nil {
16 t.Fatal(err)
17 }
18 body := ""
19 for _, p := range pids {
20 body += strconv.Itoa(p) + "\n"
21 }
22 if err := os.WriteFile(filepath.Join(dir, "cgroup.procs"), []byte(body), 0o644); err != nil {
23 t.Fatal(err)
24 }
25}
26
27// HostProcs over a fixture cgroupfs: the exec group and every group below
28// it count, and so does the unit's group; absent groups count 0; another
29// host's group never counts.
30func TestHostProcsCountsTheHostsGroupsOnly(t *testing.T) {
31 root := t.TempDir()
32 const host = "shop.example.com"
33 if n, err := HostProcs(root, host, ""); err != nil || n != 0 {
34 t.Fatalf("no group: (%d, %v), want (0, nil)", n, err)
35 }
36 writeProcs(t, filepath.Join(root, ExecName(host)))
37 if n, err := HostProcs(root, host, ""); err != nil || n != 0 {
38 t.Fatalf("an empty group: (%d, %v), want (0, nil)", n, err)
39 }
40 writeProcs(t, filepath.Join(root, ExecName(host)), 101)
41 writeProcs(t, filepath.Join(root, ExecName(host), "sidecar"), 102, 103)
42 writeProcs(t, filepath.Join(root, ExecName("other.example.com")), 201, 202, 203)
43 if n, err := HostProcs(root, host, ""); err != nil || n != 3 {
44 t.Fatalf("exec group and child: (%d, %v), want (3, nil)", n, err)
45 }
46 slice := filepath.Join(t.TempDir(), "carlos.slice", "carlos-instances.slice", "carlos-instances-shop.slice")
47 if n, err := HostProcs(root, host, slice); err != nil || n != 3 {
48 t.Fatalf("an absent unit group: (%d, %v), want (3, nil)", n, err)
49 }
50 writeProcs(t, filepath.Join(slice, "carlos-instance@shop.service"), 301)
51 if n, err := HostProcs(root, host, slice); err != nil || n != 4 {
52 t.Fatalf("with the unit's group: (%d, %v), want (4, nil)", n, err)
53 }
54 if _, err := HostProcs(root, host, "relative/slice"); err == nil {
55 t.Fatal("a relative unit path was read")
56 }
57 for _, bad := range []string{"", "..", "a/b"} {
58 if _, err := HostProcs(root, bad, ""); err == nil {
59 t.Fatalf("host %q was read", bad)
60 }
61 }
62}
63
64// Without a delegated subtree the exec children cannot be told apart from
65// the agent's own processes: unknown, never 0.
66func TestHostProcsWithoutDelegationIsUnknown(t *testing.T) {
67 if _, err := HostProcs("", "shop.example.com", ""); !errors.Is(err, ErrUnknown) {
68 t.Fatalf("no root: %v, want ErrUnknown", err)
69 }
70}
71
72// A real process in a real cgroup, where the kernel lets this test make one:
73// a child group of the test's own cgroup, with a sleep moved into it. Where
74// the cgroup is not writable (the usual case outside a delegated unit) the
75// fixture test above is the witness.
76func TestHostProcsSeesARealProcess(t *testing.T) {
77 b, err := os.ReadFile("/proc/self/cgroup")
78 if err != nil {
79 t.Skipf("no /proc/self/cgroup: %v", err)
80 }
81 rel, ok := ParseSelfCgroup(string(b))
82 if !ok {
83 t.Skip("no cgroup v2 path")
84 }
85 parent := filepath.Join("/sys/fs/cgroup", rel)
86 root := filepath.Join(parent, "hostprocs-test-"+strconv.Itoa(os.Getpid()))
87 const host = "shop.example.com"
88 group := filepath.Join(root, ExecName(host))
89 if err := os.MkdirAll(group, 0o755); err != nil {
90 t.Skipf("this cgroup is not writable: %v", err)
91 }
92 t.Cleanup(func() {
93 _ = os.Remove(group)
94 _ = os.Remove(root)
95 })
96 cmd := exec.Command("sleep", "30")
97 if err := cmd.Start(); err != nil {
98 t.Skipf("no sleep: %v", err)
99 }
100 t.Cleanup(func() {
101 _ = cmd.Process.Kill()
102 _ = cmd.Wait()
103 })
104 if err := os.WriteFile(filepath.Join(group, "cgroup.procs"), []byte(strconv.Itoa(cmd.Process.Pid)), 0o644); err != nil {
105 t.Skipf("the kernel refused the move: %v", err)
106 }
107 if n, err := HostProcs(root, host, ""); err != nil || n != 1 {
108 t.Fatalf("a sleep in the host's group: (%d, %v), want (1, nil)", n, err)
109 }
110 _ = cmd.Process.Kill()
111 _ = cmd.Wait()
112 if n, err := HostProcs(root, host, ""); err != nil || n != 0 {
113 t.Fatalf("after the sleep exited: (%d, %v), want (0, nil)", n, err)
114 }
115}
116