stages: introduce dnf.module-config

A new stage that allows writing a DNF module configuration file to a
given path.

Signed-off-by: Simon de Vlieger <supakeen@redhat.com>
This commit is contained in:
Simon de Vlieger 2025-01-16 11:42:03 +01:00 committed by Ondřej Budai
parent 4337cd9595
commit 0d25c845f8
3 changed files with 141 additions and 0 deletions

View file

@ -0,0 +1,29 @@
#!/usr/bin/python3
import configparser
import os
import sys
import osbuild.api
def main(tree, options):
path = options["path"]
conf = options["conf"]
inip = configparser.ConfigParser()
inip[conf["name"]] = conf
# we need to handle enabled profiles as a string as configparser
# would normally write [a, b]
inip[conf["name"]]["profiles"] = ", ".join(conf["profiles"])
with open(os.path.join(tree, path), "w", encoding="utf-8") as file:
inip.write(file)
return 0
if __name__ == '__main__':
args = osbuild.api.arguments()
r = main(args["tree"], args["options"])
sys.exit(r)

View file

@ -0,0 +1,41 @@
{
"summary": "Write DNF module configuration.",
"description": [
"This stage allows writing DNF module configurations. These files are",
"which are used by DNF to determine enabled modules."
],
"schema_2": {
"options": {
"additionalProperties": false,
"description": "DNF configuration.",
"properties": {
"path": {
"type": "string",
"description": "Path to write the module configuration to."
},
"conf": {
"additionalProperties": false,
"type": "object",
"description": "DNF module configuration values.",
"properties": {
"name": {
"type": "string"
},
"stream": {
"type": "string"
},
"state": {
"type": "string"
},
"profiles": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
}
}
}
}

View file

@ -0,0 +1,71 @@
#!/usr/bin/python3
import pytest
from osbuild import testutil
STAGE_NAME = "org.osbuild.dnf.module-config"
@pytest.mark.parametrize("test_data,expected_err", [
# bad
({"conf": "must-be-object"}, "'must-be-object' is not of type 'object'"),
({"path": {}}, "{} is not of type 'string'"),
# good
({
"conf":
{
"name": "some-module",
"stream": "some-stream",
"state": "some-state",
"profiles": ["some-profile"],
},
}, "")
])
def test_dnf_module_config_schema_validation(stage_schema, test_data, expected_err):
test_input = {
"type": STAGE_NAME,
"options": {
}
}
test_input["options"].update(test_data)
res = stage_schema.validate(test_input)
if expected_err == "":
assert res.valid is True, f"err: {[e.as_dict() for e in res.errors]}"
else:
assert res.valid is False
testutil.assert_jsonschema_error_contains(res, expected_err, expected_num_errs=1)
def test_dnf_module_config_writes_file(tmp_path, stage_module):
treepath = tmp_path / "tree"
confpath = "etc/dnf/modules.d/module.conf"
fullpath = treepath / confpath
fullpath.parent.mkdir(parents=True, exist_ok=True)
options = {
"path": confpath,
"conf": {
"name": "some-module",
"stream": "some-stream",
"state": "some-state",
"profiles": ["some-profile", "other-profile"],
}
}
stage_module.main(treepath, options)
assert fullpath.exists()
confdata = fullpath.read_text()
assert confdata == """\
[some-module]
name = some-module
stream = some-stream
state = some-state
profiles = some-profile, other-profile
"""