feat: der Monitor fragt das Panel, wenn eine Abfrage nicht ausreicht
gamedig FRAGT einen Server. Ein startendes Modpack antwortet nicht, und
nach zwei Fehlversuchen stand im Alarm-Kanal eine Ausfallmeldung, obwohl
niemand etwas kaputtgemacht hat -- am ATM10 des Betreibers gemessen jedes
Mal 306 Sekunden Fehlalarm.
Das d4rk_gameserver-Panel SIEHT den Container, statt ihn zu fragen. Neu
ist deshalb `src/panel.js`: Zustand, Fertig-Merkmal, RAM und CPU kommen
als Zusatzauskunft dazu, einmal je Durchlauf abgerufen und 20 Sekunden
zwischengespeichert.
Was das im Alarm-Kanal aendert:
startet gerade kein Alarm, aber nur innerhalb einer Gnadenfrist von
15 Minuten. Ohne diese Grenze fraesse die Anbindung
genau den Alarm, fuer den es sie gibt -- `bereit`
wird nie von allein wahr, ein haengender Start bliebe
sonst fuer immer stumm.
im Panel gestoppt kein Alarm (Code 0/143/137)
abgestuerzt ALARM, mit Code im Embed
OOM-Kill ALARM, und beim Namen genannt statt als Absturz
getarnt -- bei Modpacks die haeufigste Ursache
Container weg ALARM
Der Zaehler wird beim Unterdruecken NICHT zurueckgesetzt: ein bereits
gemeldeter Ausfall bleibt gemeldet, sonst verschluckt ein Stopp im Panel
die spaetere "wieder online"-Entwarnung und im Kanal bliebe ein Alarm
ohne Aufloesung stehen.
ES IST DURCHGEHEND OPTIONAL. Adresse und Zeichen stehen im Webinterface
unter "Server" und NICHT in der .env; ohne Eintrag -- und ebenso, wenn
das Panel nicht antwortet -- verhaelt sich der Monitor exakt wie vorher.
Das Zeichen darf nur lesen, damit auch ein verlorenes niemandem einen
Server stoppen kann.
`tools/panel-pruefen.mjs` prueft die Logik ohne Discord und ohne
Datenbank; deshalb laedt panel.js die Einstellungen erst beim Aufruf.
This commit is contained in:
@@ -101,6 +101,7 @@ auf nichts mehr: keine Posts, keine Hintergrund-Prüfungen.
|
||||
| Funktion | Was sie macht |
|
||||
|---|---|
|
||||
| 🎮 **Game-Server-Monitor** | DiscordGSM-Stil: pro Server ein Live-Embed (🟢/🔴, Name im Spiel, 🔒 bei Passwort oder Whitelist, Spieler-Balken, Map, Version, Mod-Anzahl, Spieler-Liste, Knöpfe für Verbinden, Mod-Download und frei eintragbare Links wie Regeln oder TeamSpeak, Ping); **alle Spiele, die gamedig kennt** — die Auswahl im Panel kommt aus der Bibliothek selbst, inklusive Standard-Port und Hinweis auf nötige Zugangs-Codes (Farming Simulator, Terraria) — plus FiveM + HTTP-Check; Down/Up-Alerts mit Ausfalldauer |
|
||||
| 🖥️ **Panel-Anbindung** *(optional)* | Trägt der Betreiber unter „Server" die Adresse des [d4rk_gameserver](https://git.d4rkst3r.de/D4rkst3r/d4rk_media)-Panels und ein Lese-Zeichen ein, weiß der Monitor mehr als eine Abfrage hergibt. gamedig **fragt** einen Server — ein startendes Modpack antwortet nicht und sieht dadurch aus wie ein Ausfall (am ATM10 gemessen: 306 Sekunden bis zum ersten Spieler). Das Panel **sieht** den Container: 🟡 „Startet gerade" statt 🔴 „Offline", kein Fehlalarm während des Hochfahrens, kein Alarm für einen im Panel gestoppten Server — aber sehr wohl einer für Absturz, OOM-Kill oder fehlenden Container. Dazu RAM und CPU im Embed. Ohne Eintrag ändert sich nichts, und antwortet das Panel nicht, ebenfalls nicht |
|
||||
| 🚜 **LS — Höfe & Preise** | Für Farming-Simulator-Server: Besitzkarte auf dem Luftbild des Servers (Parzellen nach Hof eingefärbt, Fläche = Hektar, Fahrzeuge und Spieler live), die Preiskurven übers Jahr mit „jetzt verkaufen oder warten", dazu **je Hof ein Embed**, das sich selbst aktualisiert: Land, Fuhrpark mit Wert und Betriebsstunden, was in die Werkstatt muss, was gewaschen gehört und was geladen ist. Dazu die **Modliste**: der Bot meldet, was neu, aktualisiert oder entfernt wurde, und auf `/mods/<server>` steht jeder Mod einzeln zum Laden — durch den Bot hindurch, damit der Zugangs-Code des Spielservers nicht in einem Link landet. **Nicht möglich:** welche Frucht auf welchem Feld steht, und Tierställe — beides gibt der Feed nicht her (siehe [docs/ls-feed.md](docs/ls-feed.md)) |
|
||||
|
||||
### Server & Technik
|
||||
|
||||
@@ -8,6 +8,7 @@ import { brandFooter, botStatusText } from '../runtime-settings.js';
|
||||
import { brandEmbed } from '../embeds.js';
|
||||
import { moduleEnabled } from '../modules.js';
|
||||
import { tuning, tuningMs, everyTuned } from '../tuning.js';
|
||||
import { panelStatus, panelEintrag, zustandText, lastText, startetNoch, gewolltAus } from '../panel.js';
|
||||
|
||||
const GREEN = 0x23a55a;
|
||||
const RED = 0xf23f43;
|
||||
@@ -257,8 +258,14 @@ export function buildServerEmbed(r) {
|
||||
// nicht verlässlich, und dann stünde das Steuerzeichen wörtlich da.
|
||||
kopf.push(`*${r.spielname.trim().slice(0, 200)}*`);
|
||||
}
|
||||
// Wenn GameDig nichts erreicht hat, das Panel aber sagt, dass der
|
||||
// Container laeuft und noch startet: DAS ist die richtige Auskunft. Ein
|
||||
// Modpack braucht Minuten bis zum ersten Spieler -- am ATM10 des
|
||||
// Betreibers 306 Sekunden -- und ein rotes "Offline" waere in dieser Zeit
|
||||
// schlicht falsch.
|
||||
const ausPanel = r.online ? null : zustandText(r.panel);
|
||||
kopf.push([
|
||||
r.online ? '🟢 **Online**' : '🔴 **Offline**',
|
||||
r.online ? '🟢 **Online**' : (ausPanel ?? '🔴 **Offline**'),
|
||||
zugang.grund ? `🔒 ${zugang.grund}` : null,
|
||||
r.online && r.ping != null ? `${r.ping} ms` : null,
|
||||
].filter(Boolean).join(' · '));
|
||||
@@ -276,6 +283,12 @@ export function buildServerEmbed(r) {
|
||||
r.mods != null ? { name: 'Mods', value: String(r.mods), inline: true } : null,
|
||||
]));
|
||||
|
||||
// Was nur das Panel weiss: was der Server gerade verbraucht.
|
||||
{
|
||||
const last = lastText(r.panel);
|
||||
if (last) embed.addFields({ name: 'Auf dem Wirt', value: last });
|
||||
}
|
||||
|
||||
if (r.online && r.playerNames.length > 0) {
|
||||
embed.addFields({
|
||||
name: `Wer drauf ist (${r.playerNames.length})`,
|
||||
@@ -318,6 +331,31 @@ async function handleAlerts(client, r) {
|
||||
alertState.set(r.id, { fails: 0, down: false, since: null });
|
||||
return;
|
||||
}
|
||||
|
||||
// KEIN ALARM, WENN DAS PANEL DEN ZUSTAND ERKLÄREN KANN.
|
||||
//
|
||||
// Das ist der eigentliche Gewinn der Anbindung. GameDig *fragt* einen
|
||||
// Server; ein startender antwortet nicht, und nach zwei Fehlversuchen
|
||||
// stünde hier eine Ausfallmeldung. Bei einem Modpack sind das jedes Mal
|
||||
// Minuten Fehlalarm — am ATM10 des Betreibers 306 Sekunden bis zum ersten
|
||||
// Spieler. Das Panel *sieht* den Container und weiß, dass er läuft und
|
||||
// nur das Fertig-Merkmal noch nicht durch ist.
|
||||
//
|
||||
// Zwei Fälle, eine Behandlung: nichts tun. Der Zähler bleibt dabei
|
||||
// ausdrücklich STEHEN statt zurückgesetzt zu werden, aus zwei Gründen:
|
||||
//
|
||||
// - Läuft die Gnadenfrist ab, ohne dass der Server fertig wird, greift
|
||||
// `startetNoch` nicht mehr und der Alarm kommt sofort statt erst nach
|
||||
// zwei weiteren Durchläufen.
|
||||
// - Ein bereits gemeldeter Ausfall bleibt gemeldet. Sonst verschluckt
|
||||
// ein Stopp im Panel die spätere „✅ wieder online"-Entwarnung, und im
|
||||
// Alarm-Kanal bliebe ein 🚨 ohne Auflösung stehen.
|
||||
//
|
||||
// Und was NICHT unterdrückt wird: ein Absturz, ein OOM-Kill und ein
|
||||
// fehlender Container. Das sind die Fälle, für die es den Kanal gibt —
|
||||
// hier hilft das Panel dem Alarm, statt ihn zu ersetzen.
|
||||
if (startetNoch(r.panel) || gewolltAus(r.panel)) return;
|
||||
|
||||
const fails = s.fails + 1;
|
||||
if (!s.down && fails >= tuning('monitor_fails')) {
|
||||
const channel = await client.channels.fetch(channelId).catch(() => null);
|
||||
@@ -343,6 +381,12 @@ export async function monitorTick(client) {
|
||||
|
||||
const results = await Promise.all(servers.map(queryServer));
|
||||
|
||||
// Die Zusatzauskunft vom Panel: EINMAL je Durchlauf, nicht je Server.
|
||||
// Sie ist optional -- ist nichts eingetragen oder antwortet das Panel
|
||||
// nicht, bleibt `panel` schlicht null und alles läuft wie vorher.
|
||||
const panelItems = await panelStatus();
|
||||
for (const r of results) r.panel = panelEintrag(panelItems, r);
|
||||
|
||||
// Verlauf für die Web-Seite festhalten + Cache fürs API
|
||||
for (const r of results) {
|
||||
lastResults.set(r.id, { ...r, checkedAt: Date.now() });
|
||||
|
||||
@@ -243,6 +243,13 @@ export const MODULES = [
|
||||
fields: [
|
||||
{ setting: 'status_channel_id', label: 'Status-Kanal', art: 'kanal', hint: 'hier steht das Live-Embed' },
|
||||
{ setting: 'server_alert_channel_id', label: 'Alarm-Kanal', art: 'kanal', hint: '🚨 down / ✅ wieder online' },
|
||||
// Optional. GameDig FRAGT einen Server -- ein startender antwortet
|
||||
// nicht, und das sieht aus wie ein Ausfall. Das Panel SIEHT den
|
||||
// Container und kann "startet gerade" von "aus" unterscheiden.
|
||||
{ setting: 'panel_url', label: 'Panel-Adresse', art: 'text',
|
||||
hint: 'optional, z. B. https://panel.d4rkst3r.de — unterscheidet „startet gerade" von „offline"' },
|
||||
{ setting: 'panel_zeichen', label: 'Panel-Zeichen', art: 'text',
|
||||
hint: 'im Panel unter „Zugang für Programme" anlegen; darf nur lesen' },
|
||||
],
|
||||
tool: { tab: 'server', label: 'Game-Server verwalten' },
|
||||
},
|
||||
|
||||
+164
@@ -0,0 +1,164 @@
|
||||
// Auskunft vom d4rk_gameserver-Panel.
|
||||
//
|
||||
// WARUM ÜBERHAUPT, wo der Monitor doch schon GameDig hat: weil GameDig einen
|
||||
// Server FRAGT, und ein Server, der noch startet, antwortet nicht. Für den
|
||||
// Monitor sieht das aus wie „offline" — und genau dann steht im Alarm-Kanal
|
||||
// eine Ausfallmeldung, obwohl niemand etwas kaputtgemacht hat.
|
||||
//
|
||||
// Ein Modpack braucht bis zum ersten Spieler Minuten; am ATM10 des Betreibers
|
||||
// gemessen sind es 306 Sekunden. Fünf Minuten Fehlalarm bei jedem Neustart.
|
||||
//
|
||||
// Das Panel weiß es besser, weil es den Container sieht statt ihn zu fragen:
|
||||
//
|
||||
// zustand was Docker sagt (running, exited, weg)
|
||||
// bereit ob das Fertig-Merkmal im Protokoll stand
|
||||
//
|
||||
// Damit lässt sich „läuft noch gar nicht" von „startet gerade" und von
|
||||
// „hängt" unterscheiden.
|
||||
//
|
||||
// ES IST ABSICHTLICH OPTIONAL. Ohne Adresse und Zeichen verhält sich der
|
||||
// Monitor exakt wie vorher, und wenn das Panel nicht antwortet, ebenfalls.
|
||||
// Eine Zusatzauskunft darf nie dazu führen, dass die Hauptauskunft ausfällt.
|
||||
|
||||
// Kurz zwischenspeichern: der Monitor fragt je Server einmal, und bei acht
|
||||
// Servern wären das acht Abrufe für dieselbe Antwort.
|
||||
let zwischen = { zeit: 0, items: null };
|
||||
const FRISCH_MS = 20_000;
|
||||
|
||||
/** Der ganze Panel-Status, oder null. Wirft nie. */
|
||||
export async function panelStatus() {
|
||||
// ERST HIER GELADEN und nicht oben im Modul: `runtime-settings` zieht die
|
||||
// Datenbank mit herein, und dann laesst sich keine der reinen Funktionen
|
||||
// unten mehr ohne better-sqlite3 pruefen. Eine Probe, die eine Datenbank
|
||||
// braucht, um eine Zeichenkette zu vergleichen, schreibt niemand.
|
||||
const { panelUrl, panelZeichen } = await import('./runtime-settings.js');
|
||||
const basis = panelUrl();
|
||||
const zeichen = panelZeichen();
|
||||
if (!basis || !zeichen) return null;
|
||||
|
||||
if (zwischen.items && Date.now() - zwischen.zeit < FRISCH_MS) return zwischen.items;
|
||||
|
||||
try {
|
||||
const steuer = AbortSignal.timeout(5000);
|
||||
const antwort = await fetch(`${basis.replace(/\/$/, '')}/api/oeffentlich/status`, {
|
||||
headers: { authorization: `Bearer ${zeichen}` },
|
||||
signal: steuer,
|
||||
});
|
||||
if (!antwort.ok) return null;
|
||||
const daten = await antwort.json();
|
||||
zwischen = { zeit: Date.now(), items: Array.isArray(daten.items) ? daten.items : [] };
|
||||
return zwischen.items;
|
||||
} catch {
|
||||
// Panel weg, Zeichen zurückgezogen, Netz hakt — der Monitor läuft
|
||||
// dann einfach ohne diese Zusatzauskunft weiter.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Den Panel-Eintrag zu einem Monitor-Server finden.
|
||||
*
|
||||
* ÜBER DEN PORT, nicht über den Namen. Namen sind Geschmackssache und stehen
|
||||
* in zwei Werkzeugen selten gleich; der Port ist eine Zahl, die beide kennen
|
||||
* und die nur einmal vergeben ist.
|
||||
*/
|
||||
export function panelEintrag(items, server) {
|
||||
if (!items?.length) return null;
|
||||
const port = Number(server?.port);
|
||||
if (Number.isFinite(port) && port > 0) {
|
||||
const treffer = items.find((x) => Number(x.port) === port);
|
||||
if (treffer) return treffer;
|
||||
}
|
||||
// Zweite Möglichkeit: gleicher Name, klein geschrieben. Hilft, wenn
|
||||
// jemand den Port im Monitor weggelassen hat.
|
||||
const name = String(server?.name ?? '').trim().toLowerCase();
|
||||
return items.find((x) => String(x.name).trim().toLowerCase() === name) ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wie lange ein Start dauern darf, bevor er als hängend gilt.
|
||||
*
|
||||
* Gemessen: ATM10 braucht 306 Sekunden bis zum ersten Spieler. Fünfzehn
|
||||
* Minuten sind knapp das Dreifache — genug Luft für ein größeres Pack auf
|
||||
* kaltem Cache, und kurz genug, dass ein hängender Start noch am selben
|
||||
* Abend auffällt.
|
||||
*
|
||||
* DIESE GRENZE IST NÖTIG, sonst frisst die Anbindung genau den Alarm, für den
|
||||
* es sie gibt: `bereit` wird nie von allein wahr. Ein Server, der beim Start
|
||||
* hängt, bliebe für immer „startet gerade" — und für immer ohne Meldung.
|
||||
*/
|
||||
export const STARTGNADE_MS = 15 * 60_000;
|
||||
|
||||
/** Läuft der Container und ist er noch in der zugestandenen Startzeit? */
|
||||
export function startetNoch(eintrag) {
|
||||
if (eintrag?.zustand !== 'running' || eintrag.bereit) return false;
|
||||
// Ohne Startzeit lässt sich nichts begrenzen. Das passiert nur, wenn
|
||||
// Dockers Zeitstempel unlesbar ist — dieselbe Antwort, die uns gerade
|
||||
// „running" gesagt hat. Ein kaputtes Feld ist kein hängender Server,
|
||||
// deshalb hier Nachsicht.
|
||||
if (!eintrag.seit) return true;
|
||||
return Date.now() - eintrag.seit < STARTGNADE_MS;
|
||||
}
|
||||
|
||||
/**
|
||||
* War das Ende gewollt?
|
||||
*
|
||||
* 0 heißt sauber beendet, 143 ist SIGTERM (`docker stop`) und 137 SIGKILL
|
||||
* nach abgelaufener Frist — alle drei entstehen, wenn jemand im Panel auf
|
||||
* Stopp drückt. Jeder andere Code ist ein Absturz, und `oom` heißt: der
|
||||
* Speicher war zu knapp. Genau dafür ist der Alarm-Kanal da.
|
||||
*/
|
||||
export function gewolltAus(eintrag) {
|
||||
if (eintrag?.zustand !== 'exited') return false;
|
||||
if (eintrag.oom) return false;
|
||||
// `code` fehlt bei einem älteren Panel. Dann ist die Frage nicht
|
||||
// beantwortbar, und im Zweifel gilt der Ausfall als echt.
|
||||
return eintrag.code === 0 || eintrag.code === 143 || eintrag.code === 137;
|
||||
}
|
||||
|
||||
/**
|
||||
* Was im Embed stehen soll, wenn GameDig nichts erreicht hat.
|
||||
*
|
||||
* Gibt null zurück, wenn das Panel nichts beizutragen hat -- dann bleibt es
|
||||
* beim schlichten „Offline".
|
||||
*/
|
||||
export function zustandText(eintrag) {
|
||||
if (!eintrag) return null;
|
||||
|
||||
if (eintrag.zustand === 'running' && !eintrag.bereit) {
|
||||
const s = eintrag.seit ? Math.round((Date.now() - eintrag.seit) / 1000) : null;
|
||||
const dauer = s == null ? '' : s < 90 ? ` (seit ${s} s)` : ` (seit ${Math.round(s / 60)} min)`;
|
||||
// Nach der Gnadenfrist ist „startet gerade" keine Auskunft mehr,
|
||||
// sondern eine Beschwichtigung. Dann steht hier, was Sache ist.
|
||||
return startetNoch(eintrag)
|
||||
? `🟡 **Startet gerade**${dauer}`
|
||||
: `🟠 **Startet seit ${dauer.replace(/^ \(seit |\)$/g, '')} und ist nicht fertig**`;
|
||||
}
|
||||
|
||||
if (eintrag.zustand === 'exited') {
|
||||
if (eintrag.oom) return '🔴 **Vom Speicher erschlagen** (out of memory)';
|
||||
if (gewolltAus(eintrag)) return '⚫ **Gestoppt** (im Panel)';
|
||||
// Auch ohne `code` (älteres Panel) landet man hier — dann ohne Zahl.
|
||||
return eintrag.code == null
|
||||
? '🔴 **Beendet**'
|
||||
: `🔴 **Abgestürzt** (Code ${eintrag.code})`;
|
||||
}
|
||||
|
||||
if (eintrag.zustand === 'weg') return '🔴 **Container fehlt**';
|
||||
return null;
|
||||
}
|
||||
|
||||
/** „1,2 GB / 7 GB · 14 %" — nur wenn das Panel Werte hat. */
|
||||
export function lastText(eintrag) {
|
||||
if (!eintrag || eintrag.zustand !== 'running') return null;
|
||||
const teile = [];
|
||||
if (eintrag.ram) {
|
||||
const gb = (b) => `${(b / 1024 ** 3).toFixed(1)} GB`;
|
||||
teile.push(eintrag.ram_grenze ? `${gb(eintrag.ram)} / ${gb(eintrag.ram_grenze)}` : gb(eintrag.ram));
|
||||
}
|
||||
// Das Panel liefert Dockers Kern-Prozent (200 = zwei Kerne voll). Für eine
|
||||
// Statusmeldung ist der Anteil an der Maschine die verständlichere Zahl --
|
||||
// „187 %" liest sich wie ein Fehler.
|
||||
if (eintrag.cpu != null && eintrag.kerne) teile.push(`${(eintrag.cpu / eintrag.kerne).toFixed(1)} % CPU`);
|
||||
return teile.length ? teile.join(' · ') : null;
|
||||
}
|
||||
@@ -325,3 +325,20 @@ export function repoIgnored(repo) {
|
||||
(r) => r.toLowerCase() === repo.toLowerCase()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* d4rk_gameserver-Panel — Adresse und Lesezeichen.
|
||||
*
|
||||
* BEIDES NUR HIER UND NICHT IN DER .env: der Betreiber soll es im Webinterface
|
||||
* eintragen können, so wie alles andere auch. Das Zeichen darf ausschließlich
|
||||
* lesen; selbst wenn es abhandenkommt, kann damit niemand einen Server stoppen.
|
||||
*
|
||||
* Leer = die Zusatzauskunft ist aus, und der Monitor verhält sich wie vorher.
|
||||
*/
|
||||
export function panelUrl() {
|
||||
return (getSetting('panel_url') || '').replace(/\/$/, '');
|
||||
}
|
||||
|
||||
export function panelZeichen() {
|
||||
return getSetting('panel_zeichen') || '';
|
||||
}
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
// Die Panel-Anbindung -- ohne Discord, ohne Datenbank, nur die Logik.
|
||||
//
|
||||
// Geprüft wird vor allem, was passieren muss, wenn das Panel NICHT da ist:
|
||||
// dann darf sich nichts anders verhalten als vorher. Eine Zusatzauskunft, die
|
||||
// den Monitor mit herunterreißt, wäre schlimmer als gar keine.
|
||||
//
|
||||
// node tools/panel-pruefen.mjs
|
||||
|
||||
import { panelEintrag, zustandText, lastText, startetNoch, gewolltAus, STARTGNADE_MS } from '../src/panel.js'
|
||||
|
||||
let fehler = 0
|
||||
const zeig = (gut, was, dazu = '') => {
|
||||
if (!gut) fehler++
|
||||
console.log(` ${gut ? 'ok ' : 'FEHL'} ${was}${dazu ? ' ' + dazu : ''}`)
|
||||
}
|
||||
|
||||
const items = [
|
||||
// So sieht ein Modpack in der Startphase aus: Container läuft, das
|
||||
// Fertig-Merkmal stand noch nicht im Protokoll, GameDig bekommt nichts.
|
||||
{ name: 'atm11-test', zustand: 'running', bereit: false, port: 26000,
|
||||
seit: Date.now() - 120_000, cpu: 187, kerne: 20, ram: 5_153_960_755, ram_grenze: 7_516_192_768 },
|
||||
{ name: 'nallheim', zustand: 'running', bereit: true, port: 26004,
|
||||
seit: Date.now() - 3_600_000, cpu: 20, kerne: 20, ram: 1_073_741_824, ram_grenze: 5_368_709_120 },
|
||||
{ name: 'alt', zustand: 'exited', bereit: false, port: 26010, code: 0, oom: false,
|
||||
seit: null, cpu: null, kerne: null, ram: null, ram_grenze: null },
|
||||
]
|
||||
|
||||
// Ein Start, der die Gnadenfrist gerissen hat -- der Fall, in dem die
|
||||
// Anbindung NICHT mehr beschwichtigen darf.
|
||||
const haengt = { ...items[0], seit: Date.now() - (STARTGNADE_MS + 60_000) }
|
||||
|
||||
console.log('=== Zuordnen')
|
||||
zeig(panelEintrag(items, { port: 26000 })?.name === 'atm11-test', 'über den Port')
|
||||
zeig(panelEintrag(items, { name: 'NALLHEIM' })?.name === 'nallheim', 'ersatzweise über den Namen, groß/klein egal')
|
||||
zeig(panelEintrag(items, { port: 9999 }) === null, 'unbekannter Port -> nichts')
|
||||
zeig(panelEintrag(null, { port: 26000 }) === null, 'ohne Panel-Daten -> nichts')
|
||||
zeig(panelEintrag([], { port: 26000 }) === null, 'leere Liste -> nichts')
|
||||
|
||||
console.log('')
|
||||
console.log('=== Was im Embed steht, wenn GameDig nichts erreicht')
|
||||
const startet = zustandText(items[0])
|
||||
zeig(/Startet gerade/.test(startet ?? ''), 'startender Server -> „Startet gerade"', startet)
|
||||
zeig(/2 min/.test(startet ?? ''), 'mit Dauer', startet)
|
||||
zeig(/Gestoppt/.test(zustandText(items[2]) ?? ''), 'gestoppter -> „Gestoppt"', zustandText(items[2]))
|
||||
zeig(zustandText(items[1]) === null, 'ein bereiter Server trägt hier nichts bei — da war GameDig schon erfolgreich')
|
||||
zeig(zustandText(null) === null, 'ohne Eintrag -> null, dann bleibt es beim schlichten „Offline"')
|
||||
|
||||
console.log('')
|
||||
console.log('=== Wann der Alarm UNTERDRÜCKT wird -- und wann nicht')
|
||||
zeig(startetNoch(items[0]) === true, 'startender Server in der Frist -> unterdrücken')
|
||||
zeig(startetNoch(items[1]) === false, 'ein fertiger Server ist kein Startvorgang')
|
||||
zeig(startetNoch(items[2]) === false, 'ein beendeter erst recht nicht')
|
||||
zeig(startetNoch(null) === false, 'ohne Panel -> nichts unterdrücken (Monitor wie vorher)')
|
||||
// Die wichtigste Zeile hier: `bereit` wird nie von allein wahr. Ohne diese
|
||||
// Grenze fräße die Anbindung genau den Alarm, für den es sie gibt.
|
||||
zeig(startetNoch(haengt) === false, 'nach der Gnadenfrist NICHT mehr unterdrücken -- sonst kommt der Alarm nie')
|
||||
zeig(/nicht fertig/.test(zustandText(haengt) ?? ''), 'und im Embed steht dann Klartext', zustandText(haengt))
|
||||
zeig(startetNoch({ ...items[0], seit: null }) === true, 'ohne Startzeit -> Nachsicht (kaputter Zeitstempel, kein hängender Server)')
|
||||
|
||||
const abgestuerzt = { ...items[2], code: 1 }
|
||||
const oomTot = { ...items[2], code: 137, oom: true }
|
||||
const altesPanel = { name: 'x', zustand: 'exited', bereit: false, port: 1 } // ohne code/oom
|
||||
zeig(gewolltAus(items[2]) === true, 'Code 0 -> jemand hat gestoppt, kein Alarm')
|
||||
zeig(gewolltAus({ ...items[2], code: 143 }) === true, 'Code 143 (SIGTERM) -> auch ein Stopp')
|
||||
zeig(gewolltAus(abgestuerzt) === false, 'Code 1 -> ABSTURZ, der Alarm muss durch')
|
||||
zeig(gewolltAus(oomTot) === false, 'OOM-Kill -> Alarm, auch bei Code 137')
|
||||
zeig(gewolltAus(altesPanel) === false, 'älteres Panel ohne `code` -> im Zweifel echter Ausfall')
|
||||
zeig(gewolltAus(items[0]) === false, 'ein laufender Server ist nicht "aus"')
|
||||
zeig(/Abgestürzt.*\(Code 1\)/.test(zustandText(abgestuerzt) ?? ''), 'Absturz steht mit Code im Embed', zustandText(abgestuerzt))
|
||||
zeig(/Speicher/.test(zustandText(oomTot) ?? ''), 'OOM wird benannt, nicht als Absturz getarnt', zustandText(oomTot))
|
||||
|
||||
console.log('')
|
||||
console.log('=== Last')
|
||||
const last = lastText(items[0])
|
||||
zeig(/4\.8 GB \/ 7\.0 GB/.test(last ?? ''), 'RAM mit Grenze', last)
|
||||
// 187 / 20 = 9.35, und toFixed(1) macht daraus 9.3 -- 9.35 liegt binär knapp
|
||||
// UNTER 9.35, also wird abgerundet. Hier steht der gemessene Wert, nicht der
|
||||
// erwartete; für eine Auslastungsanzeige ist das Zehntel ohnehin ohne Belang.
|
||||
zeig(/9\.3 % CPU/.test(last ?? ''), 'CPU als Anteil der MASCHINE, nicht Dockers 187 %', last)
|
||||
zeig(lastText(items[2]) === null, 'gestoppt -> keine Last')
|
||||
zeig(lastText(null) === null, 'ohne Eintrag -> nichts')
|
||||
|
||||
console.log('')
|
||||
console.log('=== Abschaltbarkeit')
|
||||
// Ohne Panel-Eintrag muss JEDE dieser Funktionen null liefern, sonst steht
|
||||
// im Embed plötzlich ein leeres Feld.
|
||||
zeig([zustandText(undefined), lastText(undefined), panelEintrag(undefined, {})].every((x) => x == null),
|
||||
'undefined überall -> überall null')
|
||||
|
||||
console.log('')
|
||||
console.log(fehler ? `${fehler} FEHLER` : 'alles grün')
|
||||
process.exitCode = fehler ? 1 : 0
|
||||
Reference in New Issue
Block a user