install.sh: Sicherheitsnetz gilt jetzt fuer JEDES Update, nicht nur Schema-Aenderungen

Live gefunden: der 'Schema unveraendert -> kein Backup'-Kurzschluss aus
v1.0.3 liess einen rein CODE-seitigen Fehler im neuen Paket (z.B. ein
kaputtes app.py -- mit dem DB-Schema hat das nichts zu tun) komplett
ungeschuetzt durch. Backup + Health-Check + ggf. Rollback laufen jetzt
bei JEDEM Update einer bestehenden Installation, unabhaengig von
SCHEMA_VERSION -- die bleibt nur noch fuer den Wortlaut der Log-Meldung
relevant (In-Place-Update vs. Migration).

Zusaetzlich beim Rollback selbst: 'mv' durch 'cp -a' beim Zurueckspielen
ersetzt, plus die fehlgeschlagene Installation vorher separat beiseite
gelegt (/srv/tesm-failed-update-<timestamp>) statt geloescht -- vorher
wurde das Backup beim Zurueckspielen selbst zum neuen /srv/tesm und
verschwand dadurch als eigenstaendige Sicherung (widersprach der
eigenen Zusage 'Backup bleibt erhalten').

Live auf der Testbox in allen drei Pfaden verifiziert:
- Erfolgreiches Update: Backup erstellt, Health-Check bestanden,
  Backup geloescht, Daten erhalten.
- Migration (kuenstlich abweichende SCHEMA_VERSION, echte Struktur
  unveraendert): Backup erstellt, Health-Check bestanden, Backup
  geloescht.
- Fehlgeschlagenes Update (absichtlich kaputte app.py): Backup
  erstellt, Health-Check schlaegt korrekt fehl, automatischer Rollback
  stellt den letzten funktionierenden Zustand wieder her (Dienst aktiv,
  Health-Check 200, Datenbank-Canary erhalten), Backup UND
  fehlgeschlagene Installation bleiben als zwei getrennte Verzeichnisse
  zur Fehlersuche erhalten.

