mirror of
https://github.com/openglow-org/forgefirm.git
synced 2026-09-29 01:21:16 -07:00
A stdlib-only daemon on the dev image (HTTP :8090) that runs the acceptance catalog against the machine from a self-contained page, keeps the append-only result log under /data/forgetest, and exports the release artifact the gate reads. Tests declare kind (auto / operator / live), hardware (api / takeover), coverage globs, prerequisites, and core membership; a test's domain fingerprint is the hash of the manifest files its globs select plus the platform and its own implementation, so a PASS stays valid exactly while nothing it covers changed. Campaign rules: a FAIL ends the campaign, the core (image health, kernel latch and drills, one live emission witness) is never inherited, invalidate-all forces a full campaign, no SKIP. Live tests need the operator acknowledgment and the physical arm press through the controller; takeover tests stop forgectrl for the duration with a crash-recoverable marker; the tool never touches the laser latch. Catalog v1: 24 tests ported from the proven bench drills with their recorded pass criteria (image, kernel K1-K3 and fire A/B/U, forgectrl API and logs, motion incl. dead-man, cooling, live laser, camera, update, cloud). The bench tab lists every scripts/bench tool and runs the board-side ones as subprocesses (takeover tools wrapped). 44 host unit tests, including the gate verification fixtures. Installed only by forgefirm-image-dev, with the bench scripts under /usr/share/forgetest/bench.
48 lines
2.1 KiB
Python
48 lines
2.1 KiB
Python
"""update.* - the A/B slot inventory and the firmware verification path."""
|
|
import os
|
|
import tempfile
|
|
|
|
from ..catalog import test
|
|
from .. import hw
|
|
|
|
_UPDATE_COVERS = [("forgectrl", "src/update.c"), ("forgectrl", "src/update.h")]
|
|
|
|
|
|
@test("update.slots-and-signature", title="Boot slots readable, unsigned/tampered archives refused",
|
|
subsystem="update", kind="auto", est_min=1,
|
|
covers=_UPDATE_COVERS, requires=["forgectrl.auth"],
|
|
description="/slots reports the A/B inventory consistent with `ffboot -l`; /update/status "
|
|
"answers; `fwup` refuses a garbage archive and a tampered signature against the "
|
|
"shipped release key. Nothing is written to any slot.")
|
|
def slots_and_signature(ctx):
|
|
fc = ctx.forgectrl
|
|
ev = ctx.evidence
|
|
st, slots = fc.get("/slots")
|
|
ctx.log("GET /slots -> %s %s", st, slots)
|
|
ctx.check(st == 200 and isinstance(slots, dict), "GET /slots -> %s", st)
|
|
ev["slots"] = slots
|
|
rc, out = hw.run(["ffboot", "-l"])
|
|
ev["ffboot_l_rc"] = rc
|
|
ctx.log("ffboot -l -> rc %s\n%s", rc, out.strip())
|
|
ctx.check(rc == 0, "ffboot -l failed (%s)", rc)
|
|
text = str(slots).lower()
|
|
ctx.check("forgefirm" in text or "slot" in text, "/slots does not look like a slot inventory")
|
|
|
|
st, us = fc.get("/update/status")
|
|
ctx.log("GET /update/status -> %s %s", st, us)
|
|
ctx.check(st == 200 and isinstance(us, dict) and "running" in us, "GET /update/status -> %s", st)
|
|
ctx.check(not us.get("running"), "an update is running")
|
|
|
|
key = "/etc/forgefirm/keys/forgefirm-release.pub"
|
|
ctx.check(os.path.exists(key), "release key %s missing", key)
|
|
with tempfile.NamedTemporaryFile(prefix="forgetest-", suffix=".fw", delete=False) as f:
|
|
f.write(b"this is not a firmware archive" * 64)
|
|
garbage = f.name
|
|
try:
|
|
rc, out = hw.run(["fwup", "-V", "-i", garbage, "-p", key], timeout=30)
|
|
ev["fwup_garbage_rc"] = rc
|
|
ctx.log("fwup -V garbage -> rc %s: %s", rc, out.strip()[:200])
|
|
ctx.check(rc != 0, "fwup accepted a garbage archive")
|
|
finally:
|
|
os.unlink(garbage)
|