orchestrate — guide
Class:
guide· audience: a techie bringing up a lake of products. The what is inorchestrate-design.md.
Compute the bring-up order
use skidbladnir::{orchestrate, product::ComponentSpec};
let products: Vec<_> = [ComponentSpec::nornir_server(), ComponentSpec::holger()]
.iter().map(|c| c.as_product()).collect();
let waves = orchestrate::start_waves(&products)?; // Vec<Vec<String>>
// e.g. [["nornir-server"], ["holger"]] — holger's `after nornir-server` puts it in a later wave
Or straight from a whole-lake spec:
use skidbladnir::{config::LakeSpec, pki::PkiSource, product::ComponentSpec};
let lake = LakeSpec {
version: "20260704".into(),
pki: PkiSource::SelfGenerated,
products: vec![ComponentSpec::nornir_server(), ComponentSpec::holger()],
};
let waves = lake.start_waves()?; // lib.rs:137
Start each wave (concurrency within a wave)
for wave in waves {
std::thread::scope(|s| {
for product in &wave {
s.spawn(move || { /* install::apply / start this product */ });
}
});
}
Products in the same wave have no ordering constraint between them, so start them concurrently; wave i+1 only begins once wave i is up.
Tear down in reverse
let order = orchestrate::teardown_order(&products)?; // dependents before their dependencies
Emit telemetry to the warehouse
use skidbladnir::events::{JsonlSink, EventEmitter, op, phase, status};
let sink = JsonlSink::under_warehouse(&warehouse_root)?; // <root>/airgap_events.jsonl
let em = EventEmitter::new("run-2026-07-04", &sink);
em.emit("holger", op::INSTALL, phase::START, status::RUNNING, "unpacking", &["nornir-server".into()]);
// … later …
em.emit("holger", op::START, phase::END, status::OK, "healthy", &[]);
Read them back (sorted by seq) with skidbladnir::events::read_jsonl(&path)?.
Gotchas
- A dependency cycle is a hard error naming the stuck products — fix the
afteredges. - Deps that name something outside your product set (
network.target) are ignored, not errors — they are init-system concerns, not products. - Emission is best-effort: a sink failure logs to stderr and never aborts the bring-up. The events are observability, not a gate.
- The sink writes under the warehouse root, never
/tmp— pass a real warehouse path.