Terminal: die Bruecke ueberlebt jetzt ein Deployment
Aus dem Gesamt-Review, und der erste Befund ist der, der still zugeschlagen haette. `terminal` lebt im Netz-Namensraum von `queue-provisioning`, und ein Prozess bleibt in dem Namensraum, in dem er gestartet ist. `update.sh` startet den Hub neu — danach lauscht die Bruecke in einem, den es nicht mehr gibt. Nichts meldet dabei einen Fehler: `docker compose ps` sagt weiter "healthy", weil die Lebendpruefung ueber Loopback INNERHALB des verwaisten Namensraums laeuft. Nach aussen antwortet nginx mit 502, und der Betreiber liest "Keine Verbindung — laeuft der Terminal-Dienst?", waehrend der Dienst behauptet, es gehe ihm gut. Genau dieselbe Falle, die zwei Bloecke tiefer schon fuer vpn-dns/vpn-gateway behandelt ist; die Bruecke fehlte in der Behandlung. Nachgemessen statt geglaubt: Hub neu gestartet -> Docker sagt "healthy", curl aus dem Namensraum bekommt gar keine Antwort. Nach `restart terminal`: 200. Und ein zweiter Ausrollfehler daneben: gebaut wurde nur `app`. `docker compose up -d` baut nur Images, die es noch GAR NICHT gibt — beim ersten Ausrollen faellt das nicht auf, danach nie wieder. Eine Aenderung an docker/terminal/ saehe ausgeliefert aus, und es liefe das alte Image. Ausserdem: - Die Meldung zu 4502 zaehlte zwei Ursachen auf, der Code deckt fuenf. Die Bruecke schickt 4502 fuer JEDE gescheiterte Anmeldung, auch fuer einen abgewiesenen Schluessel — und das ist der wahrscheinlichste Fall, wenn ein Host neu aufgesetzt wurde. "antwortet nicht" war dort schlicht falsch: die Maschine hat geantwortet und abgelehnt. Titel und Text legen sich nicht mehr fest. - R19: der Kommentar an der Kopfzeile der Spalte nannte "Berechtigung, Betriebsbereitschaft" als Grund, warum der Knopf nicht ueberall steht. Letzteres entscheidet seit dem Entsperren nichts mehr, und zwanzig Zeilen tiefer begruendete der Kommentar am Knopf ausfuehrlich das Gegenteil. - Der Kommentar am Retry-Knopf erklaerte die Reihenfolge von .hidden gegen .inline-flex fuer zu unsicher, waehrend die Buehne dreissig Zeilen hoeher genau darauf baut. Tailwind gibt .hidden als letzte Display-Klasse aus; der wahre Grund fuer den Wrapper ist, dass die Klassen des Knopfes aus einem geteilten Bauteil kommen. - REDIS_URL stand fest auf Datenbank 1, waehrend PHP REDIS_CACHE_DB liest. Wer die anfasst, legt auf der einen Seite ab, wo die andere nicht sucht. 2507 Tests gruen, compose config und bash -n sauber. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>main
parent
f64a564c40
commit
4fc1ccd3db
|
|
@ -440,7 +440,11 @@ after="$(git rev-parse HEAD)"
|
|||
# The image is only rebuilt when its definition changed — minutes versus seconds.
|
||||
if ! git diff --quiet "$base" "$after" -- docker/ 2>/dev/null; then
|
||||
phase image "Rebuilding the image"
|
||||
docker compose build --quiet app
|
||||
# `terminal` mitbauen, nicht nur `app`: `docker compose up -d` weiter unten
|
||||
# baut nur Images, die es noch GAR NICHT gibt. Beim ersten Ausrollen fällt
|
||||
# das nicht auf, danach nie wieder — eine Änderung an docker/terminal/
|
||||
# sähe ausgeliefert aus, und es liefe das alte Image.
|
||||
docker compose build --quiet app terminal
|
||||
# Recreate now, not at the end: everything below runs INSIDE this container,
|
||||
# and an update that changes the PHP runtime would otherwise install and
|
||||
# migrate under the old one.
|
||||
|
|
@ -526,6 +530,23 @@ docker compose up -d
|
|||
# keep running the code from before the update.
|
||||
docker compose restart queue queue-provisioning scheduler reverb
|
||||
|
||||
# Und die Terminal-Brücke gleich hinterher, aus demselben Grund wie die beiden
|
||||
# VPN-Dienste unten: sie lebt im Netz-Namensraum des Provisioning-Containers
|
||||
# (`network_mode: "service:queue-provisioning"`), und ein Prozess bleibt in dem
|
||||
# Namensraum, in dem er gestartet ist. Nach der Zeile darüber lauscht sie in
|
||||
# einem, den es nicht mehr gibt.
|
||||
#
|
||||
# Nichts meldet dabei einen Fehler — im Gegenteil: `docker compose ps` sagt
|
||||
# weiter „healthy", weil die Lebendprüfung über die Loopback-Adresse INNERHALB
|
||||
# des verwaisten Namensraums läuft. Nach außen antwortet nginx mit 502, und der
|
||||
# Betreiber liest „Keine Verbindung — läuft der Terminal-Dienst?", während der
|
||||
# Dienst behauptet, es gehe ihm gut.
|
||||
#
|
||||
# Ohne Profil-Abfrage, anders als unten: `terminal` hängt an keinem Profil und
|
||||
# läuft immer. Auf wg0 wartet sie nicht — sie bindet 8082 im Namensraum, nicht
|
||||
# die Tunneladresse; die braucht sie erst, wenn jemand ein Ticket einlöst.
|
||||
docker compose restart terminal
|
||||
|
||||
# AFTER the hub, always. Both VPN services live in the provisioning container's
|
||||
# network namespace, and a process holds the namespace it started in — so once
|
||||
# the hub is restarted they are listening inside one that no longer exists.
|
||||
|
|
|
|||
|
|
@ -210,9 +210,11 @@ services:
|
|||
restart: unless-stopped
|
||||
network_mode: "service:queue-provisioning"
|
||||
environment:
|
||||
# Datenbank 1 — dieselbe, auf der `Redis::connection('cache')` das Ticket
|
||||
# ablegt (REDIS_CACHE_DB, Vorgabe 1).
|
||||
REDIS_URL: "redis://redis:6379/1"
|
||||
# Dieselbe Datenbank, auf der `Redis::connection('cache')` das Ticket
|
||||
# ablegt. Der Wert kam aus derselben .env-Stelle wie bei PHP, statt fest
|
||||
# auf 1 zu stehen: sonst legt die eine Seite ab, wo die andere nicht sucht,
|
||||
# sobald jemand REDIS_CACHE_DB anfasst — und das Ticket wäre einfach nie da.
|
||||
REDIS_URL: "redis://redis:6379/${REDIS_CACHE_DB:-1}"
|
||||
# phpredis stellt jedem Schluessel dieser Verbindung
|
||||
# `database.redis.options.prefix` voran. Der ist aus APP_NAME abgeleitet
|
||||
# (Str::slug(APP_NAME).'-database-') und fuer PHP unsichtbar, fuer jeden
|
||||
|
|
|
|||
|
|
@ -158,9 +158,14 @@ return [
|
|||
'title' => 'Ticket nicht mehr gültig',
|
||||
'note' => 'Ein Ticket gilt dreißig Sekunden und genau einmal. Dieses war abgelaufen oder schon eingelöst — ein neuer Anlauf stellt ein frisches aus.',
|
||||
],
|
||||
// Absichtlich ohne Festlegung: die Brücke schickt 4502 für JEDE
|
||||
// gescheiterte Anmeldung — Maschine still, Fingerabdruck abweichend,
|
||||
// Schlüssel abgewiesen, Zeitüberschreitung. „Antwortet nicht" wäre
|
||||
// im häufigsten Fall schlicht falsch: nach einem frisch aufgesetzten
|
||||
// Host antwortet sie sehr wohl und lehnt ab.
|
||||
'unreachable' => [
|
||||
'title' => ':host antwortet nicht',
|
||||
'note' => 'Die Anmeldung über den Tunnel ist gescheitert: entweder ist die Maschine nicht erreichbar, oder ihr Fingerabdruck weicht von dem hinterlegten ab. Was davon, steht im Protokoll des Terminal-Dienstes — nicht in diesem Fenster.',
|
||||
'title' => 'Keine Sitzung auf :host',
|
||||
'note' => 'Die Brücke kam nicht bis zur Kommandozeile: die Maschine antwortet nicht, ihr Fingerabdruck weicht vom hinterlegten ab, oder sie hat den Flottenschlüssel abgewiesen — Letzteres ist das Wahrscheinlichste, wenn der Host neu aufgesetzt wurde. Was davon, steht im Protokoll des Terminal-Dienstes; dieses Fenster erfährt es absichtlich nicht.',
|
||||
],
|
||||
'retry' => 'Neu verbinden',
|
||||
],
|
||||
|
|
|
|||
|
|
@ -159,9 +159,13 @@ return [
|
|||
'title' => 'Ticket no longer valid',
|
||||
'note' => 'A ticket lasts thirty seconds and works exactly once. This one had expired or was already redeemed — another attempt issues a fresh one.',
|
||||
],
|
||||
// Deliberately non-committal: the bridge sends 4502 for EVERY failed
|
||||
// sign-in — machine silent, fingerprint mismatch, key refused,
|
||||
// timeout. "Not answering" would be plain wrong in the likeliest
|
||||
// case: after a host is rebuilt it answers and refuses.
|
||||
'unreachable' => [
|
||||
'title' => ':host is not answering',
|
||||
'note' => 'Signing in through the tunnel failed: either the machine is unreachable, or its fingerprint differs from the one on file. Which of the two is in the terminal service log, not in this window.',
|
||||
'title' => 'No session on :host',
|
||||
'note' => 'The bridge never reached a command line: the machine is not answering, its fingerprint differs from the one on file, or it refused the fleet key — the last is the likeliest once a host has been rebuilt. Which of them is in the terminal service log; this window is deliberately not told.',
|
||||
],
|
||||
'retry' => 'Reconnect',
|
||||
],
|
||||
|
|
|
|||
|
|
@ -102,11 +102,13 @@
|
|||
terminal.js an; ein `onclick` im Markup wäre Verhalten an der
|
||||
Stelle, an der sonst nur Gestalt steht.
|
||||
|
||||
`hidden` sitzt am Wrapper, nicht am Knopf: `x-ui.button` bringt
|
||||
`inline-flex` mit, und beide Klassen haben dieselbe Spezifität —
|
||||
wer gewinnt, entschiede dann die Reihenfolge im Stylesheet. Dieselbe
|
||||
Falle wie bei `size-4`/`size-5` in R18, und sie ist hier mit einem
|
||||
zusätzlichen Element billiger umgangen als nachgewiesen. --}}
|
||||
`hidden` sitzt am Wrapper, nicht am Knopf. Nicht, weil es dort nicht
|
||||
wirkte — Tailwind gibt `.hidden` als letzte der Display-Klassen aus
|
||||
und schlägt `inline-flex` damit —, sondern weil die Klassen des
|
||||
Knopfes aus einem geteilten Bauteil kommen und an dieser Stelle
|
||||
niemandem gehören. Die Bühne selbst trägt `hidden` und `flex`
|
||||
durchaus gemeinsam: das ist ein Element, das hier steht und hier
|
||||
bleibt. --}}
|
||||
<div data-stage-retry @class(['hidden' => $problem !== 'unknown'])>
|
||||
<x-ui.button data-stage-retry-button variant="secondary" size="sm">
|
||||
{{ __('hosts.terminal.stage.retry') }}
|
||||
|
|
|
|||
|
|
@ -82,10 +82,16 @@
|
|||
<th class="px-4 py-3 font-semibold">{{ __('hosts.col.capacity') }}</th>
|
||||
<th class="px-4 py-3 font-semibold">{{ __('hosts.col.status') }}</th>
|
||||
{{-- Leer mit Absicht: die Spalte trägt nur den
|
||||
Terminal-Knopf, und der ist nicht auf jeder
|
||||
Zeile da (Berechtigung, Betriebsbereitschaft).
|
||||
Eine Beschriftung, die unabhängig davon immer
|
||||
dastünde, wäre eine falsche Zusage. --}}
|
||||
Terminal-Knopf, und der ist nicht für jeden da
|
||||
(`hosts.manage`). Eine Beschriftung, die
|
||||
unabhängig davon immer dastünde, wäre eine
|
||||
falsche Zusage.
|
||||
|
||||
Hier stand einmal „Berechtigung,
|
||||
Betriebsbereitschaft" — Letzteres entscheidet
|
||||
seit dem Entsperren des Knopfes nichts mehr,
|
||||
und zwanzig Zeilen tiefer begründet der
|
||||
Kommentar am Knopf ausführlich das Gegenteil. --}}
|
||||
<th class="px-4 py-3 font-semibold"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
|
|
|||
Loading…
Reference in New Issue