Skip to main content

Crate isothrum_runner

Crate isothrum_runner 

Source
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 dir = tempfile::tempdir()?;
let manifest = dir.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 run = runner.open(&manifest, Some(RunName::new("demo")?))?;
assert_eq!(run.manifest().environment.name.as_str(), "flag-portal");
assert_eq!(run.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 started = run.up(&opts, &events).await?;
let report = run.check(&opts, &events).await?;
run.park(Park::Shutdown, &events).await?;
run.resume(&opts, &events).await?;
run.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 ProcessRunner port. SystemProcesses starts 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); the fake feature’s fake::FakeProcesses answers from a test’s script instead.

Structs§

Adoption
A deployment the runner takes over (Runner::adopt).
AttachReport
What a hot-add produced: the access box brought up, and where to reach it.
CheckReport
Every check’s outcome.
CheckResult
One check’s outcome.
Event
One event of an operation.
HeldPort
A free port, held until dropped so it isn’t handed out twice while others are picked.
HookError
Why a hook failed, in its own words.
JsonLines
Events written as JSON, one per line, flushed each time.
LastOp
The last operation on a run and how it ended.
LoadedManifest
A manifest read from its file.
LoopbackPorts
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).
MachineStatus
One machine of the manifest, as it stands.
NetworkView
A network of the run.
Orphans
What a cloud still holds past its expiry that no recorded run covers.
OutputValue
An output’s value.
Place
Where a query looks: the regions (AWS) or the project (Google Cloud); the CLI’s own default when unset.
PublishedPort
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).
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).
RunView
What a hook sees of the run.
Runner
The runner.
RunnerConfig
How a runner is built: its home, and the ports it reaches the world through.
Running
A run: a manifest under a name, on a runner.
SecretValue
A secret’s value. Its Debug never shows it.
Secrets
The values of a run’s secret and input slots, by slot name.
ShellCommand
The command that opens an interactive shell into a machine: the runner builds it (it alone knows the runtime — the Compose project and files, the Vagrant define), the embedder runs it in its own terminal. Addresses the box by service or define name, never a guessed container.
Started
A run that came up (or resumed).
Status
A run as it stands.
SystemClock
The system’s clock.
Tagged
A resource a cloud holds with Isoloom’s tags.

Enums§

Error
Why an operation of the runner failed.
ErrorCode
The stable code of a failure, the same in the Rust API and the event stream (error.code).
EventKind
What happened.
MachineState
A machine’s state, as its engine reports it.
Occupied
What up does when the run already exists.
Op
An operation on a run.
Park
How a run is parked: the manifest’s lifecycle.park.pause or .shutdown steps.
ReadyState
Where a ready condition stands.
RunState
Where a run stands.
StepState
Where a step stands.
Stream
Which output of a tool a line came from.
WarningCode
What kind of warning a warning event 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.
HostPorts
The host’s ports.

Functions§

events_schema
The JSON Schema of one event.
load_manifest
Reads and checks the manifest at path.
rfc3339
time as YYYY-MM-DDTHH:MM:SSZ (UTC, whole seconds; before 1970 reads as 1970).
this_host
This build’s host operating system.

Type Aliases§

BoxFuture
A boxed future that can be sent between threads.
Result
The runner’s Result: its error is a Error unless said otherwise.