beaker: inline kickstart snippets into custom kickstart templates
Problem
On image-mode (bootc) Beaker machines, the kickstart.script, kickstart.pre-install and kickstart.post-install fields of a guest request never reach the installer. Custom partitioning set through the tmt plan is lost with them, which is what TFT-5259 reports.
The driver submits every bootc recipe with a custom <kickstart/> template (--kickstart of bkr workflow-simple). Beaker hands <ks_append/> snippets only to its own templates. For a recipe with a custom template it passes ks_appends=None to generate_kickstart() (scheduler.py#L2654-L2706). The deployed beaker-bootc-kickstart.ks already renders {{ ks_appends|join('\n') }}, so the line has been rendering an empty list.
Fix
For a bootc distro, _create_kickstart_template() reads the custom template and prepends the snippets as a Jinja set statement:
{% set ks_appends = ["part / --grow --ondisk=mpatha", "%pre ...\n%end"] %}
<template>_create_job_xml() writes the result to a temporary file, which exists until bkr workflow-simple --kickstart finishes. The driver no longer passes --ks-append for these recipes. The set statement restores the ks_appends variable, and the template places the snippets where Beaker's default template does. The snippets are JSON-encoded string literals, so Jinja syntax inside them is not rendered, matching Beaker's default templates. Recipes without a custom kickstart are left alone.
The example server/configuration/beaker-bootc-kickstart.ks gains only the ks_appends line and keeps its autopart partitioning. The deployed template in ansible-baseos-ci already uses rhts_partitions and renders ks_appends. Deployments need no configuration change.
The new tests render the generated template with a sandboxed Jinja environment. The snippets come out verbatim, including quotes, backslashes, non-ASCII characters and Jinja syntax.
Live test
These runs tested the first version of this MR, fcf3e0b7, which rewrote the job XML instead of the template file. The <kickstart/> text Beaker receives is the same with c4959fd8, but the new code path has not run against a real hub.
Tested against the production Beaker hub (beakerpy3.engineering.redhat.com) with J:1200331231. Artemis itself wasn't involved: the job XML came from the driver's bkr workflow-simple --dry-run options, run by hand, and then went through inline_ks_appends() from fcf3e0b7 and prettify().
- Distro
RHEL-10.2BaseOS x86_64, imageimages.paas.redhat.com/bootc/rhel-bootc:latest-10.2, deployedbeaker-bootc-kickstart.ksfromansible-baseos-ci. - ks-meta: the driver's bootc defaults plus
no_autopart. - Two
--ks-appendsnippets, standing in forkickstart.scriptandkickstart.post-install:reqpart --add-bootandpart / --size 4096 --grow --fstype xfs- a
%postthat echoesTFT-4430 marker: {{ not rendered }} '%}' back\\slash ünicodeto the console
Results:
- The kickstart Beaker rendered carries both snippets after
onerror, with noautopartand no Jinja left over. - Anaconda partitioned the disk as the snippet asked: biosboot,
/boot(xfs) and/(xfs) onvda. - The console log shows the marker line exactly as written. The Jinja syntax and the non-ASCII character are intact. The shell's
echoturns the double backslash into one. - The bootc install completed, and
/distribution/check-installpassed.
An earlier run, J:1200331019, got the same snippets through to anaconda. Its install then failed with PartitioningError because the test asked for a 20 GiB / on 10 GiB disks, so I cancelled it.
rhts_partitions still emits autopart unless no_autopart is set, and anaconda rejects autopart combined with part. A request that partitions through kickstart.script therefore also needs no_autopart in kickstart.metadata, which is how Beaker's default templates behave too. The example template's hardcoded autopart has the same limit.
Notes
- Supersedes !1550 (closed), which edited only the example template.
pyupgradecrashes locally under Python 3.14 (TypeError: cannot use a bytes pattern on a string-like object), so the commits skipped that hook and rely on CI's pre-commit run.
Resolves TFT-4430 Related to TFT-5259
Generated-by: Claude Code