holger-recipe/tests/medium_kernel.rs
//! **Every holger medium asks its kernel for what pairing needs.**
//!
//! MEASURED on the mock 2026-09-28: a donor kernel built for the appliance-boot
//! contract alone carried `# CONFIG_TUN is not set`, and the installed box answered
//! `wg-installer: open /dev/net/tun … No such device (os error 19)` — it waited for
//! an installer it could never hear. The requirement is declared on the installer
//! row (`[[build.installer.requires]]`, edge `kernel`), so the donor is built with it
//! and the why-index names holger as the one who asked.
use holger_recipe::packaging::{front_installer_metered, installer, metered_installer};
use nornir_build_thing::requires::Requirement;
#[test]
fn every_holger_installer_requires_tun_and_inet_of_its_kernel() {
for m in [installer(), metered_installer(), front_installer_metered()] {
assert!(
m.kernel_donor.is_some(),
"{} names its kernel donor",
m.name
);
for want in ["CONFIG_NET", "CONFIG_INET", "CONFIG_TUN"] {
let found = m.requires.iter().any(|r| {
r.edge == "kernel" && matches!(&r.what, Requirement::Kconfig(k) if k.symbol == want)
});
assert!(
found,
"{} does not require {want} of its kernel: {:?}",
m.name, m.requires
);
}
}
}
#[test]
fn the_emitted_toml_carries_the_requirement() {
let toml = holger_recipe::emitted_toml().expect("the recipe emits");
assert!(
toml.matches("symbol = \"CONFIG_TUN\"").count() >= 3,
"every installer row asks for CONFIG_TUN"
);
}
/// The holger boxes keep their data on the estate's data volume as xfs, with
/// holger's data root there. The front lays xfs too: one donor kernel, one
/// requirement fragment for every holger medium.
#[test]
fn the_holger_boxes_lay_data_as_xfs_with_holger_data_on_it() {
use nornir_build_thing::data::DataFs;
for m in [installer(), metered_installer()] {
assert_eq!(m.data.fs, DataFs::Xfs, "{}", m.name);
assert_eq!(
m.env.get("HOLGER_DATA").map(String::as_str),
Some("/data/holger"),
"{}",
m.name
);
}
assert_eq!(
front_installer_metered().data,
installer().data,
"one donor kernel: every medium asks it the same"
);
assert_eq!(front_installer_metered().requires, installer().requires);
}
/// ★ Gunnar 35290f29 / b247bf64 (2026-09-15), shared by lane G10: **every installed holger
/// medium demands PCI hot-plug of its kernel.** UpCloud resizes only DETACHED volumes, so a
/// runtime grow is detach → resize → re-attach, and a kernel with `# CONFIG_HOTPLUG_PCI is
/// not set` answers 511 HOTPLUG_FAILED. The rows are edda's `kernel_requires::hotplug_requires`
/// (moved out of gunnar-recipe), one list for both products. Not on the live medium: its
/// `/data` is RAM.
#[test]
fn every_installed_holger_medium_requires_pci_hotplug_of_its_kernel() {
let want: Vec<String> = nornir_build_thing::kernel_requires::hotplug_requires()
.iter()
.filter_map(|r| match &r.what {
Requirement::Kconfig(k) => Some(k.symbol.clone()),
_ => None,
})
.collect();
assert!(want.iter().any(|s| s == "CONFIG_HOTPLUG_PCI"));
for m in [installer(), metered_installer(), front_installer_metered()] {
for s in &want {
let found = m.requires.iter().any(|r| {
r.edge == "kernel" && matches!(&r.what, Requirement::Kconfig(k) if &k.symbol == s)
});
assert!(found, "{} does not require {s} of its kernel", m.name);
}
}
let toml = holger_recipe::emitted_toml().expect("the recipe emits");
assert!(
toml.matches("symbol = \"CONFIG_HOTPLUG_PCI\"").count() >= 3,
"every installer row asks for CONFIG_HOTPLUG_PCI"
);
}