Skip to content

Commit a0ae1c2

Browse files
authored
Merge pull request #290 from mmerkes/unitexists
NewSystemd handles UnitExists when starting units
2 parents 8fa6424 + 9008873 commit a0ae1c2

2 files changed

Lines changed: 98 additions & 8 deletions

File tree

cgroup2/manager.go

Lines changed: 55 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -870,14 +870,7 @@ func NewSystemd(slice, group string, pid int, resources *Resources) (*Manager, e
870870
newSystemdProperty("TasksMax", uint64(resources.Pids.Max)))
871871
}
872872

873-
statusChan := make(chan string, 1)
874-
if _, err := conn.StartTransientUnitContext(ctx, group, "replace", properties, statusChan); err == nil {
875-
select {
876-
case <-statusChan:
877-
case <-time.After(time.Second):
878-
logrus.Warnf("Timed out while waiting for StartTransientUnit(%s) completion signal from dbus. Continuing...", group)
879-
}
880-
} else if !isUnitExists(err) {
873+
if err := startUnit(conn, group, properties, pid == -1); err != nil {
881874
return &Manager{}, err
882875
}
883876

@@ -886,6 +879,60 @@ func NewSystemd(slice, group string, pid int, resources *Resources) (*Manager, e
886879
}, nil
887880
}
888881

882+
func startUnit(conn *systemdDbus.Conn, group string, properties []systemdDbus.Property, ignoreExists bool) error {
883+
ctx := context.TODO()
884+
885+
statusChan := make(chan string, 1)
886+
defer close(statusChan)
887+
888+
retry := true
889+
started := false
890+
891+
for !started {
892+
if _, err := conn.StartTransientUnitContext(ctx, group, "replace", properties, statusChan); err != nil {
893+
if !isUnitExists(err) {
894+
return err
895+
}
896+
897+
if ignoreExists {
898+
return nil
899+
}
900+
901+
if retry {
902+
retry = false
903+
// When a unit of the same name already exists, it may be a leftover failed unit.
904+
// If we reset it once, systemd can try to remove it.
905+
attemptFailedUnitReset(conn, group)
906+
continue
907+
}
908+
909+
return err
910+
} else {
911+
started = true
912+
}
913+
}
914+
915+
select {
916+
case s := <-statusChan:
917+
if s != "done" {
918+
attemptFailedUnitReset(conn, group)
919+
return fmt.Errorf("error creating systemd unit `%s`: got `%s`", group, s)
920+
}
921+
case <-time.After(30 * time.Second):
922+
logrus.Warnf("Timed out while waiting for StartTransientUnit(%s) completion signal from dbus. Continuing...", group)
923+
}
924+
925+
return nil
926+
}
927+
928+
func attemptFailedUnitReset(conn *systemdDbus.Conn, group string) {
929+
err := conn.ResetFailedUnitContext(context.TODO(), group)
930+
931+
if err != nil {
932+
logrus.Warnf("Unable to reset failed unit: %v", err)
933+
}
934+
}
935+
889936
func LoadSystemd(slice, group string) (*Manager, error) {
890937
if slice == "" {
891938
slice = defaultSlice

cgroup2/manager_test.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,49 @@ import (
3030
"go.uber.org/goleak"
3131
)
3232

33+
func setupForNewSystemd(t *testing.T) (cmd *exec.Cmd, group string) {
34+
cmd = exec.Command("cat")
35+
err := cmd.Start()
36+
require.NoError(t, err, "failed to start cat process")
37+
proc := cmd.Process
38+
require.NotNil(t, proc, "process was nil")
39+
40+
group = fmt.Sprintf("testing-watcher-%d.scope", proc.Pid)
41+
42+
return
43+
}
44+
45+
func TestErrorsWhenUnitAlreadyExists(t *testing.T) {
46+
checkCgroupMode(t)
47+
48+
cmd, group := setupForNewSystemd(t)
49+
proc := cmd.Process
50+
51+
_, err := NewSystemd("", group, proc.Pid, &Resources{})
52+
require.NoError(t, err, "Failed to init new cgroup manager")
53+
54+
_, err = NewSystemd("", group, proc.Pid, &Resources{})
55+
if err == nil {
56+
t.Fatal("Expected recreating cgroup manager should fail")
57+
} else if !isUnitExists(err) {
58+
t.Fatalf("Failed to init cgroup manager with unexpected error: %s", err)
59+
}
60+
}
61+
62+
// kubelet relies on this behavior to make sure a slice exists
63+
func TestIgnoreUnitExistsWhenPidNegativeOne(t *testing.T) {
64+
checkCgroupMode(t)
65+
66+
cmd, group := setupForNewSystemd(t)
67+
proc := cmd.Process
68+
69+
_, err := NewSystemd("", group, proc.Pid, &Resources{})
70+
require.NoError(t, err, "Failed to init new cgroup manager")
71+
72+
_, err = NewSystemd("", group, -1, &Resources{})
73+
require.NoError(t, err, "Expected to be able to recreate cgroup manager")
74+
}
75+
3376
//nolint:staticcheck // Staticcheck false positives for nil pointer deference after t.Fatal
3477
func TestEventChanCleanupOnCgroupRemoval(t *testing.T) {
3578
checkCgroupMode(t)

0 commit comments

Comments
 (0)