fix(station): Manifest-Merge robust gegen Fehlform, Version pro Eintrag
Fixrunde 2 zu "Migrationsimages beim Deploy signieren":
- N1 (Important): das Merge-Skript im SSH-Heredoc von deploy.sh fing beim
Bestandsmanifest nur Lesefehler und kaputtes JSON ab (OSError/ValueError).
War die Datei gueltiges JSON mit falscher Form ("[]", "null", ein String,
oder ein images-Eintrag, der selbst kein Objekt ist), krachte .get() mit
AttributeError - und das VOR der Installation von
server.py/products.py/CLI/Unit und vor dem Neustart, liess einen Pi also
mit neuen Images, aber altem Stationscode zurueck. Jetzt wird alte wie
neue Manifest-Struktur konsequent auf isinstance(dict)/isinstance(list)
geprueft, jede Fehlform faellt auf ein leeres Manifest zurueck statt
abzustuerzen. Neues Szenario 21 deckt alle vier Faelle aus der
Review-Tabelle ab (kaputt, leer, falsch geformtes JSON, kaputter Eintrag).
- N2 (Important): das zusammengefuehrte Manifest hat "ninlight_version"
bisher blind vom neuen Manifest uebernommen, obwohl uebernommene
Alt-Eintraege (deren Images unveraendert liegen bleiben, siehe Fixrunde 1
I1) eine andere Firmware-Version tragen koennen - eine konkrete falsche
Version beim Protokollieren ist schlechter als das vorherige "unbekannt".
Die Version wandert jetzt PRO EINTRAG ins Manifest (deploy.sh erzeugt sie
dort, der Merge behaelt sie); das manifestweite Feld bleibt nur als
Bestandskompatibilitaet und wird nur gesetzt, wenn wirklich alle Eintraege
uebereinstimmen. server.py liest beim Ausliefern jetzt die Version DES
AUSGELIEFERTEN EINTRAGS (Station.image_version(target, variant) ersetzt
manifest_version()) und faellt nur auf das manifestweite Feld zurueck,
wenn der Eintrag selbst keine eigene traegt. test_server.py bekommt einen
neuen Test fuer den Vorrang des Eintrags vor dem manifestweiten Feld,
Szenario 14 in test_deploy.sh prueft jetzt zusaetzlich unterschiedliche
Versionen zwischen zusammengefuehrten Eintraegen und dass das
manifestweite Feld dann fehlt statt falsch zu sein. Die Spec
(docs/superpowers/specs/.../design.md, Abschnitt 4, lokal/nicht
versioniert) ist nachgezogen. Die dadurch ueberfluessig gewordene
"sorted(versions)[0]"-Stelle aus Fixrunde 1 ist entfallen.
- Kleinigkeit: die Remote-Meldung, wenn migrate/ von diesem Rechner leer
ist, behauptet nicht mehr "kein Schluesselverzeichnis" (das stimmt im
Fall "Verzeichnis vorhanden, aber leer" nicht) - sie nennt jetzt keinen
bestimmten Grund mehr, nur die Konsequenz.
- Kleinigkeit: --exclude=manifest.json ist jetzt --exclude=/manifest.json,
konsistent mit dem daneben verankerten --exclude=/migrate.
Alle drei Aenderungen (N1, N2, beide Kleinigkeiten) sind vor dem Fix per
Rot-Probe an einer Kopie von deploy.sh verifiziert: mit den urspruenglichen
Fehlern schlagen Szenario 21 (N1) bzw. Szenario 14 (N2) zuverlaessig fehl.
Bewusst nicht angefasst (Review-Vorgabe): der fehlende HOME-Testfall, und
dass der Merge kein os.path.exists() prueft.
This commit is contained in:
@@ -686,6 +686,9 @@ def test_range_resume_on_migration_image(migration_server):
|
||||
|
||||
|
||||
def test_version_comes_from_manifest(migration_server):
|
||||
"""Faellt auf das manifestweite Feld zurueck, wenn der Eintrag selbst keine eigene
|
||||
Version traegt - Bestandskompatibilitaet mit einem Manifest aus der Zeit vor
|
||||
Fixrunde 2 (N2)."""
|
||||
srv, imgdir = migration_server
|
||||
(imgdir / "migrate").mkdir(exist_ok=True)
|
||||
(imgdir / "migrate" / "g95_cw_dev.img").write_bytes(b"IMG")
|
||||
@@ -697,6 +700,32 @@ def test_version_comes_from_manifest(migration_server):
|
||||
assert st["recent"][-1]["version"] == "0.3.9"
|
||||
|
||||
|
||||
def test_version_prefers_the_delivered_entry_over_the_manifest_wide_field(migration_server):
|
||||
"""Fixrunde 2, N2: nach einem Merge in deploy.sh (Voll-Deploy mit nur einem der
|
||||
beiden Signierschluessel) koennen Eintraege im selben Manifest unterschiedliche
|
||||
Firmware-Versionen tragen - z.B. ein neu signiertes g95_cw_dev.img auf 1.2.0, aber
|
||||
ein unveraendert liegen gebliebenes aelteres g95_cw_prod.img auf 1.0.0. Der Server
|
||||
darf dann nicht das manifestweite Feld fuer beide protokollieren, sondern muss die
|
||||
Version DES AUSGELIEFERTEN Eintrags nehmen."""
|
||||
srv, imgdir = migration_server
|
||||
(imgdir / "migrate").mkdir(exist_ok=True)
|
||||
(imgdir / "migrate" / "g95_cw_dev.img").write_bytes(b"IMG-DEV")
|
||||
(imgdir / "migrate" / "g95_cw_prod.img").write_bytes(b"IMG-PROD")
|
||||
(imgdir / "migrate" / "manifest.json").write_text(json.dumps({
|
||||
# Manifestweites Feld absichtlich falsch/veraltet, damit ein Zurueckfallen
|
||||
# darauf im Test auffallen wuerde.
|
||||
"ninlight_version": "1.2.0",
|
||||
"images": [
|
||||
{"target": "g95_cw", "variant": "dev", "file": "g95_cw_dev.img",
|
||||
"ninlight_version": "1.2.0"},
|
||||
{"target": "g95_cw", "variant": "prod", "file": "g95_cw_prod.img",
|
||||
"ninlight_version": "1.0.0"},
|
||||
]}))
|
||||
srv.get("/qcx_firmware_-klyqa.lighting.cw-ww.g95_V1_ota_light.img")
|
||||
st = srv.status(lambda s: len(s["recent"]) >= 1)
|
||||
assert st["recent"][-1]["version"] == "1.0.0"
|
||||
|
||||
|
||||
def test_control_file_is_world_readable_even_under_a_restrictive_umask(tmp_path):
|
||||
"""Dieselbe 0600-Falle wie bei status.json (siehe
|
||||
test_state_file_is_world_readable_even_under_a_restrictive_umask oben), aber fuer
|
||||
|
||||
Reference in New Issue
Block a user