forked from docker-archive/runc
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
libct/cg/sd/v1: add freezeBeforeSet unit test
Add a test for freezeBeforeSet, checking various scenarios including those that were failing before the fix in the previous commit. [v2: add more cases, add a check before creating a unit.] Signed-off-by: Kir Kolyshkin <[email protected]> (cherry picked from commit fec49f2) Signed-off-by: Kir Kolyshkin <[email protected]>
- Loading branch information
Showing
1 changed file
with
217 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,217 @@ | ||
package systemd | ||
|
||
import ( | ||
"os" | ||
"os/exec" | ||
"strings" | ||
"testing" | ||
|
||
"golang.org/x/sys/unix" | ||
|
||
"github.com/opencontainers/runc/libcontainer/cgroups" | ||
"github.com/opencontainers/runc/libcontainer/configs" | ||
) | ||
|
||
func TestFreezeBeforeSet(t *testing.T) { | ||
requireV1(t) | ||
|
||
testCases := []struct { | ||
desc string | ||
// Test input. | ||
cg *configs.Cgroup | ||
preFreeze bool | ||
// Expected values. | ||
// Before unit creation (Apply). | ||
freeze0, thaw0 bool | ||
// After unit creation. | ||
freeze1, thaw1 bool | ||
}{ | ||
{ | ||
// A slice with SkipDevices. | ||
desc: "slice,skip-devices", | ||
cg: &configs.Cgroup{ | ||
Name: "system-runc_test_freeze_1.slice", | ||
Parent: "system.slice", | ||
Resources: &configs.Resources{ | ||
SkipDevices: true, | ||
}, | ||
}, | ||
// Expected. | ||
freeze0: false, | ||
thaw0: false, | ||
freeze1: false, | ||
thaw1: false, | ||
}, | ||
{ | ||
// A scope with SkipDevices. Not a realistic scenario with runc | ||
// (as container can't have SkipDevices == true), but possible | ||
// for a standalone cgroup manager. | ||
desc: "scope,skip-devices", | ||
cg: &configs.Cgroup{ | ||
ScopePrefix: "test", | ||
Name: "testFreeze2", | ||
Parent: "system.slice", | ||
Resources: &configs.Resources{ | ||
SkipDevices: true, | ||
}, | ||
}, | ||
// Expected. | ||
freeze0: false, | ||
thaw0: false, | ||
freeze1: false, | ||
thaw1: false, | ||
}, | ||
{ | ||
// A slice that is about to be frozen in Set. | ||
desc: "slice,will-freeze", | ||
cg: &configs.Cgroup{ | ||
Name: "system-runc_test_freeze_3.slice", | ||
Parent: "system.slice", | ||
Resources: &configs.Resources{ | ||
Freezer: configs.Frozen, | ||
}, | ||
}, | ||
// Expected. | ||
freeze0: true, | ||
thaw0: false, | ||
freeze1: true, | ||
thaw1: false, | ||
}, | ||
{ | ||
// A pre-frozen slice that should stay frozen. | ||
desc: "slice,pre-frozen,will-freeze", | ||
cg: &configs.Cgroup{ | ||
Name: "system-runc_test_freeze_4.slice", | ||
Parent: "system.slice", | ||
Resources: &configs.Resources{ | ||
Freezer: configs.Frozen, | ||
}, | ||
}, | ||
preFreeze: true, | ||
// Expected. | ||
freeze0: true, // not actually frozen yet. | ||
thaw0: false, | ||
freeze1: false, | ||
thaw1: false, | ||
}, | ||
{ | ||
// A pre-frozen scope with skip devices set. | ||
desc: "scope,pre-frozen,skip-devices", | ||
cg: &configs.Cgroup{ | ||
ScopePrefix: "test", | ||
Name: "testFreeze5", | ||
Parent: "system.slice", | ||
Resources: &configs.Resources{ | ||
SkipDevices: true, | ||
}, | ||
}, | ||
preFreeze: true, | ||
// Expected. | ||
freeze0: false, | ||
thaw0: false, | ||
freeze1: false, | ||
thaw1: false, | ||
}, | ||
{ | ||
// A pre-frozen scope which will be thawed. | ||
desc: "scope,pre-frozen", | ||
cg: &configs.Cgroup{ | ||
ScopePrefix: "test", | ||
Name: "testFreeze6", | ||
Parent: "system.slice", | ||
Resources: &configs.Resources{}, | ||
}, | ||
preFreeze: true, | ||
// Expected. | ||
freeze0: true, // not actually frozen yet. | ||
thaw0: true, | ||
freeze1: false, | ||
thaw1: false, | ||
}, | ||
} | ||
|
||
for _, tc := range testCases { | ||
tc := tc | ||
t.Run(tc.desc, func(t *testing.T) { | ||
m := NewLegacyManager(tc.cg, nil) | ||
defer m.Destroy() //nolint:errcheck | ||
lm := m.(*legacyManager) | ||
|
||
// Checks for a non-existent unit. | ||
freeze, thaw, err := lm.freezeBeforeSet(getUnitName(tc.cg), tc.cg.Resources) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
if freeze != tc.freeze0 || thaw != tc.thaw0 { | ||
t.Errorf("before Apply (non-existent unit): expected freeze: %v, thaw: %v, got freeze: %v, thaw: %v", | ||
tc.freeze0, tc.thaw0, freeze, thaw) | ||
} | ||
|
||
// Create systemd unit. | ||
pid := -1 | ||
if strings.HasSuffix(getUnitName(tc.cg), ".scope") { | ||
// Scopes require a process inside. | ||
cmd := exec.Command("bash", "-c", "sleep 1m") | ||
if err := cmd.Start(); err != nil { | ||
t.Fatal(err) | ||
} | ||
pid = cmd.Process.Pid | ||
// Make sure to not leave a zombie. | ||
defer func() { | ||
// These may fail, we don't care. | ||
_ = cmd.Process.Kill() | ||
_ = cmd.Wait() | ||
}() | ||
} | ||
if err := m.Apply(pid); err != nil { | ||
t.Fatal(err) | ||
} | ||
if tc.preFreeze { | ||
if err := m.Freeze(configs.Frozen); err != nil { | ||
t.Error(err) | ||
return // no more checks | ||
} | ||
} | ||
freeze, thaw, err = lm.freezeBeforeSet(getUnitName(tc.cg), tc.cg.Resources) | ||
if err != nil { | ||
t.Error(err) | ||
return // no more checks | ||
} | ||
if freeze != tc.freeze1 || thaw != tc.thaw1 { | ||
t.Errorf("expected freeze: %v, thaw: %v, got freeze: %v, thaw: %v", | ||
tc.freeze1, tc.thaw1, freeze, thaw) | ||
} | ||
// Destroy() timeouts on a frozen container, so we need to thaw it. | ||
if tc.preFreeze { | ||
if err := m.Freeze(configs.Thawed); err != nil { | ||
t.Error(err) | ||
} | ||
} | ||
// Destroy() does not kill processes in cgroup, so we should. | ||
if pid != -1 { | ||
if err = unix.Kill(pid, unix.SIGKILL); err != nil { | ||
t.Errorf("unable to kill pid %d: %s", pid, err) | ||
} | ||
} | ||
// Not really needed, but may help catch some bugs. | ||
if err := m.Destroy(); err != nil { | ||
t.Errorf("destroy: %s", err) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
// requireV1 skips the test unless a set of requirements (cgroup v1, | ||
// systemd, root) is met. | ||
func requireV1(t *testing.T) { | ||
t.Helper() | ||
if cgroups.IsCgroup2UnifiedMode() { | ||
t.Skip("Test requires cgroup v1.") | ||
} | ||
if !IsRunningSystemd() { | ||
t.Skip("Test requires systemd.") | ||
} | ||
if os.Geteuid() != 0 { | ||
t.Skip("Test requires root.") | ||
} | ||
} |