Gate 0.0.27: de statuskaart loog na elke herstart. Relay 0.5.4: Status naar rechts

De melding van de gebruiker was "Not answering" met [Errno 111] Connection refused, terwijl de
gateway van buiten gewoon werkte. Het was een startvolgorde en geen storing:

1. de compose laat `server` ná de agent starten (depends_on: [agent]), want nginx lost
   http://agent:8000 bij het starten op en breekt af op een onbekende naam;
2. build_status draait meteen bij het opstarten, vóór de eerste WAKE.wait;
3. de branch die de meting uitstelt hing alleen aan `reloaded`, en die is bij een herstart met een
   ongewijzigd certificaat False, dus er werd gemeten;
4. nginx luisterde toen nog niet op 50022;
5. en het bleef staan, want de volgende meting kwam pas na SELF_CHECK_INTERVAL.

Twee reparaties, elk voor een helft. De eerste ronde van een proces meet niet maar zet `pending`
(die toestand heeft geen `at`, dus de ronde daarna meet gewoon), en na een mislukking wordt er na
GATE_SELF_CHECK_RETRY (60s) opnieuw gemeten in plaats van na 300. Een geslaagde meting blijft op
300, want dat is de waarde die zelden verandert en die het activiteitenlog belast.

De fout zat er sinds de zelfcontrole bestaat (27-08-2026) en was alleen binnen vijf minuten na een
herstart te zien. Mutatie-getest: de eerste-rondebranch uitschakelen laat twee tests omvallen, de
snelle herhaling uitschakelen precies één.

Relay: de statuswidget staat nu rechts in de bovenste rij en heet Status in plaats van Relay, gelijk
aan Electrum Gate. De twee pagina's zijn één app store en horen op dezelfde manier te lezen, en dit
is de widget die als enige begrijpelijk is zonder buur. De id's relay-state en relay-sub blijven.

Suite groen: 54 + 28 + 65 + 39 + 43 + 22 + 115.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Harmen
2026-08-31 12:14:37 +02:00
co-authored by Claude Opus 5
parent 63f1557713
commit 5ee21b10e7
8 changed files with 234 additions and 15 deletions
+102
View File
@@ -24,6 +24,7 @@ import sys
# Zelfde reden als in test_agent_certificates.py: geen .pyc naast de app-map.
sys.dont_write_bytecode = True
import json # noqa: E402
import os # noqa: E402
import socket # noqa: E402
import ssl # noqa: E402
@@ -315,6 +316,106 @@ def test_ronde_meet_niet_elke_keer(agent, u):
server.close()
def test_eerste_ronde_en_snelle_herhaling(agent, u):
"""De startrace, en de rem die een mislukking vasthield.
AANLEIDING (gemeld door de gebruiker, 31-08-2026): na elke herstart of update
las de statuskaart vijf minuten lang "Not answering" met
`[Errno 111] Connection refused`, terwijl de gateway van buiten gewoon werkte.
De oorzaak was een startvolgorde en geen storing. De compose laat `server` ná
de agent starten (`depends_on: [agent]`), en `build_status` draait meteen bij
het opstarten. De allereerste meting valt dus gegarandeerd in het gat waarin
nginx nog niet luistert. Dat was op zichzelf te verdragen; het probleem was dat
de volgende meting pas na SELF_CHECK_INTERVAL kwam.
Twee dingen worden hier vastgelegd, en ze repareren allebei een helft:
1. de eerste ronde van een proces meet niet, ook niet als het certificaat
ongewijzigd is (dus met `reloaded` op False, en dát is het geval dat de
bestaande toets hierboven NIET raakt: daar is de eerste ronde `pending`
omdat het certificaat net gekozen is);
2. na een mislukking wordt er na SELF_CHECK_RETRY opnieuw gemeten en niet pas
na SELF_CHECK_INTERVAL.
"""
with tempfile.TemporaryDirectory() as tmp:
bron = os.path.join(tmp, "certs")
os.makedirs(bron)
paar = openssl_paar(bron, "gate", "gate.example.org")
if not paar:
print("OVERGESLAGEN: geen openssl, dus de startrace is niet getoetst")
return
cert, key = paar
agent.SOURCES = [("Own folder", bron)]
agent.ELECTRUM_HOST = ""
agent.STATE_DIR = tmp
agent.CONFIG_DIR = os.path.join(tmp, "config")
agent.SELECTED_FILE = os.path.join(agent.CONFIG_DIR, "selected-cert")
agent.STATUS_FILE = os.path.join(tmp, "status.json")
agent.CERT_CONF = os.path.join(tmp, "cert.conf")
agent.RELOAD_FLAG = os.path.join(tmp, "reload")
agent.STREAM_LOG = os.path.join(tmp, "stream.log")
agent.SESSIONS_FILE = os.path.join(tmp, "sessions")
agent.SELF_CHECK_INTERVAL = 300
agent.SELF_CHECK_RETRY = 60
server = TlsServer(cert, key)
try:
richt(agent, server.port)
def ronde():
status = agent.build_status([])
agent.write_status(status)
return status
ronde() # certificaat gekozen: pending via reloaded
tweede = ronde()["tls"]["self_check"]
u.check("de meting lukt zodra het certificaat staat",
tweede.get("state") == "ok", "gaf %r" % tweede)
# Nu doen alsof het proces opnieuw begint, met een ONGEWIJZIGD
# certificaat. Vóór de reparatie mat hij hier gewoon door.
agent.EERSTE_RONDE = True
herstart = ronde()["tls"]["self_check"]
u.check("de eerste ronde na een herstart meet niet",
herstart.get("state") == "pending", "gaf %r" % herstart)
u.check("en zonder `at`, zodat de volgende ronde wél meet",
"at" not in herstart, "gaf %r" % herstart)
derde = ronde()["tls"]["self_check"]
u.check("de ronde daarna meet wel",
derde.get("state") == "ok", "gaf %r" % derde)
# De tweede helft: een mislukking mag niet vijf minuten blijven staan.
# De opgeslagen meting met de hand op 'failed' zetten, 90 seconden oud:
# dat is meer dan SELF_CHECK_RETRY en minder dan SELF_CHECK_INTERVAL.
def zet_vorige(state, ouderdom):
with open(agent.STATUS_FILE, "r", encoding="utf-8") as f:
status = json.load(f)
status["tls"]["self_check"] = {
"at": int(time.time()) - ouderdom, "state": state,
}
with open(agent.STATUS_FILE, "w", encoding="utf-8") as f:
json.dump(status, f)
zet_vorige("failed", 90)
na_fout = ronde()["tls"]["self_check"]
u.check("na een mislukking wordt er binnen een minuut opnieuw gemeten",
na_fout.get("state") == "ok", "gaf %r" % na_fout)
# En de tegenproef, want anders toetst het bovenstaande alleen dat er
# ooit gemeten wordt: een GESLAAGDE meting van dezelfde ouderdom hoort
# juist te blijven staan.
zet_vorige("ok", 90)
na_goed = ronde()["tls"]["self_check"]
u.check("een geslaagde meting van 90 seconden oud blijft staan",
na_goed.get("at") == int(time.time()) - 90,
"gaf %r" % na_goed)
finally:
server.close()
def main():
u = Uitslag()
agent = load_agent()
@@ -325,6 +426,7 @@ def main():
test_eigen_sessies_uit_het_log(agent, u)
# Als laatste, want deze verzet de module-instellingen naar een tijdelijke map.
test_ronde_meet_niet_elke_keer(agent, u)
test_eerste_ronde_en_snelle_herhaling(agent, u)
return u.rapport()