diff --git a/stages/org.osbuild.kernel-cmdline b/stages/org.osbuild.kernel-cmdline index b0ea4899..df72094f 100755 --- a/stages/org.osbuild.kernel-cmdline +++ b/stages/org.osbuild.kernel-cmdline @@ -29,7 +29,8 @@ MAX_SIZE_MAP = { def main(tree, options): root_fs_uuid = options.get("root_fs_uuid", "") additional = options.get("kernel_opts", "") - max_cmdline_size = MAX_SIZE_MAP.get(platform.machine().lower(), 4096) + max_cmdline_size = options.get("kernel_cmdline_size", + MAX_SIZE_MAP.get(platform.machine().lower(), 4096)) params = [] diff --git a/stages/org.osbuild.kernel-cmdline.meta.json b/stages/org.osbuild.kernel-cmdline.meta.json index 4ba73d4e..e511078b 100644 --- a/stages/org.osbuild.kernel-cmdline.meta.json +++ b/stages/org.osbuild.kernel-cmdline.meta.json @@ -30,6 +30,12 @@ "description": "Additional kernel boot options", "type": "string", "default": "" + }, + "kernel_cmdline_size": { + "description": "Sets a custom maximum kernel cmdline size", + "type": "integer", + "minimum": 256, + "maximum": 4096 } } } diff --git a/stages/test/test_kernel_cmdline.py b/stages/test/test_kernel_cmdline.py new file mode 100644 index 00000000..9b0c87fd --- /dev/null +++ b/stages/test/test_kernel_cmdline.py @@ -0,0 +1,63 @@ +#!/usr/bin/python3 + +from unittest import mock + +import pytest # type: ignore + +from osbuild import testutil + +STAGE_NAME = "org.osbuild.kernel-cmdline" + + +@pytest.mark.parametrize("options, expected", [ + ({"kernel_opts": "--custom-opt=some"}, "--custom-opt=some"), + ({"root_fs_uuid": "some-uuid"}, "root=UUID=some-uuid"), + ({"root_fs_uuid": "other-uuid", "kernel_opts": "--custom-opt=other"}, + "root=UUID=other-uuid --custom-opt=other"), +]) +def test_kernel_cmdline(tmp_path, stage_module, options, expected): + tree = tmp_path / "tree" + tree.mkdir() + + stage_module.main(tree, options) + cmdline_file = tree / "etc" / "kernel" / "cmdline" + assert cmdline_file.exists() + assert cmdline_file.read_text() == expected + + +@pytest.mark.parametrize("options, arch", [ + ({"kernel_opts": "a" * 2049}, "x86_64"), + ({"kernel_opts": "a" * 1025}, "arm"), + ({"kernel_opts": "a" * 2049}, "aarch64"), + ({"kernel_opts": "a" * 257, "kernel_cmdline_size": 256}, "any"), +]) +@mock.patch("platform.machine") +def test_kernel_opts_size_check(mock_machine, tmp_path, stage_module, options, arch): + tree = tmp_path / "tree" + tree.mkdir() + mock_machine.return_value = arch + + with pytest.raises(ValueError) as e: + stage_module.main(tree, options) + assert str(e.value).startswith("The size of the kernel cmdline options cannot be larger than") + + +@pytest.mark.parametrize("options, expected_error", [ + ({"kernel_cmdline_size": 256}, ""), + ({"kernel_cmdline_size": 4096}, ""), + ({"kernel_cmdline_size": "not integer"}, "is not of type 'integer'"), + ({"kernel_cmdline_size": 0}, "is less than the minimum of 256"), + ({"kernel_cmdline_size": 4097}, "is greater than the maximum of 4096"), +]) +def test_schema_validation_kernel_cmdline(stage_schema, options, expected_error): + test_input = { + "type": STAGE_NAME, + "options": options + } + res = stage_schema.validate(test_input) + + if not expected_error: + assert res.valid + else: + assert not res.valid + testutil.assert_jsonschema_error_contains(res, expected_error, expected_num_errs=1)