diff --git a/scripts/factory-edge-demo-live.test.sh b/scripts/factory-edge-demo-live.test.sh index 726d65347f..ba398b2812 100755 --- a/scripts/factory-edge-demo-live.test.sh +++ b/scripts/factory-edge-demo-live.test.sh @@ -168,4 +168,18 @@ grep -q '^FINDING: install-time preparation ran during the demo' <<<"${out}" || out="$(fresh; stage ok HYPERVISOR=hvf >/dev/null; demo needs-bootstrap HYPERVISOR=hvf)" && fail "an install that needs bootstrap passed" grep -q "^FINDING: the install asked for 'mvmctl bootstrap'" <<<"${out}" || fail "no bootstrap finding" +# ---- the one-command runner ---- +fresh +out="$(envrun ok factory-edge-run.sh HYPERVISOR=hvf)" || fail "the one-command run failed on a correct product: ${out}" +grep -q '^DEMO_RESULT=pass$' <<<"${out}" || fail "the one-command run printed no pass line: ${out}" +grep -q '^The demo passed in ' <<<"${out}" || fail "the one-command run did not say it passed: ${out}" +[ "$(ls "${work}"/state/m.* 2>/dev/null | wc -l | tr -d ' ')" = 0 ] || fail "the one-command run left staged machines behind" +fresh +out="$(envrun inspection-silent factory-edge-run.sh HYPERVISOR=hvf)" && fail "the one-command run passed although an inspection workload stopped: ${out}" +grep -q '^The demo did not pass' <<<"${out}" || fail "a failing one-command run did not say so: ${out}" +[ "$(ls "${work}"/state/m.* 2>/dev/null | wc -l | tr -d ' ')" = 0 ] || fail "a failing one-command run left staged machines behind" +fresh +out="$(envrun image-missing factory-edge-run.sh HYPERVISOR=hvf)" && fail "the one-command run passed with the image missing: ${out}" +grep -q 'could not be staged' <<<"${out}" || fail "a staging failure was not reported by the one-command run: ${out}" + echo "ok ($n checks per run)" diff --git a/scripts/factory-edge-demo.md b/scripts/factory-edge-demo.md new file mode 100644 index 0000000000..df97863997 --- /dev/null +++ b/scripts/factory-edge-demo.md @@ -0,0 +1,85 @@ +# The factory-edge demo + +Three small programs run side by side on one Mac, and each is allowed to reach +only its own destination. One of them asks for something it was not allowed, the +host refuses and records it, and the other two carry on. Then that program is +stopped and a fresh one takes its place. + +## Run it + +```sh +scripts/factory-edge-run.sh +``` + +It takes about 20 seconds. It starts the three programs, runs the demo against +them, and removes them again, even if the demo fails. Nothing is left running. + +You need an Apple Silicon Mac on macOS 26 or later with the demo set up (see +"Set up a new Mac" below). Ari's Mac is already set up. + +## What you will see + +The demo prints one line for each thing it checks. A run that works ends with +`DEMO_RESULT=pass`. + +1. The two inspection programs are working, each reaching only its own + destination. +2. The maintenance program asks for a destination it was not allowed. The host + refuses it, and the refusal can be read back afterwards. +3. The two inspection programs are not affected and keep working. +4. The audit record is checked and is intact. +5. The maintenance program is stopped and its session sealed. +6. A replacement starts as a fresh program and carries none of the old refusals. + +## If it does not pass + +The script stops at the first problem and prints a line starting `FAIL` or +`FINDING` that says what is missing. The usual causes: + +- **Not set up.** `mvmctl` is not installed, or the demo home does not exist, or + the test image is not in it. Follow "Set up a new Mac". +- **No network.** Every allowed destination is looked up when its program + starts, so the demo needs working internet and DNS. +- **Something left over.** Run `scripts/factory-edge-stage.sh --down`, then run it + again. + +## Set up a new Mac + +Do this once, from a checkout of this repository. It installs the release the +demo is verified against and prepares a home for it. The demo never installs or +prepares anything itself. + +```sh +MVM_VERSION=v0.28.0-rc.2 MVM_SKIP_BOOTSTRAP=1 sh install.sh +export MVM_HOME="$HOME/.mvm-demo" +mvmctl image pull curlimages/curl:8.21.0 +mvmctl run --image curlimages/curl:8.21.0 -- true +``` + +The last command builds the runtime the first time, which takes a few minutes. +The demo scripts use `~/.mvm-demo` by default, so nothing else needs setting. + +## Presenting it + +For a live presentation, start the programs ahead of time so the audience sees +only the demo itself: + +1. `scripts/factory-edge-stage.sh` about ten minutes before. Staging earlier makes + the audit record, and with it the readback, slower. +2. `scripts/factory-edge-demo-live.sh` for the live part, about 30 seconds. +3. `scripts/factory-edge-stage.sh --down` afterwards. + +After a reboot the staged programs are gone, so stage again. + +## What this does not show + +- The Studio application's own view. The demo drives the command line, and Studio + will do the same things through the `mvm-client` library. +- The real maintenance and inspection tasks. These are small scripted programs + over synthetic traffic. +- Launches under one second. A launch takes a few seconds on this release, and + the replacement in step 6 is a live launch. + +More detail, including what was measured and why launches are slow, is in the +factory-edge section of +`specs/contracts/host-interface-distributed-execution.md`. diff --git a/scripts/factory-edge-run.sh b/scripts/factory-edge-run.sh new file mode 100755 index 0000000000..1576c06b82 --- /dev/null +++ b/scripts/factory-edge-run.sh @@ -0,0 +1,54 @@ +#!/usr/bin/env bash +# Run the whole factory-edge demo with one command: stage the three workloads, +# run the demo against them, and remove them again, even if the demo fails. +# +# scripts/factory-edge-run.sh +# +# This is the version for someone who just wants to see it work. A presenter +# who wants no waiting on stage runs the two scripts separately instead: stage +# about ten minutes ahead with scripts/factory-edge-stage.sh, present with +# scripts/factory-edge-demo-live.sh, then remove with +# scripts/factory-edge-stage.sh --down. +# +# It assumes what the two scripts assume: an installed mvmctl and a prepared +# demo home. Nothing here installs or prepares anything. See +# scripts/factory-edge-demo.md for the one-time setup. +# +# Every setting of the two scripts is passed through (MVMCTL, MVM_HOME, +# HYPERVISOR, IMAGE and the rest). ATTEMPT_AFTER and INSPECT_EVERY default to 3 +# and 2 seconds here, so the maintenance workload makes its request soon after +# staging and the inspection workloads report often, and the run does not wait. +# A presenter staging ahead wants longer ones, because every lookup is a signed +# audit entry and the record grows for as long as the workloads run; a run of +# about a minute does not grow it meaningfully. +# +# The exit status is the demo's: 0 only if every check passed. +set -uo pipefail + +here="$(cd "$(dirname "$0")" && pwd)" +export ATTEMPT_AFTER="${ATTEMPT_AFTER:-3}" INSPECT_EVERY="${INSPECT_EVERY:-2}" + +# Remove the staged workloads on any exit, including an interrupt. +cleanup() { + "$here/factory-edge-stage.sh" --down >/dev/null 2>&1 || true +} +trap cleanup EXIT +trap 'exit 130' INT TERM + +t0=$SECONDS +echo "Step 1 of 2: staging the three workloads" +"$here/factory-edge-stage.sh" || { echo "The workloads could not be staged, so the demo was not run." >&2; exit 1; } + +staged_in=$((SECONDS - t0)) +echo +echo "Step 2 of 2: running the demo" +"$here/factory-edge-demo-live.sh" +status=$? + +echo +if [ "$status" -eq 0 ]; then + echo "The demo passed in $((SECONDS - t0)) seconds ($staged_in to stage, the rest to run). The staged workloads have been removed." +else + echo "The demo did not pass (see the line starting FAIL or FINDING above). The staged workloads have been removed." >&2 +fi +exit "$status" diff --git a/scripts/factory-edge-stage.sh b/scripts/factory-edge-stage.sh index d8b306fdfc..5f9ba2ec45 100755 --- a/scripts/factory-edge-stage.sh +++ b/scripts/factory-edge-stage.sh @@ -121,12 +121,16 @@ staged_at="$(python3 -I -c "from datetime import datetime, timezone; print(datet # An inspection workload's work: send a result to its granted destination, repeatedly. inspect() { echo "while true; do curl -sS -m 10 -o /dev/null https://$1/ >/dev/null 2>&1; sleep $INSPECT_EVERY; done"; } start() { #