-
Notifications
You must be signed in to change notification settings - Fork 21
v2: set memory.oom.group => OOMPolicy in systemd #41
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,8 +1,10 @@ | ||
| package systemd | ||
|
|
||
| import ( | ||
| "context" | ||
| "os" | ||
| "reflect" | ||
| "strconv" | ||
| "testing" | ||
|
|
||
| systemdDbus "github.com/coreos/go-systemd/v22/dbus" | ||
|
|
@@ -157,6 +159,12 @@ func TestUnifiedResToSystemdProps(t *testing.T) { | |
| newProp("CPUWeight", uint64(1000)), | ||
| }, | ||
| }, | ||
| { | ||
| name: "memory.oom.group handled by Apply method", | ||
| res: map[string]string{ | ||
| "memory.oom.group": "1", | ||
| }, | ||
| }, | ||
| } | ||
|
|
||
| for _, tc := range testCases { | ||
|
|
@@ -236,3 +244,98 @@ func TestAddCPUQuota(t *testing.T) { | |
| }) | ||
| } | ||
| } | ||
|
|
||
| func TestOOMPolicyApply(t *testing.T) { | ||
| if !IsRunningSystemd() { | ||
| t.Skip("Test requires systemd.") | ||
| } | ||
| if !cgroups.IsCgroup2UnifiedMode() { | ||
| t.Skip("cgroup v2 is required") | ||
| } | ||
| if os.Geteuid() != 0 { | ||
| t.Skip("Test requires root.") | ||
| } | ||
|
|
||
| testCases := []struct { | ||
| name string | ||
| oomGroupValue string | ||
| expectedPolicy string | ||
| expectError bool | ||
| }{ | ||
| { | ||
| name: "memory.oom.group=0 sets OOMPolicy=continue", | ||
| oomGroupValue: "0", | ||
| expectedPolicy: "continue", | ||
| expectError: false, | ||
| }, | ||
| { | ||
| name: "memory.oom.group=1 sets OOMPolicy=kill", | ||
| oomGroupValue: "1", | ||
| expectedPolicy: "kill", | ||
| expectError: false, | ||
| }, | ||
| { | ||
| name: "invalid memory.oom.group value", | ||
| oomGroupValue: "invalid", | ||
| expectError: true, | ||
| }, | ||
| } | ||
|
|
||
| for _, tc := range testCases { | ||
| t.Run(tc.name, func(t *testing.T) { | ||
| config := &cgroups.Cgroup{ | ||
| Name: "test-oom-policy-" + strconv.FormatInt(int64(os.Getpid()), 10), | ||
| Resources: &cgroups.Resources{ | ||
| Unified: map[string]string{ | ||
| "memory.oom.group": tc.oomGroupValue, | ||
| }, | ||
| }, | ||
| } | ||
|
|
||
| manager, err := NewUnifiedManager(config, "") | ||
| if err != nil { | ||
| t.Fatalf("Failed to create manager: %v", err) | ||
| } | ||
| defer func() { | ||
| _ = manager.Destroy() | ||
| }() | ||
|
|
||
| err = manager.Apply(-1) | ||
| if tc.expectError { | ||
| if err == nil { | ||
| t.Fatal("Expected error but got none") | ||
| } | ||
| return | ||
| } | ||
| if err != nil { | ||
| t.Fatalf("Unexpected error: %v", err) | ||
| } | ||
|
|
||
| unitName := getUnitName(config) | ||
| conn, err := systemdDbus.NewSystemdConnectionContext(context.Background()) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: you can probably use |
||
| if err != nil { | ||
| t.Fatalf("Failed to connect to systemd: %v", err) | ||
| } | ||
| defer conn.Close() | ||
|
|
||
| properties, err := conn.GetUnitPropertiesContext(context.Background(), unitName) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ditto |
||
| if err != nil { | ||
| t.Fatalf("Failed to get unit properties: %v", err) | ||
| } | ||
|
|
||
| oomPolicyValue, exists := properties["OOMPolicy"] | ||
| if !exists { | ||
| t.Fatal("OOMPolicy property not found") | ||
| } | ||
|
|
||
| oomPolicyStr, ok := oomPolicyValue.(string) | ||
| if !ok { | ||
| t.Fatalf("OOMPolicy value is not a string: %T", oomPolicyValue) | ||
| } | ||
|
|
||
| if oomPolicyStr != tc.expectedPolicy { | ||
| t.Errorf("Expected OOMPolicy=%s, got %s", tc.expectedPolicy, oomPolicyStr) | ||
| } | ||
| }) | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -180,13 +180,8 @@ func unifiedResToSystemdProps(cm *dbusConnManager, res map[string]string) (props | |
| newProp("TasksMax", num)) | ||
|
|
||
| case "memory.oom.group": | ||
| // Setting this to 1 is roughly equivalent to OOMPolicy=kill | ||
| // (as per systemd.service(5) and | ||
| // https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html), | ||
| // but it's not clear what to do if it is unset or set | ||
| // to 0 in runc update, as there are two other possible | ||
| // values for OOMPolicy (continue/stop). | ||
| fallthrough | ||
| // This was set before the unit started, so no need to | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Dumb question, do we know this to actually be true? i.e. we've made it so we also process I guess I don't have a better answer for these sort of "must be done on unit creation" type settings unless we can warn iff we know the systemd prop isn't aligned already.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Oh, I suppose what we should do is query the existing value, and warn/error if they mismatch? because you're right: if someone does a |
||
| // warn about it here. | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder if the warning should be louder (maybe just more explicitly stating systemd might override this on a daemon-reload) for other uses that hit it since systemd will stomp on it as you highlight, or is that only for a subset of things that systemd has a knob for? I realize that's a bit out of scope for this PR, but sort of a tricky thing for folks to debug down to.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. for |
||
|
|
||
| default: | ||
| // Ignore the unknown resource here -- will still be | ||
|
|
@@ -327,6 +322,24 @@ func (m *UnifiedManager) Apply(pid int) error { | |
|
|
||
| properties = append(properties, c.SystemdProps...) | ||
|
|
||
| if c.Resources != nil && c.Resources.Unified != nil { | ||
| if v, ok := c.Resources.Unified["memory.oom.group"]; ok { | ||
| value, err := strconv.ParseUint(v, 10, 64) | ||
| if err != nil { | ||
| return fmt.Errorf("unified resource %q value conversion error: %w", "memory.oom.group", err) | ||
| } | ||
|
|
||
| switch value { | ||
| case 0: | ||
| properties = append(properties, newProp("OOMPolicy", "continue")) | ||
| case 1: | ||
| properties = append(properties, newProp("OOMPolicy", "kill")) | ||
| default: | ||
| logrus.Debugf("don't know how to convert memory.oom.group=%d; skipping (will still be applied to cgroupfs)", value) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| if err := startUnit(m.dbus, unitName, properties, pid == -1); err != nil { | ||
| return fmt.Errorf("unable to start unit %q (properties %+v): %w", unitName, properties, err) | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: do you need to add a check for systemd version here, to skip the test for older versions?
Or, perhaps, get the unit properties, check if the
OOMPolicyis there (if it's always there on newer systemd, of course), otherwiset.Skip("no OOMPolicy property; older systemd?")