VERSION: 1.0.4 (v1.0.3 hatte die oben beschriebene Luecke und wurde
bereits ausgerollt, daher hier ein neues Release statt eines erneuten
Tags auf v1.0.3).
This commit is contained in:
2026-08-22 10:30:33 +02:00
parent e17d91743a
commit 89c5fc7b68
2 changed files with 58 additions and 35 deletions
+57 -34
View File
@@ -8,23 +8,30 @@
# Erkennt selbstständig, ob unter /srv/tesm bereits eine Installation # Erkennt selbstständig, ob unter /srv/tesm bereits eine Installation
# existiert (an sqlite.db), und verhält sich entsprechend: # existiert (an sqlite.db), und verhält sich entsprechend:
# #
# - Keine vorhandene Installation -> normale Frischinstallation. # - Keine vorhandene Installation -> normale Frischinstallation, kein
# - Vorhanden, SCHEMA_VERSION unverändert -> einfaches In-Place-Update # Backup nötig (nichts, worauf zurückgerollt werden könnte).
# (Code/Templates/Abhängigkeiten aktualisieren; sqlite.db bleibt # - Vorhanden (egal ob SCHEMA_VERSION gleich geblieben oder sich
# unangetastet -- rsync schließt sie ohnehin aus). Kein Backup nötig, # geändert hat) -> Update MIT Sicherheitsnetz: die komplette bisherige
# da sich am Datenbank-Schema nichts ändert. # Installation (Code, venv, Datenbank, Schlüssel) wird vorher 1:1 nach
# - Vorhanden, SCHEMA_VERSION unbekannt oder abweichend -> Update MIT # /srv/tesm-backup-pre-update-<timestamp> kopiert. Eine eigentliche
# Sicherheitsnetz: die komplette bisherige Installation (Code, venv, # Schema-Migration übernimmt danach die App selbst beim Start (siehe
# Datenbank, Schlüssel) wird vorher 1:1 nach /srv/tesm-backup-pre- # _ensure_schema() in app.py). Kommt der Dienst anschließend
# migration-<timestamp> kopiert. Die eigentliche Schema-Migration # nachweislich NICHT gesund hoch (HTTP-Check auf /login), wird
# übernimmt danach die App selbst beim Start (siehe _ensure_schema() # automatisch die komplette Sicherung zurückgespielt (alter Code +
# in app.py). Kommt der Dienst anschließend nachweislich NICHT gesund # alte Datenbank zusammen -- ein reines DB-Rollback allein würde
# hoch (HTTP-Check auf /login), wird automatisch die komplette # denselben Fehler beim nächsten Start mit dem neuen Code sofort
# Sicherung zurückgespielt (alter Code + alte Datenbank zusammen -- # wiederholen) und das Backup bleibt zur manuellen Prüfung erhalten.
# ein reines DB-Rollback allein würde denselben Migrationsfehler beim # Kommt er gesund hoch, gilt das Update als erfolgreich und das
# nächsten Start mit dem neuen Code sofort wiederholen) und das # Backup wird gelöscht.
# Backup bleibt zur manuellen Prüfung erhalten. Kommt er gesund hoch, #
# gilt die Migration als erfolgreich und das Backup wird gelöscht. # Das Sicherheitsnetz greift bewusst bei JEDEM Update einer bestehenden
# Installation, nicht nur bei einer erkannten Schema-Änderung -- ein
# rein code-seitiger Fehler im neuen Paket hat mit dem Datenbank-Schema
# nichts zu tun, hätte den Dienst bei einem "Schema unverändert -> kein
# Backup"-Kurzschluss aber trotzdem ungeschützt lahmgelegt (live
# reproduziert). SCHEMA_VERSION bestimmt nur noch den Wortlaut der
# Log-Meldung (In-Place-Update vs. Migration), nicht mehr, OB
# abgesichert wird.
# ============================================================================ # ============================================================================
set -e set -e
@@ -46,6 +53,15 @@ step() { echo -e "${RED}→${NC} ${1}..." | tee -a /var/log/tesm-install
clear 2>/dev/null || true clear 2>/dev/null || true
# ---- Bestehende Installation erkennen ---- # ---- Bestehende Installation erkennen ----
# WICHTIG: das Sicherheitsnetz (Backup + Health-Check + ggf. Rückroll)
# greift bei JEDEM Update einer bestehenden Installation, nicht nur bei
# einer erkannten Schema-Änderung -- live so gefunden: ein rein
# code-seitiger Fehler im neuen Paket (z.B. ein kaputtes app.py, mit dem
# Schema selbst hat das nichts zu tun) hätte bei einem reinen "Schema
# unverändert -> kein Backup"-Kurzschluss ungeschützt den Dienst
# lahmgelegt, ganz ohne automatische Wiederherstellung. Die
# SCHEMA_VERSION-Auswertung dient jetzt nur noch der Log-Meldung (WARUM
# aktualisiert wird), nicht mehr der Entscheidung OB abgesichert wird.
EXISTING_INSTALL=0 EXISTING_INSTALL=0
[ -f /srv/tesm/sqlite.db ] && EXISTING_INSTALL=1 [ -f /srv/tesm/sqlite.db ] && EXISTING_INSTALL=1
@@ -63,7 +79,7 @@ if [ "$EXISTING_INSTALL" -eq 1 ]; then
print_status "Service stopped" print_status "Service stopped"
if [ -n "$OLD_SCHEMA_VERSION" ] && [ -n "$NEW_SCHEMA_VERSION" ] && [ "$OLD_SCHEMA_VERSION" == "$NEW_SCHEMA_VERSION" ]; then if [ -n "$OLD_SCHEMA_VERSION" ] && [ -n "$NEW_SCHEMA_VERSION" ] && [ "$OLD_SCHEMA_VERSION" == "$NEW_SCHEMA_VERSION" ]; then
echo -e "${GREEN}Bestehende Installation erkannt, Datenbank-Schema unverändert (Version ${OLD_SCHEMA_VERSION}):${NC} einfaches In-Place-Update, kein Backup nötig." echo -e "${GREEN}Bestehende Installation erkannt, Datenbank-Schema unverändert (Version ${OLD_SCHEMA_VERSION}):${NC} In-Place-Update."
else else
if [ -n "$OLD_SCHEMA_VERSION" ] && [ -n "$NEW_SCHEMA_VERSION" ]; then if [ -n "$OLD_SCHEMA_VERSION" ] && [ -n "$NEW_SCHEMA_VERSION" ]; then
reason="Datenbank-Schema hat sich geändert (${OLD_SCHEMA_VERSION} -> ${NEW_SCHEMA_VERSION})" reason="Datenbank-Schema hat sich geändert (${OLD_SCHEMA_VERSION} -> ${NEW_SCHEMA_VERSION})"
@@ -71,13 +87,13 @@ if [ "$EXISTING_INSTALL" -eq 1 ]; then
reason="Schema-Version der bestehenden Installation oder dieses Pakets unbekannt" reason="Schema-Version der bestehenden Installation oder dieses Pakets unbekannt"
fi fi
echo -e "${YELLOW}Bestehende Installation erkannt, ${reason}:${NC}" echo -e "${YELLOW}Bestehende Installation erkannt, ${reason}:${NC}"
echo "Update mit automatischer Migration und Sicherheitsnetz (Rückroll-Backup)." echo "Update mit automatischer Migration."
NEEDS_MIGRATION_GUARD=1
BACKUP_DIR="/srv/tesm-backup-pre-migration-$(date +%Y%m%d-%H%M%S)"
step "Backing up current installation to $BACKUP_DIR before migration"
cp -a /srv/tesm "$BACKUP_DIR"
print_status "Backup created"
fi fi
NEEDS_MIGRATION_GUARD=1
BACKUP_DIR="/srv/tesm-backup-pre-update-$(date +%Y%m%d-%H%M%S)"
step "Backing up current installation to $BACKUP_DIR before update"
cp -a /srv/tesm "$BACKUP_DIR"
print_status "Backup created"
fi fi
# ---- Pakete ---- # ---- Pakete ----
@@ -164,11 +180,10 @@ sudo systemctl enable --now tesm.service
sudo systemctl enable --now tesm-check.service tesm-check-restart.timer sudo systemctl enable --now tesm-check.service tesm-check-restart.timer
print_status "Services enabled and started" print_status "Services enabled and started"
# ---- Migrations-Sicherheitsnetz prüfen ---- # ---- Sicherheitsnetz prüfen ----
# Nur relevant, wenn oben tatsächlich eine riskante Migration (abweichende/ # Läuft bei JEDEM Update einer bestehenden Installation (NEEDS_MIGRATION_
# unbekannte SCHEMA_VERSION) erkannt wurde -- beim einfachen In-Place-Update # GUARD=1, siehe oben) -- nur bei einer Frischinstallation ist BACKUP_DIR
# oder einer Frischinstallation ist BACKUP_DIR leer und dieser Block wird # leer und dieser Block wird komplett übersprungen.
# komplett übersprungen.
if [ "$NEEDS_MIGRATION_GUARD" -eq 1 ]; then if [ "$NEEDS_MIGRATION_GUARD" -eq 1 ]; then
step "Verifying migrated installation" step "Verifying migrated installation"
HEALTHY=0 HEALTHY=0
@@ -182,15 +197,23 @@ if [ "$NEEDS_MIGRATION_GUARD" -eq 1 ]; then
if [ "$HEALTHY" -eq 1 ]; then if [ "$HEALTHY" -eq 1 ]; then
print_status "Migration verified healthy" print_status "Migration verified healthy"
rm -rf "$BACKUP_DIR" rm -rf "$BACKUP_DIR"
echo -e "${GREEN}${NC} Migration erfolgreich -- Backup wieder entfernt." echo -e "${GREEN}${NC} Update erfolgreich -- Backup wieder entfernt."
else else
echo -e "${RED}Migration fehlgeschlagen -- Dienst antwortet nicht gesund. Rolle zurück...${NC}" echo -e "${RED}Update fehlgeschlagen -- Dienst antwortet nicht gesund. Rolle zurück...${NC}"
sudo systemctl stop tesm.service 2>/dev/null || true sudo systemctl stop tesm.service 2>/dev/null || true
sudo rm -rf /srv/tesm # Fehlgeschlagene Installation NICHT einfach löschen, sondern separat
sudo mv "$BACKUP_DIR" /srv/tesm # beiseite legen (zur Fehlersuche) -- und das Backup per "cp" statt
# "mv" zurückspielen, damit BEIDES danach getrennt erhalten bleibt
# (sonst würde das Backup beim Zurückspielen selbst zum neuen
# /srv/tesm werden und als eigenständige Sicherung verschwinden).
FAILED_DIR="/srv/tesm-failed-update-$(date +%Y%m%d-%H%M%S)"
sudo mv /srv/tesm "$FAILED_DIR"
sudo cp -a "$BACKUP_DIR" /srv/tesm
sudo systemctl start tesm.service sudo systemctl start tesm.service
echo -e "${YELLOW}Zurückgerollt auf den Stand vor dem Update (alter Code + alte Datenbank zusammen).${NC}" echo -e "${YELLOW}Zurückgerollt auf den Stand vor dem Update (alter Code + alte Datenbank zusammen).${NC}"
echo "Bitte /var/log/tesm/app.log prüfen oder Export/Import nutzen, bevor erneut aktualisiert wird." echo "Backup bleibt erhalten unter: ${BACKUP_DIR}"
echo "Die fehlgeschlagene Installation liegt zur Fehlersuche unter: ${FAILED_DIR}"
echo "Bitte /var/log/tesm/app.log dort prüfen, bevor erneut aktualisiert wird."
fi fi
fi fi
+1 -1
View File
@@ -1 +1 @@
1.0.3 1.0.4