...

Source file src/github.com/opencontainers/runc/libcontainer/notify_linux_test.go

Documentation: github.com/opencontainers/runc/libcontainer

     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  }
   118  

View as plain text