Skip to content

Commit

Permalink
stages(kickstart): implement "reboot" option
Browse files Browse the repository at this point in the history
This commit implements the `reboot` option for kickstart files.

Note that there are two ways this can be enabled via the json.
Either via a boolean or by passing a dict with options.
```
{"reboot": true}
{"reboot": {"eject": true, "kexec": true}
```

Note that passing the empty dict will *not* write out a reboot
line and the code also changes "clearpart" have have the same
behavior to be consistent.
```
{"reboot": {}}
```
I can see arguments that passing the empty dict should still
enable `reboot` without options. Happy to change it if someone
has strong(er) opinions.
  • Loading branch information
mvo5 committed Nov 9, 2023
1 parent b9ad7dd commit 00357b8
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 3 deletions.
39 changes: 38 additions & 1 deletion stages/org.osbuild.kickstart
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,26 @@ SCHEMA = r"""
"type": "boolean"
}
}
},
"reboot": {
"description": "Reboot after the installation is successfully completed",
"oneOf": [
{
"type": "boolean"
}, {
"type": "object",
"additionalProperties": false,
"properties": {
"eject": {
"description": "Attempt to eject the installation media before rebooting",
"type": "boolean"
},
"kexec": {
"description": "Use this option to reboot into the new system using the kexec",
"type": "boolean"
}
}
}]
}
}
"""
Expand Down Expand Up @@ -238,7 +258,7 @@ def make_users(users: Dict) -> List[str]:

def make_clearpart(options: Dict) -> str:
clearpart = options.get("clearpart")
if clearpart is None:
if not clearpart:
return ""
cmd = "clearpart"
al = clearpart.get("all", False)
Expand All @@ -259,6 +279,19 @@ def make_clearpart(options: Dict) -> str:
return cmd


def make_reboot(options):
reboot = options.get("reboot", None)
if not reboot:
return ""
cmd = "reboot"
if isinstance(reboot, dict):
if reboot.get("eject"):
cmd += " --eject"
if reboot.get("kexec"):
cmd += " --kexec"
return cmd


def main(tree, options):
path = options["path"].lstrip("/")
ostree = options.get("ostree")
Expand Down Expand Up @@ -302,6 +335,10 @@ def main(tree, options):
if clearpart:
config += [clearpart]

reboot = make_reboot(options)
if reboot:
config += [reboot]

target = os.path.join(tree, path)
base = os.path.dirname(target)
os.makedirs(base, exist_ok=True)
Expand Down
18 changes: 16 additions & 2 deletions stages/test/test_kickstart.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@
'sshkey --username someusr "ssh-rsa not-really-a-real-key"'
),
({"zerombr": "true"}, "zerombr"),
({"clearpart": {}}, "clearpart"),
# no clearpart for an empty dict (will not do anything with options anyway)
({"clearpart": {}}, ""),
({"clearpart": {"all": True}}, "clearpart --all"),
({"clearpart": {"drives": ["hda", "hdb"]}}, "clearpart --drives=hda,hdb",),
({"clearpart": {"drives": ["hda"]}}, "clearpart --drives=hda"),
Expand Down Expand Up @@ -88,6 +89,14 @@
},
"lang en_US.UTF-8\nkeyboard us\ntimezone UTC\nzerombr\nclearpart --all --drives=sd*|hd*|vda,/dev/vdc",
),
# no reboot for an empty dict
({"reboot": {}}, ""),
({"reboot": True}, "reboot"),
({"reboot": {"eject": False}}, "reboot"),
({"reboot": {"eject": True}}, "reboot --eject"),
({"reboot": {"kexec": False}}, "reboot"),
({"reboot": {"kexec": True}}, "reboot --kexec"),
({"reboot": {"eject": True, "kexec": True}}, "reboot --eject --kexec"),
])
def test_kickstart(tmp_path, test_input, expected):
ks_stage_path = os.path.join(os.path.dirname(__file__), "../org.osbuild.kickstart")
Expand All @@ -105,7 +114,8 @@ def test_kickstart(tmp_path, test_input, expected):
assert ks_content == expected + "\n"

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


@pytest.mark.parametrize("test_data,expected_err", [
Expand All @@ -118,10 +128,14 @@ def test_kickstart(tmp_path, test_input, expected):
({"clearpart": {"list": ["\n%pre not allowed"]}}, "not allowed' does not match"),
({"clearpart": {"list": ["no,comma"]}}, "no,comma' does not match"),
({"clearpart": {"disklabel": "\n%pre not allowed"}}, "not allowed' does not match"),
({"reboot": "random-string"}, "'random-string' is not valid "),
({"reboot": {"random": "option"}}, "{'random': 'option'} is not valid "),
# GOOD pattern we want to keep working
({"clearpart": {"drives": ["sd*|hd*|vda", "/dev/vdc"]}}, ""),
({"clearpart": {"drives": ["disk/by-id/scsi-58095BEC5510947BE8C0360F604351918"]}}, ""),
({"clearpart": {"list": ["sda2", "sda3", "sdb1"]}}, ""),
({"reboot": True}, ""),
({"reboot": {"kexec": False}}, ""),
])
def test_schema_validation_smoke(test_data, expected_err):
name = "org.osbuild.kickstart"
Expand Down

0 comments on commit 00357b8

Please sign in to comment.