From 5ab9f742c5645f77c31d0b82cf5b6d470d6457b6 Mon Sep 17 00:00:00 2001 From: Ari Date: Sat, 14 Nov 2026 16:51:22 -0800 Subject: [PATCH] test: run the factory-edge demo with one command, and add a short guide scripts/factory-edge-run.sh stages the three workloads, runs the demo against them and removes them again, even if the demo fails, and exits with the demo's status. It is for someone who just wants to see it work; a presenter still stages ahead and runs the two scripts separately. A run takes about 20 seconds on the demo Mac, down from 32 for the same steps. Two things made it slower than it needed to be. The staging script started the three workloads one after another, each waiting out its own launch, so they are now started together and each start is checked afterwards. And the built-in waits (a 10 second delay before the maintenance request and a 5 second lookup cadence) are shortened to 3 and 2 seconds in the one-command run, which is about a minute long and so does not grow the audit record meaningfully; a presenter staging ahead keeps the longer defaults. scripts/factory-edge-demo.md is a one-page guide in plain language: what it shows, the one command, what the output means, what to do when it does not pass, a one-time setup for a new Mac, and what the demo does not show. The hermetic test now also covers the runner: a clean run, a failing run that still removes the staged workloads, and a staging failure. --- scripts/factory-edge-demo-live.test.sh | 14 +++++ scripts/factory-edge-demo.md | 85 ++++++++++++++++++++++++++ scripts/factory-edge-run.sh | 54 ++++++++++++++++ scripts/factory-edge-stage.sh | 14 +++-- 4 files changed, 162 insertions(+), 5 deletions(-) create mode 100644 scripts/factory-edge-demo.md create mode 100755 scripts/factory-edge-run.sh 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() { #