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 9, 2023
1 parent 2c3f54d commit 47a8aa0
Show file tree
Hide file tree
Showing 2 changed files with 172 additions and 1 deletion.
81 changes: 81 additions & 0 deletions stages/org.osbuild.kickstart
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,66 @@ SCHEMA = r"""
"type": "boolean"
}
}
},
"autopart": {
"description": "Automatically creates partitions",
"type": "object",
"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": "boolean"
},
"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 +319,24 @@ 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"
for key in ["type", "fstype", "nolvm", "encrypted", "passphrase",
"escrowcert", "backuppassphrase", "cipher", "luks-version",
"pbkdf", "pbkdf-memory", "pbkdf-time", "pbkdf-iterations"]:
if key not in autopart:
continue
val = autopart[key]
if isinstance(val, 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 +379,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
92 changes: 91 additions & 1 deletion stages/test/test_kickstart.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import pytest

import osbuild.meta
from osbuild.testutil.imports import import_module_from_path


Expand Down Expand Up @@ -83,10 +84,59 @@
"sd*|hd*|vda",
"/dev/vdc"
]
}
},
},
"lang en_US.UTF-8\nkeyboard us\ntimezone UTC\nzerombr\nclearpart --all --drives=sd*|hd*|vda,/dev/vdc",
),
({"autopart": {}}, "autopart"),
({"autopart": {"type": "plain"}}, "autopart --type=plain"),
({"autopart": {"fstype": "ext4"}}, "autopart --fstype=ext4"),
({"autopart": {"nolvm": True}}, "autopart --nolvm"),
({"autopart": {"encrypted": True}}, "autopart --encrypted"),
({"autopart": {"passphrase": "secret"}}, "autopart --passphrase=secret"),
({"autopart": {"escrowcert": "http://escrow"}}, "autopart --escrowcert=http://escrow"),
({"autopart": {"backuppassphrase": True}}, "autopart --backuppassphrase"),
({"autopart": {"cipher": "aes-xts-plain2048"}}, "autopart --cipher=aes-xts-plain2048"),
({"autopart": {"luks-version": "42"}}, "autopart --luks-version=42"),
({"autopart": {"pbkdf": "scrypt"}}, "autopart --pbkdf=scrypt"),
({"autopart": {"pbkdf-memory": 64}}, "autopart --pbkdf-memory=64"),
({"autopart": {"pbkdf-time": 128}}, "autopart --pbkdf-time=128"),
({"autopart": {"pbkdf-iterations": 256}}, "autopart --pbkdf-iterations=256"),
({
"lang": "en_US.UTF-8",
"keyboard": "us",
"timezone": "UTC",
"zerombr": True,
"clearpart": {
"all": True,
"drives": [
"sd*|hd*|vda",
"/dev/vdc"
]
},
"autopart": {
"type": "lvm",
"fstype": "zfs",
"nolvm": True,
"encrypted": True,
"passphrase": "secret2",
"escrowcert": "http://some-url",
"backuppassphrase": True,
"cipher": "twofish-cbc",
"luks-version": "2",
"pbkdf": "scrypt",
"pbkdf-memory": "256",
"pbkdf-time": "512",
# pbkdf-iterations cannot be used together with time
},
},
"lang en_US.UTF-8\nkeyboard us\ntimezone UTC\nzerombr\n" +
"clearpart --all --drives=sd*|hd*|vda,/dev/vdc\n" +
"autopart --type=lvm --fstype=zfs --nolvm --encrypted" +
" --passphrase=secret2 --escrowcert=http://some-url" +
" --backuppassphrase --cipher=twofish-cbc --luks-version=2" +
" --pbkdf=scrypt --pbkdf-memory=256 --pbkdf-time=512"
),
])
def test_kickstart(tmp_path, test_input, expected):
ks_stage_path = os.path.join(os.path.dirname(__file__), "../org.osbuild.kickstart")
Expand All @@ -105,3 +155,43 @@ def test_kickstart(tmp_path, test_input, expected):

# double check with pykickstart if the file looks valid
subprocess.check_call(["ksvalidator", ks_path])


@pytest.mark.parametrize("test_data,expected_err", [
# Valid patterns
({"autopart": {}}, ""),
({"autopart": {"type": "lvm"}}, ""),
({"autopart": {"fstype": "foo"}}, ""),
({"autopart": {"pbkdf-time": 1}}, ""),
({"autopart": {"pbkdf-iterations": 1}}, ""),
# Bad patterns
# Only specific types are allowed
({"autopart": {"type": "not-valid"}}, "'not-valid' is not one of ["),
# Only one of --pbkdf-{time,iterations} can be specified at the same time
({"autopart": {"pbkdf-time": 1, "pbkdf-iterations": 2}}, " should not be valid under "),
])
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 47a8aa0

Please sign in to comment.