1 package libcontainer
2
3 import (
4 "encoding/binary"
5 "fmt"
6 "os"
7 "path/filepath"
8 "testing"
9 "time"
10
11 "golang.org/x/sys/unix"
12 )
13
14 type notifyFunc func(path string) (<-chan struct{}, error)
15
16 func testMemoryNotification(t *testing.T, evName string, notify notifyFunc, targ string) {
17 memoryPath := t.TempDir()
18 evFile := filepath.Join(memoryPath, evName)
19 eventPath := filepath.Join(memoryPath, "cgroup.event_control")
20 if err := os.WriteFile(evFile, []byte{}, 0o700); err != nil {
21 t.Fatal(err)
22 }
23 if err := os.WriteFile(eventPath, []byte{}, 0o700); err != nil {
24 t.Fatal(err)
25 }
26 ch, err := notify(memoryPath)
27 if err != nil {
28 t.Fatal("expected no error, got:", err)
29 }
30
31 data, err := os.ReadFile(eventPath)
32 if err != nil {
33 t.Fatal("couldn't read event control file:", err)
34 }
35
36 var eventFd, evFd int
37 var arg string
38 if targ != "" {
39 _, err = fmt.Sscanf(string(data), "%d %d %s", &eventFd, &evFd, &arg)
40 } else {
41 _, err = fmt.Sscanf(string(data), "%d %d", &eventFd, &evFd)
42 }
43 if err != nil || arg != targ {
44 t.Fatalf("invalid control data %q: %s", data, err)
45 }
46
47
48 efd, err := unix.Dup(eventFd)
49 if err != nil {
50 t.Fatal("unable to dup eventfd:", err)
51 }
52 defer unix.Close(efd)
53
54 buf := make([]byte, 8)
55 binary.LittleEndian.PutUint64(buf, 1)
56
57 if _, err := unix.Write(efd, buf); err != nil {
58 t.Fatal("unable to write to eventfd:", err)
59 }
60
61 select {
62 case <-ch:
63 case <-time.After(100 * time.Millisecond):
64 t.Fatal("no notification on channel after 100ms")
65 }
66
67
68
69 if err := os.RemoveAll(memoryPath); err != nil {
70 t.Fatal(err)
71 }
72 if _, err := unix.Write(efd, buf); err != nil {
73 t.Fatal("unable to write to eventfd:", err)
74 }
75
76
77 select {
78 case _, ok := <-ch:
79 if ok {
80 t.Fatal("expected no notification to be triggered")
81 }
82 case <-time.After(100 * time.Millisecond):
83 t.Fatal("channel not closed after 100ms")
84 }
85
86 if _, _, err := unix.Syscall(unix.SYS_FCNTL, uintptr(evFd), unix.F_GETFD, 0); err != unix.EBADF {
87 t.Errorf("expected event control to be closed, but received error %s", err.Error())
88 }
89
90 if _, _, err := unix.Syscall(unix.SYS_FCNTL, uintptr(eventFd), unix.F_GETFD, 0); err != unix.EBADF {
91 t.Errorf("expected event fd to be closed, but received error %s", err.Error())
92 }
93 }
94
95 func TestNotifyOnOOM(t *testing.T) {
96 f := func(path string) (<-chan struct{}, error) {
97 return notifyOnOOM(path)
98 }
99
100 testMemoryNotification(t, "memory.oom_control", f, "")
101 }
102
103 func TestNotifyMemoryPressure(t *testing.T) {
104 tests := map[PressureLevel]string{
105 LowPressure: "low",
106 MediumPressure: "medium",
107 CriticalPressure: "critical",
108 }
109
110 for level, arg := range tests {
111 f := func(path string) (<-chan struct{}, error) {
112 return notifyMemoryPressure(path, level)
113 }
114
115 testMemoryNotification(t, "memory.pressure_level", f, arg)
116 }
117 }
118
View as plain text