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.2 BaseOS x86_64, image images.paas.redhat.com/bootc/rhel-bootc:latest-10.2, deployed beaker-bootc-kickstart.ks from ansible-baseos-ci.
  • ks-meta: the driver's bootc defaults plus no_autopart.
  • Two --ks-append snippets, standing in for kickstart.script and kickstart.post-install:
    • reqpart --add-boot and part / --size 4096 --grow --fstype xfs
    • a %post that echoes TFT-4430 marker: {{ not rendered }} '%}' back\\slash ünicode to the console

Results:

  • The kickstart Beaker rendered carries both snippets after onerror, with no autopart and no Jinja left over.
  • Anaconda partitioned the disk as the snippet asked: biosboot, /boot (xfs) and / (xfs) on vda.
  • The console log shows the marker line exactly as written. The Jinja syntax and the non-ASCII character are intact. The shell's echo turns the double backslash into one.
  • The bootc install completed, and /distribution/check-install passed.

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.
  • pyupgrade crashes 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

Edited by Miroslav Vadkerti

Merge request reports

Loading
Loading