// +build linux package libcontainer import ( "encoding/binary" "fmt" "io/ioutil" "os" "path/filepath" "syscall" "testing" "time" ) func TestNotifyOnOOM(t *testing.T) { memoryPath, err := ioutil.TempDir("", "testnotifyoom-") if err != nil { t.Fatal(err) } oomPath := filepath.Join(memoryPath, "memory.oom_control") eventPath := filepath.Join(memoryPath, "cgroup.event_control") if err := ioutil.WriteFile(oomPath, []byte{}, 0700); err != nil { t.Fatal(err) } if err := ioutil.WriteFile(eventPath, []byte{}, 0700); err != nil { t.Fatal(err) } var eventFd, oomControlFd int st := &State{ CgroupPaths: map[string]string{ "memory": memoryPath, }, } ooms, err := NotifyOnOOM(st) if err != nil { t.Fatal("expected no error, got:", err) } data, err := ioutil.ReadFile(eventPath) if err != nil { t.Fatal("couldn't read event control file:", err) } if _, err := fmt.Sscanf(string(data), "%d %d", &eventFd, &oomControlFd); err != nil { t.Fatalf("invalid control data %q: %s", data, err) } // re-open the eventfd efd, err := syscall.Dup(eventFd) if err != nil { t.Fatal("unable to reopen eventfd:", err) } defer syscall.Close(efd) if err != nil { t.Fatal("unable to dup event fd:", err) } buf := make([]byte, 8) binary.LittleEndian.PutUint64(buf, 1) if _, err := syscall.Write(efd, buf); err != nil { t.Fatal("unable to write to eventfd:", err) } select { case <-ooms: case <-time.After(100 * time.Millisecond): t.Fatal("no notification on oom channel after 100ms") } // simulate what happens when a cgroup is destroyed by cleaning up and then // writing to the eventfd. if err := os.RemoveAll(memoryPath); err != nil { t.Fatal(err) } if _, err := syscall.Write(efd, buf); err != nil { t.Fatal("unable to write to eventfd:", err) } // give things a moment to shut down select { case _, ok := <-ooms: if ok { t.Fatal("expected no oom to be triggered") } case <-time.After(100 * time.Millisecond): } if _, _, err := syscall.Syscall(syscall.SYS_FCNTL, uintptr(oomControlFd), syscall.F_GETFD, 0); err != syscall.EBADF { t.Error("expected oom control to be closed") } if _, _, err := syscall.Syscall(syscall.SYS_FCNTL, uintptr(eventFd), syscall.F_GETFD, 0); err != syscall.EBADF { t.Error("expected event fd to be closed") } }