github.com/opencontainers/runc@v1.2.0-rc.1.0.20240520010911-492dc558cdd6/libcontainer/notify_linux_test.go (about) 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 // dup the eventfd 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 // simulate what happens when a cgroup is destroyed by cleaning up and then 68 // writing to the eventfd. 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 // give things a moment to shut down 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 }