Expand description
The Isothrum runner: executes the run manifest Isoloom writes next to a target’s generated
files (.isoloom/<target>/run.json), and reads nothing else of them.
The manifest holds the native commands in order, the documented driver actions, readiness,
outputs, check runners, and the slots inputs and secrets travel through. The runner adds what
only it can know: the run’s name and private state, one operation per run at a time, the
values of params and secrets, and the event stream an embedder (the Cyber CTF launcher, the
Isothrum desktop app, the isothrum command line) shows. It sits between Isoloom (whose
isoloom-manifest crate is the contract) and those embedders, and depends on no UI.
§Example
Opening a manifest reads and checks it, and nothing runs yet:
use isothrum_runner::{RunName, Runner, RunnerConfig};
let home = tempfile::tempdir()?;
let lab = tempfile::tempdir()?;
let manifest = lab.path().join("run.json");
std::fs::write(&manifest, include_str!("../tests/fixtures/flag-portal.run.json"))?;
let runner = Runner::new(RunnerConfig::new(home.path()))?;
let lab = runner.open(&manifest, Some(RunName::new("demo")?))?;
assert_eq!(lab.manifest().lab.name.as_str(), "flag-portal");
assert_eq!(lab.name().as_str(), "demo");
assert!(runner.runs()?.is_empty(), "no operation has run");Running it takes the tools it names (here Docker), and an event sink:
use isothrum_runner::{JsonLines, Park, RunOptions};
let events = JsonLines::new(std::io::stderr());
let opts = RunOptions::new().with_param("ISOLOOM_PUBLISH_FIXED", "1");
let run = lab.up(&opts, &events).await?;
let report = lab.check(&opts, &events).await?;
lab.park(Park::Shutdown, &events).await?;
lab.resume(&opts, &events).await?;
lab.down(&opts, &events).await?;§Ports
Every outside effect goes through a port the embedder can replace: ProcessRunner (the
tools), HostPorts (the host’s ports), Clock (the time), Events (where events go)
and Hooks (the embedder’s sidecars). They are open traits on purpose, since embedders
implement them; the fake feature has test doubles for the first three.
§Deliveries
Params and slot values reach the tools only through the manifest’s deliveries, never on a
command line: env (a step’s variables), tf_var (TF_VAR_<var>, a map_entry making one
JSON object), state_file (an owner-only file in the run’s state folder, a Terraform variable
file or NAME=value lines, written at up, kept for the destroy, removed by a successful
down), and guest over SSH, Isoloom’s delivery into a machine for a cloud with no secret
store (DigitalOcean, Linode): once the step it names (else every up step) succeeded, one
SSH session per machine, to the outputs address_<machine> (- as _) and
ssh_user_<machine> (else ssh_user), runs a fixed script as root that keeps each value
owner-only under /var/lib/isoloom/secrets/<slot name> and then touches
/var/lib/isoloom/secrets/.delivered, which the machine’s set-up waits for; the values travel
base64-encoded on its standard input, retried while SSH can’t connect (up to 15 minutes).
§Concurrency
The runner is async, on Tokio, chosen once for the whole crate: every tool runs as an async
process with a time limit (a step’s timeout_s, else an hour; 25 s for a probe; two minutes
for an action’s own Docker calls; ten for a hook), and dropping an operation’s future ends the
process tree it started. Operations on one run are serialized; operations on different runs
run side by side. One exception to “no blocking call in async code”: the run’s state files (a
record, a lock, the pinned ports, the event log) are a few KB on the local disk, so they are
read and written inline rather than through spawn_blocking.
§Errors and tracing
Every fallible call returns Result, whose Error has a stable ErrorCode; an
operation also reports its failure as its last event. The runner logs through tracing (spans
run, step and driver, events for each process), never by printing.
Re-exports§
pub use process::ProcessRunner;pub use process::ProcessSpec;pub use process::SystemProcesses;pub use isoloom_manifest;
Modules§
- process
- Running the tools a manifest names: the
ProcessRunnerport.SystemProcessesstarts real processes (never through a shell, headless on Windows, each tool in a process group or job object so a timeout or a cancellation ends the whole tree it started); thefakefeature’sfake::FakeProcessesanswers from a test’s script instead.
Structs§
- Adoption
- A deployment the runner takes over (
Runner::adopt). - Check
Report - Every check’s outcome.
- Check
Result - One check’s outcome.
- Event
- One event of an operation.
- Held
Port - A free port, held until dropped so it isn’t handed out twice while others are picked.
- Hook
Error - Why a hook failed, in its own words.
- Json
Lines - Events written as JSON, one per line, flushed each time.
- Lab
- A run: a manifest under a name, on a runner.
- LabView
- What a hook sees of the run.
- LastOp
- The last operation on a run and how it ended.
- Loaded
Manifest - A manifest read from its file.
- Loopback
Ports - The real host: sockets on the loopback. Never all interfaces: binding 0.0.0.0 made Windows Firewall ask whether the app may accept connections from the network (launcher #230).
- Machine
Status - One machine of the manifest, as it stands.
- Network
View - A network of the lab.
- Orphans
- What a cloud still holds past its expiry that no recorded run covers.
- Output
Value - An output’s value.
- Place
- Where a query looks: the regions (AWS) or the project (Google Cloud); the CLI’s own default when unset.
- Published
Port - A published port: where a machine’s port answers on the host.
- Recorder
- Events kept in memory, for an embedder that reads them later (and for tests).
- Run
- A run that came up (or resumed).
- RunName
- A run’s name: 1 to 63 lowercase letters, digits,
-or_, starting with a letter or digit (Compose’s rule for a project name). - RunOptions
- The options of one operation.
- RunRecord
- What the runner keeps about a run (never a secret’s value).
- Runner
- The runner.
- Runner
Config - How a runner is built: its home, and the ports it reaches the world through.
- Secret
Value - A secret’s value. Its
Debugnever shows it. - Secrets
- The values of a run’s secret and input slots, by slot name.
- Status
- A run as it stands.
- System
Clock - The system’s clock.
- Tagged
- A resource a cloud holds with Isoloom’s tags.
Enums§
- Error
- Why an operation of the runner failed.
- Error
Code - The stable code of a failure, the same in the Rust API and the event stream (
error.code). - Event
Kind - What happened.
- Machine
State - A machine’s state, as its engine reports it.
- Occupied
- What
updoes when the run already exists. - Op
- An operation on a run.
- Park
- How a run is parked: the manifest’s
lifecycle.park.pauseor.shutdownsteps. - Ready
State - Where a ready condition stands.
- RunState
- Where a run stands.
- Step
State - Where a step stands.
- Stream
- Which output of a tool a line came from.
- Warning
Code - What kind of warning a
warningevent is, the same in the Rust API and the event stream.
Constants§
- EVENTS_
VERSION - The event stream’s version.
Traits§
- Clock
- Where the runner reads the time (event and record timestamps).
- Events
- Where an operation’s events go. Implemented for closures (
|e: &Event| …). - Hooks
- Hooks around a run’s lifecycle edges; each defaults to doing nothing.
- Host
Ports - The host’s ports.
Functions§
- events_
schema - The JSON Schema of one event.
- load_
manifest - Reads and checks the manifest at
path. - rfc3339
timeasYYYY-MM-DDTHH:MM:SSZ(UTC, whole seconds; before 1970 reads as 1970).- this_
host - This build’s host operating system.