diff --git a/stages/org.osbuild.systemd.unit.create b/stages/org.osbuild.systemd.unit.create index 625f62b8..74f6e295 100755 --- a/stages/org.osbuild.systemd.unit.create +++ b/stages/org.osbuild.systemd.unit.create @@ -19,6 +19,8 @@ def validate(filename, cfg): raise ValueError(f"Error: {filename} unit requires Mount section") if filename.endswith(".socket") and "Socket" not in cfg: raise ValueError(f"Error: {filename} unit requires Socket section") + if filename.endswith(".swap") and "Swap" not in cfg: + raise ValueError(f"Error: {filename} unit requires Swap section") def main(tree, options): diff --git a/stages/org.osbuild.systemd.unit.create.meta.json b/stages/org.osbuild.systemd.unit.create.meta.json index f8cc3c58..5acc979f 100644 --- a/stages/org.osbuild.systemd.unit.create.meta.json +++ b/stages/org.osbuild.systemd.unit.create.meta.json @@ -46,6 +46,11 @@ " - 'Service' - string", " - 'RuntimeDirectory' - string", " - 'RemoveOnStop' - bool", + " - 'Swap' section", + " - 'What' - string", + " - 'Priority' - integer", + " - 'Options' - string", + " - 'TimeoutSec' - string", " - 'Install' section", " - 'WantedBy' - [string]", " - 'RequiredBy' - [string]" @@ -59,7 +64,7 @@ "properties": { "filename": { "type": "string", - "pattern": "^[\\w:.\\\\-]+[@]{0,1}[\\w:.\\\\-]*\\.(service|mount|socket)$" + "pattern": "^[\\w:.\\\\-]+[@]{0,1}[\\w:.\\\\-]*\\.(service|mount|socket|swap)$" }, "unit-type": { "type": "string", @@ -100,6 +105,11 @@ "required": [ "Socket" ] + }, + { + "required": [ + "Swap" + ] } ] } @@ -119,6 +129,11 @@ "required": [ "Service" ] + }, + { + "required": [ + "Swap" + ] } ] } @@ -138,6 +153,35 @@ "required": [ "Socket" ] + }, + { + "required": [ + "Swap" + ] + } + ] + } + }, + { + "required": [ + "Swap" + ], + "not": { + "anyOf": [ + { + "required": [ + "Service" + ] + }, + { + "required": [ + "Socket" + ] + }, + { + "required": [ + "Mount" + ] } ] } @@ -342,6 +386,32 @@ } } }, + "Swap": { + "additionalProperties": false, + "type": "object", + "description": "'Swap' configuration section of a unit file", + "required": [ + "What" + ], + "properties": { + "What": { + "description": "Absolute path to device node", + "type": "string" + }, + "Priority": { + "descriptions": "Swap priority to use when activating the swap device or file", + "type": "integer" + }, + "Options": { + "descriptions": "May contain an option string for the swap device", + "type": "string" + }, + "TimeoutSec": { + "descriptions": "Configures the time to wait for the swapon command to finish", + "type": "string" + } + } + }, "Install": { "additionalProperties": false, "type": "object", diff --git a/stages/test/test_systemd_unit_create.py b/stages/test/test_systemd_unit_create.py index c4ed2b2b..d4f9de42 100644 --- a/stages/test/test_systemd_unit_create.py +++ b/stages/test/test_systemd_unit_create.py @@ -44,6 +44,15 @@ STAGE_NAME = "org.osbuild.systemd.unit.create" }, "", ), + ( + { + "filename": "foo.swap", + "config": { + "Swap": {"What": ""}, + }, + }, + "", + ), # bad # # No filename @@ -77,6 +86,11 @@ STAGE_NAME = "org.osbuild.systemd.unit.create" "Mount": {"What": "", "Where": ""}, "Install": {}}}, "{'Unit': {}, 'Service': {}, 'Mount': {'What': '', 'Where': ''}, " "'Install': {}} is not valid under any of the given schemas"), + + # # .swap unit with Service section + ({"filename": "foo.swap", "config": {"Unit": {}, "Service": {}, "Swap": {"What": ""}, "Install": {}}}, + "{'Unit': {}, 'Service': {}, 'Swap': {'What': ''}, " + "'Install': {}} is not valid under any of the given schemas"), ]) @pytest.mark.parametrize("stage_schema", ["1"], indirect=True) def test_schema_validation(stage_schema, test_data, expected_err): @@ -125,6 +139,15 @@ def test_schema_validation(stage_schema, test_data, expected_err): }, "", ), + ( + { + "filename": "foo.swap", + "config": { + "Swap": {}, + }, + }, + "", + ), # bad ({"filename": "something.service", "config": {"Unit": {}, "Mount": {}, "Install": {}}}, "Error: something.service unit requires Service section"), @@ -132,6 +155,8 @@ def test_schema_validation(stage_schema, test_data, expected_err): "Error: data-gifs-cats.mount unit requires Mount section"), ({"filename": "data-gifs-cats.socket", "config": {"Unit": {}, "Service": {}, "Install": {}}}, "Error: data-gifs-cats.socket unit requires Socket section"), + ({"filename": "data-gifs-cats.swap", "config": {"Unit": {}, "Service": {}, "Install": {}}}, + "Error: data-gifs-cats.swap unit requires Swap section"), ]) def test_name_config_match(tmp_path, stage_module, test_data, expected_err): expected_unit_path = tmp_path / "usr/lib/systemd/system"