Skip to content

Commit

Permalink
stages(kickstart): support autopart
Browse files Browse the repository at this point in the history
This commit implements the `autopart` kickstart option and adds
matching tests.
  • Loading branch information
mvo5 committed Nov 8, 2023
1 parent 78238ba commit 23c8d3b
Show file tree
Hide file tree
Showing 2 changed files with 128 additions and 1 deletion.
91 changes: 91 additions & 0 deletions stages/org.osbuild.kickstart
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,74 @@ SCHEMA = r"""
"type": "boolean"
}
}
},
"autopart": {
"description": "Automatically creates partitions",
"type": "object",
"anyOf": [
{ "required": ["pbkdf-time"],
"not": {"required": ["pbkdf-iterations"]}
},
{ "required": ["pbkdf-iterations"],
"not": {"required": ["pbkdf-time"]}
},
{ "not": {"required": ["pbkdf-iterations", "pbkdf-time"]}}
],
"properties": {
"type": {
"description": "Selects one of the predefined automatic partitioning schemes you want to use",
"type": "string",
"enum": ["lvm", "btrfs", "plain", "thinp"]
},
"fstype": {
"description": "Specify a supported file system (such as ext4 or xfs) to replace the default when doing automatic partitioning",
"type": "string"
},
"nolvm": {
"description": "Do not use LVM or Btrfs for automatic partitioning. This option is equal to --type=plain",
"type": "boolean"
},
"encrypted": {
"description": "Encrypts all partitions",
"type": "boolean"
},
"passphrase": {
"description": "Provides a default system-wide passphrase for all encrypted devices",
"type": "string"
},
"escrowcert": {
"description": "Stores data encryption keys of all encrypted volumes as files in /root, encrypted using the X.509 certificate from the URL specified",
"type": "string"
},
"backuppassphrase": {
"description": "Adds a randomly-generated passphrase to each encrypted volume",
"type": "string"
},
"cipher": {
"description": "Specifies which type of encryption will be used if the Anaconda default aes-xts-plain64 is not satisfactory",
"type": "string"
},
"luks-version": {
"description": "Specifies which version of LUKS should be used to encrypt the system",
"type": "string"
},
"pbkdf": {
"description": "Sets Password-Based Key Derivation Function (PBKDF) algorithm for the LUKS keyslot",
"type": "string"
},
"pbkdf-memory": {
"description": "Sets the memory cost for PBKDF",
"type": "integer"
},
"pbkdf-time": {
"description": "Sets the number of milliseconds to spend with PBKDF passphrase processing",
"type": "integer"
},
"pbkdf-iterations": {
"description": "Sets the number of iterations for passphrase processing directly",
"type": "integer"
}
}
}
}
"""
Expand Down Expand Up @@ -259,6 +327,26 @@ def make_clearpart(options: Dict) -> str:
return cmd


def make_autopart(options: Dict) -> str:
autopart = options.get("autopart")
if autopart is None:
return ""
cmd = "autopart"
# TODO: make pkdf-{time,iterations} mutally exclusive at the
# schema level
for key in ["type", "fstype", "nolvm", "encrypted", "passphrase",
"escrowcert", "backuppassphrase", "cipher", "luks-version",
"pbkdf", "pbkdf-memory", "pbkdf-time", "pbkdf-iterations"]:
if not key in autopart:
continue
val = autopart[key]
if type(val) is bool:
cmd += f" --{key}"
else:
cmd += f" --{key}={val}"
return cmd


def main(tree, options):
path = options["path"].lstrip("/")
ostree = options.get("ostree")
Expand Down Expand Up @@ -301,6 +389,9 @@ def main(tree, options):
clearpart = make_clearpart(options)
if clearpart:
config += [clearpart]
autopart = make_autopart(options)
if autopart:
config += [autopart]

target = os.path.join(tree, path)
base = os.path.dirname(target)
Expand Down
38 changes: 37 additions & 1 deletion stages/test/test_kickstart.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import pytest

from osbuild.testutil.imports import import_module_from_path

import osbuild.meta

@pytest.mark.parametrize("test_input,expected", [
({"lang": "en_US.UTF-8"}, "lang en_US.UTF-8"),
Expand All @@ -16,6 +16,10 @@
"timezone": "UTC",
},
"lang en_US.UTF-8\nkeyboard us\ntimezone UTC"),
({"autopart": {}},"autopart"),
({"autopart": {"type": "plain"}},"autopart --type=plain"),
({"autopart": {"type": "lvm"}},"autopart --type=lvm"),
({"autopart": {"nolvm": True}},"autopart --nolvm"),
])
def test_kickstart(tmp_path, test_input, expected):
ks_stage_path = os.path.join(os.path.dirname(__file__), "../org.osbuild.kickstart")
Expand All @@ -30,3 +34,35 @@ def test_kickstart(tmp_path, test_input, expected):
with open(os.path.join(tmp_path, ks_path), encoding="utf-8") as fp:
ks_content = fp.read()
assert ks_content == expected + "\n"


@pytest.mark.parametrize("test_data,expected_err", [
({"autopart": {"pbkdf-time": 1}}, ""),
({"autopart": {"pbkdf-iterations": 1}}, ""),
({"autopart": {"pbkdf-time": 1, "pbkdf-iterations": 2}}, " is not valid "),
])
def test_schema_validation_smoke(test_data, expected_err):
name = "org.osbuild.kickstart"
root = os.path.join(os.path.dirname(__file__), "../..")
mod_info = osbuild.meta.ModuleInfo.load(root, "Stage", name)
schema = osbuild.meta.Schema(mod_info.get_schema(), name)

test_input = {
"name": "org.osbuild.kickstart",
"options": {
"path": "some-path",
}
}
test_input["options"].update(test_data)
res = schema.validate(test_input)

if expected_err == "":
# debug
if not res.valid:
print([e.as_dict() for e in res.errors])
assert res.valid is True
else:
assert res.valid is False
assert len(res.errors) == 1
err_msgs = [e.as_dict()["message"] for e in res.errors]
assert expected_err in err_msgs[0]

0 comments on commit 23c8d3b

Please sign in to comment.