Support for setting systemd properties via annotations

In case systemd is used to set cgroups for the container,
it creates a scope unit dedicated to it (usually named
`runc-$ID.scope`).

This patch adds an ability to set arbitrary systemd properties
for the systemd unit via runtime spec annotations.

Initially this was developed as an ability to specify the
`TimeoutStopUSec` property, but later generalized to work with
arbitrary ones.

Example usage: add the following to runtime spec (config.json):

```
	"annotations": {
		"org.systemd.property.TimeoutStopUSec": "uint64 123456789",
		"org.systemd.property.CollectMode":"'inactive-or-failed'"
	},
```

and start the container (e.g. `runc --systemd-cgroup run $ID`).

The above will set the following systemd parameters:
* `TimeoutStopSec` to 2 minutes and 3 seconds,
* `CollectMode` to "inactive-or-failed".

The values are in the gvariant format (see [1]). To figure out
which type systemd expects for a particular parameter, see
systemd sources.

In particular, parameters with `USec` suffix require an `uint64`
typed argument, while gvariant assumes int32 for a numeric values,
therefore the explicit type is required.

NOTE that systemd receives the time-typed parameters as *USec
but shows them (in `systemctl show`) as *Sec. For example,
the stop timeout should be set as `TimeoutStopUSec` but
is shown as `TimeoutStopSec`.

[1] https://developer.gnome.org/glib/stable/gvariant-text.html

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
This commit is contained in:
Kir Kolyshkin
2020-02-06 20:26:06 -08:00
parent 81ef5024f8
commit 4c5c3fb960
5 changed files with 129 additions and 1 deletions
+80
View File
@@ -9,6 +9,7 @@ import (
"golang.org/x/sys/unix"
"github.com/godbus/dbus"
"github.com/opencontainers/runc/libcontainer/configs"
"github.com/opencontainers/runc/libcontainer/configs/validate"
"github.com/opencontainers/runtime-spec/specs-go"
@@ -450,3 +451,82 @@ func TestNonZeroEUIDCompatibleSpecconvValidate(t *testing.T) {
t.Errorf("Expected specconv to produce valid rootless container config: %v", err)
}
}
func TestInitSystemdProps(t *testing.T) {
type inT struct {
name, value string
}
type expT struct {
isErr bool
name string
value interface{}
}
testCases := []struct {
desc string
in inT
exp expT
}{
{
in: inT{"org.systemd.property.TimeoutStopUSec", "uint64 123456789"},
exp: expT{false, "TimeoutStopUSec", uint64(123456789)},
},
{
in: inT{"org.systemd.property.CollectMode", "'inactive-or-failed'"},
exp: expT{false, "CollectMode", "inactive-or-failed"},
},
{
desc: "unrelated property",
in: inT{"some.other.annotation", "0"},
exp: expT{false, "", ""},
},
{
desc: "too short property name",
in: inT{"org.systemd.property.Xo", "1"},
exp: expT{true, "", ""},
},
{
desc: "invalid character in property name",
in: inT{"org.systemd.property.Number1", "1"},
exp: expT{true, "", ""},
},
{
desc: "invalid property value",
in: inT{"org.systemd.property.ValidName", "invalid-value"},
exp: expT{true, "", ""},
},
}
spec := &specs.Spec{}
for _, tc := range testCases {
tc := tc
spec.Annotations = map[string]string{tc.in.name: tc.in.value}
outMap, err := initSystemdProps(spec)
//t.Logf("input %+v, expected %+v, got err:%v out:%+v", tc.in, tc.exp, err, outMap)
if tc.exp.isErr != (err != nil) {
t.Errorf("input %+v, expecting error: %v, got %v", tc.in, tc.exp.isErr, err)
}
expLen := 1 // expect a single item
if tc.exp.name == "" {
expLen = 0 // expect nothing
}
if len(outMap) != expLen {
t.Fatalf("input %+v, expected %d, got %d entries: %v", tc.in, expLen, len(outMap), outMap)
}
if expLen == 0 {
continue
}
out := outMap[0]
if tc.exp.name != out.Name {
t.Errorf("input %+v, expecting name: %q, got %q", tc.in, tc.exp.name, out.Name)
}
expValue := dbus.MakeVariant(tc.exp.value).String()
if expValue != out.Value.String() {
t.Errorf("input %+v, expecting value: %s, got %s", tc.in, expValue, out.Value)
}
}
}