draupnir functional-status emit — lifecycle surfaces into nornir's matrix
Added draupnir 0.1.9 (additive). Draupnir's live lifecycle surfaces self-report whether they actually worked by recording a functional_status(component, check, ok, detail) row — GREEN when the surface succeeded, RED when a boot / probe / teardown / seed authoring failed. The nornir Aspect::Functional runner drains those rows into the constellation-wide introspection-coverage matrix, so a failed boot shows up as a RED matrix row automatically, without anyone watching a log.
This reuses the shared nornir-testmatrix sink verbatim (the functional_status shim in src/lib.rs behind the optional testmatrix feature) — no twin. With the feature OFF the emit is a compiled-out #[inline] no-op and the lean default build pulls no nornir dependency.
The emitting surfaces
| Surface | component / check |
GREEN | RED |
boot() (src/lib.rs) |
draupnir/boot · boot |
backend brought the instance up | Boot::boot failed (no slot, backend error) |
rollback() (src/lib.rs) |
draupnir/lifecycle · rollback |
backend confirmed power-off | Lifecycle::power_off failed |
await_power_state() (src/lib.rs) |
draupnir/lifecycle · await_power_state |
instance reached the wanted state | bounded wait timed out |
boot_fleet_and_await[_parallel]() |
draupnir/lifecycle · boot_fleet_and_await[_parallel] |
every member ready | ≥1 member timed out / errored |
ContainerBoot::wait_ready() (src/container.rs) |
draupnir/container · wait_ready |
container reached its Readiness |
probe timed out / backend error |
write_seed_dir() (src/seed.rs) |
draupnir/seed · write_seed_dir |
NoCloud file set landed on disk | mkdir/write failed |
build_seed_image() (src/seed.rs) |
draupnir/seed · build_seed_image |
cidata vfat image authored |
fatfs format/author failed |
Each emit wraps the surface's own Result (outcome.is_ok()) and then returns that same Result unchanged — the verdict is an observation, never a control-flow change. A failure is always both a RED row and a propagated Err: a boot / teardown / seed that could not complete must be visible, never swallowed.
rollback() — the undo seam (draupnir 0.1.9, additive)
// Tear a booted instance down by powering it off through the backend's Lifecycle,
// recording the teardown verdict. GREEN on power-off; a backend failure to power it
// down is a RED row AND a propagated Err.
pub fn rollback<L: Lifecycle>(lifecycle: &L, machine: &Machine) -> Result<()>;
The undo half of boot_and_await: jera / Skidbladnir call it when a provision must be reverted — a boot that never reached PowerState::On, a wait_ready that timed out, or a fleet member being reclaimed. Written against the plain Lifecycle trait so it rolls back any backend (KVM / container / Redfish) and a mock alike — one shared teardown, never a per-backend twin.
Tests (RED-when-broken, no live instance)
tests/testmatrix_rows.rs:
failed_boot_and_rollback_emit_red_rows— a fake/failed instance (a backend whosebootandpower_offboth error) drivesboot()androllback(); each call (a) propagates theErrand (b) records a RED row, while a healthy backend records the GREEN control. RED-when-broken: drop the emit or invert the verdict (a broken boot reported GREEN) and the drain assertions fail. The drain-and-assert half usesdraupnir::drain_status_rows()(a thin re-drain of the nornir buffer, featuretestmatrix); the default build still asserts the propagatedErr.- The existing
await_power_state_*/boot_fleet_*/seed_*rows cover the other surfaces in the table above.