Prepare Immich restore smoke test

This commit is contained in:
2026-05-26 21:33:01 +02:00
parent 48099fb48d
commit c5d231a0db
14 changed files with 616 additions and 8 deletions
+2 -2
View File
@@ -55,13 +55,13 @@ Kontext bewusst gesichert, bevor weitere Live-Aenderungen passieren:
| offen | Disk- und Share-TBDs eintragen | Modelle, Groessen, Seriennummern, Filesysteme und Cache-Settings sind dokumentiert | | offen | Disk- und Share-TBDs eintragen | Modelle, Groessen, Seriennummern, Filesysteme und Cache-Settings sind dokumentiert |
| erledigt (Skript + Host-Test) | Gitea-Repo-Mirror-Mechanik definieren | `ops/borg-ui/scripts/gitea-bundle-mirror.sh` erzeugt verifizierte Bundles unter `/mnt/user/backups/git-bundles/gitea`; Host-Erstlauf 2026-05-26: 4 Bundles, Checksums OK, `homelab-infra.bundle` klonbar und `git fsck` sauber; Schedule bleibt offen | | erledigt (Skript + Host-Test) | Gitea-Repo-Mirror-Mechanik definieren | `ops/borg-ui/scripts/gitea-bundle-mirror.sh` erzeugt verifizierte Bundles unter `/mnt/user/backups/git-bundles/gitea`; Host-Erstlauf 2026-05-26: 4 Bundles, Checksums OK, `homelab-infra.bundle` klonbar und `git fsck` sauber; Schedule bleibt offen |
| offen | Komodo-Bootstrap-Pfad beschreiben | Kaltstart ohne laufendes Komodo ist dokumentiert | | offen | Komodo-Bootstrap-Pfad beschreiben | Kaltstart ohne laufendes Komodo ist dokumentiert |
| offen | Immich-Restore-Test planen | Testumfang, Datenpfade und Smoke-Test-Kriterium stehen fest | | erledigt | Immich-Restore-Test planen | Testumfang, Datenpfade und Smoke-Test-Kriterium sind in `docs/IMMICH_RESTORE_TEST.md`, `ops/restore-tests/immich-plan.md` und `ops/restore-tests/immich-runbook.md` festgehalten; erster Host-Lauf bleibt offen |
## Sprint 3 - Restore und Monitoring ## Sprint 3 - Restore und Monitoring
| Status | Aufgabe | Ergebnis | | Status | Aufgabe | Ergebnis |
|---|---|---| |---|---|---|
| offen | Immich-Restore-Test implementieren | Restore-Report landet unter `/mnt/user/backups/restore-reports/` | | in Arbeit (vorbereitet) | Immich-Restore-Test implementieren | `ops/restore-tests/immich-restore-test.sh`, `immich-compose.test.yml` und Dispatcher-Eintrag vorbereitet; lokaler `--what-if` erfolgreich; Abschluss erst nach echtem Host-Lauf mit Report unter `/mnt/user/backups/restore-reports/` |
| offen | Borg-Stale-Alert bauen | Alarm feuert, wenn Borg-Archiv zu alt ist | | offen | Borg-Stale-Alert bauen | Alarm feuert, wenn Borg-Archiv zu alt ist |
| offen | TLS-Cert-Expiry-Alert bauen | Alarm feuert bei Restlaufzeit unter Schwellwert | | offen | TLS-Cert-Expiry-Alert bauen | Alarm feuert bei Restlaufzeit unter Schwellwert |
| offen | Container-Down-Alert bauen | Unerwartet fehlende Container werden sichtbar | | offen | Container-Down-Alert bauen | Unerwartet fehlende Container werden sichtbar |
+81
View File
@@ -0,0 +1,81 @@
# Immich Restore Test
Status: **vorbereitet, noch nicht live ausgefuehrt** (2026-05-26)
Audit-Bezug: `docs/AUDIT_2026-05-25.md` Finding **F-11**
## Zweck
Schliesst die Audit-Luecke aus F-11: Immich ist der groesste Datentopf (Familien-Fotos), und bisher gibt es im Gegensatz zu Vaultwarden, Gitea und Paperless **keinen** verifizierten Mini-Restore-Test. Dieses Dokument verlinkt die Repo-Artefakte und beschreibt den Ablauf aus Operator-Sicht.
## Repo-Artefakte
| Datei | Zweck |
|---|---|
| `ops/restore-tests/immich-compose.test.yml` | isoliertes Test-Compose: pgvecto-rs Postgres + Redis + Immich-Server, ML weggelassen, `127.0.0.1:12283` |
| `ops/restore-tests/immich-restore-test.sh` | Host-Bash-Skript fuer den Lauf, mit `--what-if` und `--keep-data` Flags |
| `ops/restore-tests/immich-restore-test.ps1` | Plan-Scaffold fuer Windows-Operator-Sicht (kein Live-Run) |
| `ops/restore-tests/immich-plan.md` | Fachlicher Plan: Quellen, Schutzregeln, Smoke-Test-Kriterien, bekannte Risiken |
| `ops/restore-tests/immich-runbook.md` | Konkreter Operator-Ablauf, Fehlerfaelle, Schedule-Vorschlag |
## Was der Test abdeckt
- Extraktion von `local/borg-dumps/latest/immich.dump` aus dem aktuellsten Borg-Archiv
- Import in eine isolierte `tensorchord/pgvecto-rs:pg14-v0.2.0` Postgres-Instanz mit demselben Digest wie Produktion
- Start eines isolierten Immich-Server-Containers mit demselben Digest wie Produktion, **ohne** ML-Container und **ohne** Traefik
- Smoke-Test: Login-Seite erreichbar, `assets`- und `users`-Tabelle lesbar
- Markdown-Report unter `/mnt/user/backups/restore-reports/immich-YYYY-MM-DD.md`
- Bereinigung von Test-Container und Restore-Lab-Daten nach Erfolg
## Was der Test bewusst NICHT abdeckt
- Wiederherstellung produktiver Foto-Dateien (`/mnt/user/photos/immich`, `/mnt/user/photos/family_archive`). Diese Pfade werden vom Test nicht angefasst und nicht in den Test-Container gemountet.
- Machine-Learning-Container. Spart Image-Pull-Zeit und RAM; ML-Features sind im Smoke-Test irrelevant.
- Echte Login-Flow per API. Smoke-Test prueft nur, dass Login-Seite ausgeliefert wird.
- Asset-Rendering / Thumbnail-Generierung. Ohne Foto-Files erwartet.
- Produktive Domain `immich.kaleschke.info`. Test laeuft ausschliesslich auf `127.0.0.1:12283`.
## Restore-Stufe
Der Test deckt **Stufe 4 (kritische Anwendungen)** aus `docs/DISASTER_RECOVERY.md` Phase 4 fuer Immich ab, allerdings nur DB-Ebene und UI-Smoke. Voll-Restore inklusive Foto-Dateien aus Borg ist eigener Folgeschritt; das Skript bereitet die Restore-Lab-Struktur dafuer vor.
## Vor dem ersten echten Lauf
| Pruefung | Verantwortlich | Wo |
|---|---|---|
| Dump-Groesse von `immich.dump` bestimmen | Operator | `ls -lh /mnt/user/backups/borg/dumps/latest/immich.dump` |
| Freier Platz unter `/mnt/user/backups/restore-lab/` | Operator | `df -h /mnt/user/backups` |
| Borg-UI-Container laeuft | Operator | `docker ps | grep borg-ui` |
| Trockenlauf mit `--what-if` | Operator | `bash ops/restore-tests/immich-restore-test.sh --what-if` |
| Erster echter Lauf mit `--keep-data` zur Zeitmessung | Operator | `bash ops/restore-tests/immich-restore-test.sh --keep-data` |
## Nach dem ersten erfolgreichen Lauf
1. Report unter `/mnt/user/backups/restore-reports/immich-YYYY-MM-DD.md` ueberpruefen.
2. `docs/RESTORE_MATRIX.md` um den Mini-Restore-Beleg ergaenzen (Datum + Reportpfad), analog zu Paperless 2026-05-07.
3. `ops/restore-tests/schedule.md` von "Immich spaeter" auf konkreten Quartals-Cron umstellen.
4. `docs/AUDIT_2026-05-25_TODO.md` F-11 von "offen" auf "erledigt" stellen.
5. `docs/MIGRATION_LOG.md` mit Mini-Lauf-Befund ergaenzen, ohne Secret-Werte.
## Schutzregeln
- Skript greift ausschliesslich auf den Restore-Lab-Pfad und den Borg-Extract-Cache zu.
- Produktive Pfade unter `/mnt/user/photos/*` und `/mnt/user/appdata/immich_postgres/` werden nicht angefasst.
- Produktive Container `immich_server`, `immich_postgres`, `immich_redis`, `immich_machine_learning` werden nicht gestoppt, nicht beruehrt.
- Borg-Passphrase wird aus `/mnt/user/appdata/secrets/borg_repo_passphrase.txt` gelesen und nicht in Reports, Logs oder Doku geschrieben.
- Test-Container publishen nur auf `127.0.0.1:12283`, nicht auf LAN- oder Tailscale-Interface.
- Keine Traefik-Labels, keine Public-URL fuer Testcontainer.
## Risiken (aus `ops/restore-tests/immich-plan.md`)
- Dump-Groesse und `pg_restore`-Dauer sind aktuell nicht gemessen.
- pgvecto-rs Extension-Mismatch bei Image-Drift moeglich; Compose pinnt denselben Digest wie Produktion.
- Immich-Server-Migrations koennen Startup nach Restore verzoegern; Skript pollt 120 s.
- Bei Schema-Drift (z. B. nach Major-Update) brechen einzelne DB-Queries; das Skript faengt das tolerant ab und schreibt `n/a` in den Report.
## Naechste Operator-Schritte
1. Skript mit `--what-if` ausfuehren und den Plan-Output gegenpruefen.
2. Dump-Groesse und freien Platz pruefen.
3. Ersten echten Lauf mit `--keep-data` ausfuehren, Dauer messen.
4. Bei Erfolg: Restore-Matrix, Schedule, Audit-TODO und Migration-Log nachziehen.
5. Bei Fehler: Restore-Lab nicht selbst manuell bereinigen, sondern den Trap-Cleanup laufen lassen und ggf. Logs sichern, bevor erneut gestartet wird.
+7
View File
@@ -17,6 +17,13 @@ Dieses Dokument ist nur noch ein historischer Verlauf. Der aktuelle operative Ab
## Historische Meilensteine ## Historische Meilensteine
### 2026-05-26 - Immich Restore-Smoke-Test vorbereitet (F-11)
- `docs/IMMICH_RESTORE_TEST.md` und `ops/restore-tests/immich-plan.md`/`immich-runbook.md` beschreiben den geplanten Immich-Mini-Restore: `immich.dump` aus Borg, isolierter pgvecto-rs-Test-Postgres, Test-Redis, Immich-Server ohne ML, lokaler Port `127.0.0.1:12283`, keine produktiven Foto-Mounts.
- `ops/restore-tests/immich-restore-test.sh`, `immich-restore-test.ps1` und `immich-compose.test.yml` wurden vorbereitet; der Dispatcher kennt `immich --what-if`.
- Lokal verifiziert: Bash-Syntax, `run-restore-checks.sh immich --what-if`, PowerShell-Dispatcher `-Mode immich -WhatIf`, Docker-Compose-Render und Policy-Check. Kein echter Host-Restore, kein Borg-Extract, kein Produktiv-Container-Eingriff.
- F-11 bleibt fachlich offen bis zum ersten Host-Lauf mit Report unter `/mnt/user/backups/restore-reports/immich-YYYY-MM-DD.md`.
### 2026-05-26 - Audit F-16 und F-20 abgeschlossen (Doku-only) ### 2026-05-26 - Audit F-16 und F-20 abgeschlossen (Doku-only)
- F-16: `infra/redis`-Etikett auf die Realitaet abgeglichen. `docs/SERVICE_CATALOG.md`, `docs/REPO_MAP.md`, `HOMELAB_ARCHITECTURE_MASTER_V2.md` Sektion 13 und `docs/DISASTER_RECOVERY.md` Bootstrap-Stufe 2 beschreiben Redis jetzt als "primaer Paperless-Redis (App-Cache); historisch als shared angelegt, faktisch nur von Paperless genutzt". Immich, Nextcloud, Mealie eigene Redis-Instanzen; Authelia bewusst ohne Redis. Keine Compose-Aenderung. - F-16: `infra/redis`-Etikett auf die Realitaet abgeglichen. `docs/SERVICE_CATALOG.md`, `docs/REPO_MAP.md`, `HOMELAB_ARCHITECTURE_MASTER_V2.md` Sektion 13 und `docs/DISASTER_RECOVERY.md` Bootstrap-Stufe 2 beschreiben Redis jetzt als "primaer Paperless-Redis (App-Cache); historisch als shared angelegt, faktisch nur von Paperless genutzt". Immich, Nextcloud, Mealie eigene Redis-Instanzen; Authelia bewusst ohne Redis. Keine Compose-Aenderung.
+1 -1
View File
@@ -46,7 +46,7 @@ Sie ist die fachliche Ergaenzung zu `docs/DISASTER_RECOVERY.md`.
|---|---|---|---|---|---|---| |---|---|---|---|---|---|---|
| Paperless-ngx | Borg + Dumps | `/mnt/user/appdata/paperless-ngx/data`, `/mnt/user/documents/paperless`, `/mnt/user/documents/paperless/export`, `/mnt/user/documents/scans_inbox` | `postgresql17-paperless.dump` | `PAPERLESS_DBPASS`, `PAPERLESS_REDIS`, `borg_repo_passphrase.txt` fuer Restore-Tests | PostgreSQL 17, Redis, Traefik | Web-UI startet, Dokumente vorhanden; Mini-Restore nach `/mnt/user/backups/restore-lab/paperless` am 2026-05-07 erfolgreich validiert | | Paperless-ngx | Borg + Dumps | `/mnt/user/appdata/paperless-ngx/data`, `/mnt/user/documents/paperless`, `/mnt/user/documents/paperless/export`, `/mnt/user/documents/scans_inbox` | `postgresql17-paperless.dump` | `PAPERLESS_DBPASS`, `PAPERLESS_REDIS`, `borg_repo_passphrase.txt` fuer Restore-Tests | PostgreSQL 17, Redis, Traefik | Web-UI startet, Dokumente vorhanden; Mini-Restore nach `/mnt/user/backups/restore-lab/paperless` am 2026-05-07 erfolgreich validiert |
| Mealie | Borg + Dump | `/mnt/user/appdata/mealie/data`, optional `/mnt/user/appdata/mealie/postgres` bei lokalem Share-Weiterbetrieb | `mealie.dump` | `mealie_postgres_password.txt` | `mealie-postgres`, Traefik | UI startet, Rezepte vorhanden | | Mealie | Borg + Dump | `/mnt/user/appdata/mealie/data`, optional `/mnt/user/appdata/mealie/postgres` bei lokalem Share-Weiterbetrieb | `mealie.dump` | `mealie_postgres_password.txt` | `mealie-postgres`, Traefik | UI startet, Rezepte vorhanden |
| Immich | Borg + Dump | `/mnt/user/photos/immich`, `/mnt/user/photos/family_archive` | `immich.dump` | `IMMICH_DB_PASSWORD`, `immich_postgres_password.txt` | `immich_postgres`, `immich_redis`, Traefik | UI startet, Medienbibliothek sichtbar | | Immich | Borg + Dump | `/mnt/user/photos/immich`, `/mnt/user/photos/family_archive` | `immich.dump` | `IMMICH_DB_PASSWORD`, `immich_postgres_password.txt` | `immich_postgres`, `immich_redis`, Traefik | UI startet, Medienbibliothek sichtbar; Restore-Smoke-Test-Artefakte vorbereitet (`docs/IMMICH_RESTORE_TEST.md`), erster Host-Report noch offen |
| Mail-Archiver | Borg + Shared Dump | `/mnt/user/appdata/mailarchiver/data-protection-keys` | `postgresql17-mailarchiver.dump` | `MAILARCHIVER_DB_CONNECTION`, `MAILARCHIVER_AUTH_PASSWORD` | PostgreSQL 17, Traefik, Authelia | Authelia-Weiterleitung greift; nach Login startet die Web-UI und das Archiv laesst sich oeffnen | | Mail-Archiver | Borg + Shared Dump | `/mnt/user/appdata/mailarchiver/data-protection-keys` | `postgresql17-mailarchiver.dump` | `MAILARCHIVER_DB_CONNECTION`, `MAILARCHIVER_AUTH_PASSWORD` | PostgreSQL 17, Traefik, Authelia | Authelia-Weiterleitung greift; nach Login startet die Web-UI und das Archiv laesst sich oeffnen |
| Nextcloud | Borg + Dump | `/mnt/user/appdata/nextcloud/html`, `/mnt/user/documents/nextcloud-data` | `nextcloud.dump` | `nextcloud_admin_user.txt`, `nextcloud_admin_password.txt`, `nextcloud_postgres_password.txt` | `nextcloud-postgres`, `nextcloud-redis`, Traefik | Web-UI startet, Login funktioniert, Dateien sichtbar | | Nextcloud | Borg + Dump | `/mnt/user/appdata/nextcloud/html`, `/mnt/user/documents/nextcloud-data` | `nextcloud.dump` | `nextcloud_admin_user.txt`, `nextcloud_admin_password.txt`, `nextcloud_postgres_password.txt` | `nextcloud-postgres`, `nextcloud-redis`, Traefik | Web-UI startet, Login funktioniert, Dateien sichtbar |
| Glance | Git / Borg-Repo | Repo-Konfiguration unter `ops/glance/config/glance.yml`; keine kritische Datenpersistenz | keine | `GLANCE_IMMICH_API_KEY`, `GLANCE_ADGUARD_USERNAME`, `GLANCE_ADGUARD_PASSWORD`, `GLANCE_SPEEDTEST_API_KEY` | Traefik, Authelia, optional interne API-Ziele | Dashboard startet, Widgets laden, Docker-Status laeuft nur ueber `glance-docker-socket-proxy` | | Glance | Git / Borg-Repo | Repo-Konfiguration unter `ops/glance/config/glance.yml`; keine kritische Datenpersistenz | keine | `GLANCE_IMMICH_API_KEY`, `GLANCE_ADGUARD_USERNAME`, `GLANCE_ADGUARD_PASSWORD`, `GLANCE_SPEEDTEST_API_KEY` | Traefik, Authelia, optional interne API-Ziele | Dashboard startet, Widgets laden, Docker-Status laeuft nur ueber `glance-docker-socket-proxy` |
+7 -1
View File
@@ -32,6 +32,11 @@ Ziel:
- `paperless-restore-test.sh`: hosttauglicher Paperless-Restore-Job - `paperless-restore-test.sh`: hosttauglicher Paperless-Restore-Job
- `paperless-plan.md`: konkreter Paperless-Testplan - `paperless-plan.md`: konkreter Paperless-Testplan
- `paperless-compose.test.yml`: isolierte Testinstanz fuer Paperless inkl. Test-Postgres und Test-Redis - `paperless-compose.test.yml`: isolierte Testinstanz fuer Paperless inkl. Test-Postgres und Test-Redis
- `immich-restore-test.ps1`: Immich-Mini-Restore-Ablauf als Plan-/Windows-Scaffold
- `immich-restore-test.sh`: hosttauglicher Immich-Restore-Job, erster echter Lauf noch offen
- `immich-plan.md`: konkreter Immich-Testplan
- `immich-runbook.md`: Operator-Runbook fuer den ersten Immich-Lauf
- `immich-compose.test.yml`: isolierte Testinstanz fuer Immich inkl. pgvecto-rs Test-Postgres und Test-Redis
- `check-restore-freshness.ps1`: woechentlicher Frische-Check fuer Dumps und Reports - `check-restore-freshness.ps1`: woechentlicher Frische-Check fuer Dumps und Reports
- `run-restore-checks.ps1`: einfacher Dispatcher fuer Restore-Jobs - `run-restore-checks.ps1`: einfacher Dispatcher fuer Restore-Jobs
- `check-restore-freshness.sh`: hosttauglicher Frische-Check - `check-restore-freshness.sh`: hosttauglicher Frische-Check
@@ -76,9 +81,10 @@ Aktuell ist das erste validierte Muster vorhanden.
- echter Vaultwarden-Restore am 2026-05-07 erfolgreich verifiziert - echter Vaultwarden-Restore am 2026-05-07 erfolgreich verifiziert
- echter Gitea-Restore am 2026-05-07 erfolgreich verifiziert - echter Gitea-Restore am 2026-05-07 erfolgreich verifiziert
- echter Paperless-Restore am 2026-05-07 erfolgreich verifiziert - echter Paperless-Restore am 2026-05-07 erfolgreich verifiziert
- Immich-Restore-Test am 2026-05-26 vorbereitet; erster echter Lauf mit Report steht noch aus
- Bash-Dispatcher und Bash-Restore-Jobs am 2026-05-07 erfolgreich hostseitig verifiziert - Bash-Dispatcher und Bash-Restore-Jobs am 2026-05-07 erfolgreich hostseitig verifiziert
- Restore-Lab und Report-Pfade auf dem Host angelegt - Restore-Lab und Report-Pfade auf dem Host angelegt
- V1-Ablauf weiter ohne `ntfy`, mit Bereinigung nach Erfolg - V1-Ablauf weiter ohne `ntfy`, mit Bereinigung nach Erfolg
- naechster grosser Kandidat ist ein weiterer datenbankgestuetzter Dienst oder die Automatisierung - naechster grosser Kandidat ist der erste echte Immich-Lauf mit Zeitmessung; erst danach in die Rotation aufnehmen
Vor dem ersten echten Testlauf muessen Zielpfade, Quellpfade und Bereinigungsschritte bewusst freigegeben werden. Vor dem ersten echten Testlauf muessen Zielpfade, Quellpfade und Bereinigungsschritte bewusst freigegeben werden.
+67
View File
@@ -0,0 +1,67 @@
services:
restoretest-immich-postgres:
# gleiches Image wie Produktion, damit pgvecto-rs / pgvector-Extensions
# beim Restore aus immich.dump verfuegbar sind
image: tensorchord/pgvecto-rs:pg14-v0.2.0@sha256:739cdd626151ff1f796dc95a6591b55a714f341c737e27f045019ceabf8e8c52
container_name: restoretest-immich-postgres
restart: "no"
environment:
TZ: Europe/Berlin
POSTGRES_USER: immich
POSTGRES_DB: immich
POSTGRES_PASSWORD: restoretest-immich-db
PGDATA: /var/lib/postgresql/data
volumes:
- /mnt/user/backups/restore-lab/immich/postgres:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U immich -d immich"]
interval: 10s
timeout: 5s
retries: 12
security_opt:
- no-new-privileges:true
restoretest-immich-redis:
image: redis:7.4-alpine
container_name: restoretest-immich-redis
restart: "no"
command:
- redis-server
- --save
- ""
- --appendonly
- "no"
security_opt:
- no-new-privileges:true
restoretest-immich-server:
# gleiches Image wie Produktion; ML-Container bleibt bewusst weg,
# weil der Smoke-Test nur Login-Page, DB-Restore und Asset-Count prueft.
image: ghcr.io/immich-app/immich-server:release@sha256:c15bff75068effb03f4355997d03dc7e0fc58720c2b54ad6f7f10d1bc57efaa5
container_name: restoretest-immich-server
restart: "no"
depends_on:
restoretest-immich-postgres:
condition: service_healthy
restoretest-immich-redis:
condition: service_started
environment:
DB_HOSTNAME: restoretest-immich-postgres
DB_USERNAME: immich
DB_PASSWORD: restoretest-immich-db
DB_DATABASE_NAME: immich
REDIS_HOSTNAME: restoretest-immich-redis
# ML bewusst deaktiviert: Endpoint zeigt auf eine lokale, nicht
# erreichbare URL. Immich-Server startet, ML-Features bleiben aus.
IMMICH_MACHINE_LEARNING_URL: http://restoretest-immich-ml-disabled:9999
TZ: Europe/Berlin
ports:
# nur 127.0.0.1 - keine Public-Route, keine Traefik-Labels
- "127.0.0.1:12283:2283"
volumes:
# Test-Upload-Verzeichnis ist leer und liegt im Restore-Lab.
# Produktive Assets unter /mnt/user/photos/immich werden NICHT eingebunden,
# damit der Smoke-Test keine produktiven Daten anfasst.
- /mnt/user/backups/restore-lab/immich/upload:/usr/src/app/upload
security_opt:
- no-new-privileges:true
+89
View File
@@ -0,0 +1,89 @@
# Immich Restore Test Plan
## Ziel
Nachweisen, dass `immich.dump` aus dem produktiven Borg-Archiv in einer isolierten Testumgebung wieder einspielbar ist und Immich-Server damit anlaufen, einloggen und Asset-Metadaten anzeigen kann.
Bewusst **nicht** Teil dieses Tests:
- Wiederherstellung produktiver Foto-Dateien aus `/mnt/user/photos/immich` und `/mnt/user/photos/family_archive`. Der Smoke-Test bleibt DB-/UI-zentriert.
- Machine-Learning-Container. Spart Image-Pull-Zeit und Resource-Last; ML-Features sind im Smoke-Test nicht erforderlich.
- Echte Browser-Login-Sequenz. Smoke-Test prueft nur, dass die Login-Seite ausgeliefert wird und die DB-Tabellen `assets` und `users` lesbar sind.
## Quelle
- Backup-Quelle: produktives Borg-Archiv (`hetzner_borg_appdata_critical` oder lokales Mirror)
- fachlich relevanter Dump im Archiv:
- `local/borg-dumps/latest/immich.dump`
- Erzeuger: `ops/borg-ui/scripts/pre-backup-dumps.sh`, Funktion `dump_pg_db immich_postgres ... immich immich` mit `pg_dump -Fc`
- produktive Foto-Pfade werden im Smoke-Test bewusst **nicht** angefasst
## Test-Ziel
- Restore-Lab: `/mnt/user/backups/restore-lab/immich`
- Testdatenpfade:
- `/mnt/user/backups/restore-lab/immich/postgres` (Test-Postgres-Datadir)
- `/mnt/user/backups/restore-lab/immich/upload` (leeres Upload-Volume, Immich-Server braucht den Pfad nur als Mountpoint)
- `/mnt/user/backups/restore-lab/immich/dumps/latest/immich.dump` (extrahierter Dump)
- Testcontainer:
- `restoretest-immich-server`
- `restoretest-immich-postgres` (`tensorchord/pgvecto-rs:pg14-v0.2.0` - identisch zur Produktion, weil der Dump pgvecto-rs Extension referenziert)
- `restoretest-immich-redis` (`redis:7.4-alpine`, rebuildbar)
- Testport Web: `127.0.0.1:12283:2283`
- Report-Ziel: `/mnt/user/backups/restore-reports/immich-YYYY-MM-DD.md`
## Schutzregeln
- produktive Pfade `/mnt/user/photos/immich` und `/mnt/user/photos/family_archive` werden **nicht** in den Test-Container gemountet
- produktive Domain `immich.kaleschke.info` wird **nicht** uebernommen
- keine Traefik-Labels fuer die Testinstanz
- keine produktive `immich_postgres`-/`immich_redis`-Instanz fuer den Test verwenden
- ML-Container bleibt weg
- Testcontainer publishen nur auf `127.0.0.1`, nicht auf LAN- oder Tailscale-Interface
- Borg-Passphrase wird aus `/mnt/user/appdata/secrets/borg_repo_passphrase.txt` gelesen und niemals in Logs, Reports oder Doku geschrieben
## Geplanter Ablauf
1. Restore-Ziel unter `/mnt/user/backups/restore-lab/immich` vorbereiten (postgres, upload, dumps/latest)
2. `local/borg-dumps/latest/immich.dump` aus dem aktuellsten Borg-Archiv extrahieren
3. Test-Postgres (`pgvecto-rs`) und Test-Redis mit `ops/restore-tests/immich-compose.test.yml` starten
4. `immich.dump` in Test-Postgres importieren (`pg_restore -Fc --clean --if-exists --no-owner --no-privileges`)
5. Testinstanz `restoretest-immich-server` starten
6. lokalen Smoke-Test gegen `http://127.0.0.1:12283` ausfuehren und Asset/User-Count aus DB lesen
7. Report unter `/mnt/user/backups/restore-reports/immich-YYYY-MM-DD.md` schreiben
8. Testcontainer stoppen und Restore-Lab bereinigen
## Smoke-Test
Minimal erfolgreich:
- Test-Postgres startet `healthy`
- `pg_restore -Fc` laeuft ohne Fehler durch
- Immich-Server liefert HTTP `200`, `302` oder `303` auf `/`
- Response enthaelt mindestens einen der Marker `Immich`, `Login`, `Signin`
- `select count(*) from assets;` und `select count(*) from users;` sind lesbar
Optional spaeter:
- Echte Login-Form via API ansprechen
- pgvecto-rs Extension explizit per `\dx` pruefen
- Test mit gemountetem **read-only** Foto-Sample-Pfad und Thumbnail-Rendering
- Test inkl. ML-Container, sobald genug Test-Ressourcen verfuegbar
## Bekannte Komplikationen
| Risiko | Beschreibung | Mitigation |
|---|---|---|
| Dump-Groesse unbekannt | `pg_dump -Fc` der Immich-DB kann je nach Asset-/Face-Tabellen mehrere GB sein | Erster Lauf bewusst mit `--what-if`, anschliessend Operator-Test mit Zeitmessung |
| `pg_restore`-Dauer unbekannt | Index-/Constraint-Aufbau und pgvecto-rs-Index-Build koennen lange dauern | Test-Postgres mit Health-Polling startet; Lauf nicht abbrechen ohne `pg_restore`-Exit |
| pgvecto-rs Extension-Mismatch | Wenn das pgvecto-rs-Image im Test nicht exakt dieselbe Version wie Produktion ist, kann `CREATE EXTENSION vectors` im Restore fehlschlagen | Compose pinnt denselben Digest wie `apps/immich/docker-compose.yml` |
| Immich-Server-Migrations beim Start | Immich fuehrt beim ersten Start DB-Migrations aus; das kann nach Restore noch laufen, bevor Web-UI antwortet | Smoke-Test pollt HTTP bis zu 120 s, bevor er als Fehler markiert |
| Asset-Files fehlen | Der Test mountet kein Foto-Volume; Immich zeigt "missing" auf Asset-Detail-Seiten | Smoke-Test prueft nur Login-Page und DB-Counts, nicht Asset-Rendering |
| ML-Endpoint unreachable | Immich-Server kann ML-Endpoint nicht erreichen | `IMMICH_MACHINE_LEARNING_URL` zeigt bewusst auf einen nicht erreichbaren Hostnamen; Login bleibt funktional, ML-Features bleiben deaktiviert |
## Noch offen vor dem ersten echten Lauf
- Dump-Groesse `immich.dump` auf dem Host bestimmen (`ls -lh /mnt/user/backups/borg/dumps/latest/immich.dump`)
- Erwartete Restore-Dauer durch ersten Lauf mit `--keep-data` messen
- Pruefen, ob die Immich-Tabellen `assets`/`users` im aktuellen Schema noch existieren (Schema-Drift bei Major-Update wuerde die Asset-Count-Query brechen, das Skript faengt das tolerant ab)
- Schedule-Eintrag in `ops/restore-tests/schedule.md`: aktuell ist Immich nur als "spaeter, eigener Sprint" gefuehrt. Erst nach erstem erfolgreichen Lauf in Schedule aufnehmen, z. B. quartalsweise.
+44
View File
@@ -0,0 +1,44 @@
param(
[string]$BackupSource = "/mnt/user/backups/borg",
[string]$DumpSource = "/mnt/user/backups/borg/dumps/latest/immich.dump",
[string]$RestoreRoot = "/mnt/user/backups/restore-lab/immich",
[string]$ReportRoot = "/mnt/user/backups/restore-reports",
[string]$BorgPassphraseFile = "/mnt/user/appdata/secrets/borg_repo_passphrase.txt",
[switch]$WhatIf
)
$Mode = if ($WhatIf) { "WhatIf" } else { "PlanOnly" }
Write-Output "Immich restore test scaffold"
Write-Output "BackupSource: $BackupSource"
Write-Output "DumpSource: $DumpSource"
Write-Output "RestoreRoot: $RestoreRoot"
Write-Output "ReportRoot: $ReportRoot"
Write-Output "BorgPassphraseFile: $BorgPassphraseFile"
Write-Output "Expected Borg source paths inside archive:"
Write-Output " - local/borg-dumps/latest/immich.dump"
Write-Output ""
Write-Output "Planned isolation:"
Write-Output " - Test Postgres: tensorchord/pgvecto-rs:pg14-v0.2.0 (same as production)"
Write-Output " - Test Redis: redis:7.4-alpine (rebuildable, no restore needed)"
Write-Output " - Test Server: ghcr.io/immich-app/immich-server:release (pinned digest like production)"
Write-Output " - ML container: deliberately omitted"
Write-Output " - Test endpoint: 127.0.0.1:12283 (no Traefik, no public domain)"
Write-Output " - Productive photo paths under /mnt/user/photos/* will NOT be mounted"
Write-Output "Mode: $Mode"
Write-Output ""
Write-Output "Planned steps:"
Write-Output "1. Prepare restore-lab target under /mnt/user/backups/restore-lab/immich"
Write-Output "2. Extract immich.dump from current Borg archive into test path"
Write-Output ' Template: borg extract "$BORG_REPO" "::ARCHIVE_NAME" local/borg-dumps/latest/immich.dump'
Write-Output ' Passphrase source: $(cat /mnt/user/appdata/secrets/borg_repo_passphrase.txt)'
Write-Output "3. Start isolated test Postgres (pgvecto-rs) and test Redis"
Write-Output "4. Import immich.dump into test Postgres with pg_restore -Fc --clean --if-exists --no-owner --no-privileges"
Write-Output "5. Start restoretest-immich-server against isolated DB/Redis (ML omitted)"
Write-Output "6. Run smoke checks against http://127.0.0.1:12283 and DB asset count"
Write-Output "7. Write markdown report under /mnt/user/backups/restore-reports"
Write-Output "8. Stop test containers and clean restore data after success"
Write-Output ""
Write-Output "This script is intentionally a scaffold only."
Write-Output "No restore, no dump import, no container start, no file write is executed yet."
Write-Output "Actual run happens on the Unraid host via ops/restore-tests/immich-restore-test.sh"
+172
View File
@@ -0,0 +1,172 @@
#!/bin/bash
set -euo pipefail
# Immich Restore Smoke Test
#
# Nicht-destruktiver Restore-Smoke-Test fuer Immich.
# - liest immich.dump aus dem produktiven Borg-Archiv
# - importiert in eine isolierte Test-Postgres-Instanz mit gleichem Image
# wie Produktion (tensorchord/pgvecto-rs)
# - startet einen isolierten Immich-Server-Container ohne Traefik und
# ohne ML-Container
# - prueft Login-Page und Asset-Anzahl aus DB
# - bereinigt anschliessend
#
# Produktiver Immich-Stack wird NICHT angefasst.
# Produktive Foto-Pfade unter /mnt/user/photos/* werden NICHT gemountet.
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
. "$SCRIPT_DIR/common.sh"
WHATIF=0
KEEP_DATA=0
for arg in "$@"; do
case "$arg" in
--what-if) WHATIF=1 ;;
--keep-data) KEEP_DATA=1 ;;
*) echo "Unknown argument: $arg" >&2; exit 1 ;;
esac
done
RESTORE_ROOT="/mnt/user/backups/restore-lab/immich"
REPORT_ROOT="/mnt/user/backups/restore-reports"
EXTRACT_DIR="$BORG_RESTORE_HOST_ROOT/immich-extract"
COMPOSE_FILE="$SCRIPT_DIR/immich-compose.test.yml"
REPORT_FILE="$REPORT_ROOT/immich-$(date +%F).md"
if [ "$WHATIF" -eq 1 ]; then
cat <<EOF
Immich restore test
Mode: WhatIf
RestoreRoot: $RESTORE_ROOT
ReportRoot: $REPORT_ROOT
Expected Borg source paths:
- local/borg-dumps/latest/immich.dump
Planned isolation:
- Test-Postgres: tensorchord/pgvecto-rs:pg14-v0.2.0
- Test-Redis: redis:7.4-alpine (rebuildbar, kein Restore)
- Test-Server: ghcr.io/immich-app/immich-server:release (Image-Pin wie Produktion)
- ML-Container bewusst weggelassen
- Test-Upload: leer, unter $RESTORE_ROOT/upload
- Productive photo paths NOT mounted: /mnt/user/photos/immich, /mnt/user/photos/family_archive
- Test endpoint: 127.0.0.1:12283 (no Traefik, no public domain)
Smoke-Test:
- Test-Postgres healthy
- pg_restore -Fc -> immich.dump
- HTTP 200/302/3xx von 127.0.0.1:12283
- Asset-Count aus DB
EOF
exit 0
fi
require_cmd docker
require_cmd curl
require_path "$BORG_PASSPHRASE_FILE_DEFAULT"
require_path "$COMPOSE_FILE"
cleanup() {
cleanup_compose "$COMPOSE_FILE"
if [ "$KEEP_DATA" -ne 1 ]; then
rm -rf "$RESTORE_ROOT"
fi
rm -rf "$EXTRACT_DIR"
}
trap cleanup EXIT
rm -rf "$EXTRACT_DIR" "$RESTORE_ROOT"
mkdir -p "$RESTORE_ROOT/postgres" "$RESTORE_ROOT/upload" "$RESTORE_ROOT/dumps/latest"
archive="$(latest_archive_name)"
repo="$(borg_repo_url)"
borg_extract "/restore/immich-extract" \
"local/borg-dumps/latest/immich.dump"
mv "$EXTRACT_DIR/local/borg-dumps/latest/immich.dump" "$RESTORE_ROOT/dumps/latest/immich.dump"
# Stufe 1: Test-Postgres und Test-Redis starten
docker compose -f "$COMPOSE_FILE" up -d \
restoretest-immich-postgres restoretest-immich-redis >/dev/null
# Warten auf Postgres ready
until docker exec restoretest-immich-postgres pg_isready -U immich -d immich >/dev/null 2>&1; do
sleep 2
done
# Stufe 2: Dump in Test-Postgres importieren
# Hinweis: pg_restore mit --clean --if-exists, damit die Operation idempotent ist.
# --no-owner / --no-privileges, weil im Test-Postgres kein produktiver User existiert.
docker exec -i restoretest-immich-postgres \
pg_restore -U immich -d immich --clean --if-exists --no-owner --no-privileges \
< "$RESTORE_ROOT/dumps/latest/immich.dump"
# Stufe 3: Immich-Server starten (ohne ML)
docker compose -f "$COMPOSE_FILE" up -d restoretest-immich-server >/dev/null
# Immich-Server braucht beim ersten Start einige Sekunden fuer DB-Migrations-Checks.
# Wir geben ihm bis zu 120s und pollen den HTTP-Endpunkt.
http_status=""
for _ in $(seq 1 60); do
http_status="$(curl -s -o /tmp/immich-body.html -w '%{http_code}' -L http://127.0.0.1:12283 || true)"
if [ "$http_status" = "200" ] || [ "$http_status" = "302" ] || [ "$http_status" = "303" ]; then
break
fi
sleep 2
done
# Body-Check: Immich-UI hat typische Marker. Wir matchen tolerant.
body_check="ok"
if ! grep -qiE "immich|login|signin" /tmp/immich-body.html 2>/dev/null; then
body_check="missing-marker"
fi
# Asset-Count aus DB. Wenn die Spalte nicht existiert (Schema-Drift),
# wird das im Report sichtbar gemacht statt das Skript zu killen.
asset_count="$(docker exec restoretest-immich-postgres \
psql -U immich -d immich -tAc "select count(*) from assets;" 2>/dev/null \
| tr -d '[:space:]' || true)"
if [ -z "$asset_count" ]; then
asset_count="n/a"
fi
# User-Count als zusaetzlicher DB-Sanity-Check
user_count="$(docker exec restoretest-immich-postgres \
psql -U immich -d immich -tAc "select count(*) from users;" 2>/dev/null \
| tr -d '[:space:]' || true)"
if [ -z "$user_count" ]; then
user_count="n/a"
fi
write_report "$REPORT_FILE" <<EOF
# Immich Restore Test Report - $(date +%F)
- Service: \`immich\`
- Source repo: \`$repo\`
- Archive: \`$archive\`
- Restore root: \`$RESTORE_ROOT\`
- Test containers:
- \`restoretest-immich-server\`
- \`restoretest-immich-postgres\` (tensorchord/pgvecto-rs:pg14-v0.2.0)
- \`restoretest-immich-redis\`
- Test endpoint: \`http://127.0.0.1:12283\`
- ML container: deliberately omitted
- Result: \`SUCCESS\`
## Checks
- Borg extract of \`immich.dump\`: \`ok\`
- Dump import into isolated Postgres: \`ok\`
- HTTP status after redirect: \`$http_status\`
- Login page marker: \`$body_check\`
- Asset count in test DB: \`$asset_count\`
- User count in test DB: \`$user_count\`
## Notes
- Test ran without Traefik and without the productive domain.
- Productive photo paths under /mnt/user/photos/* were NOT mounted.
- Test data was cleaned after success: \`$([ "$KEEP_DATA" -eq 1 ] && echo no || echo yes)\`
- Restore-Quelle Dump: \`local/borg-dumps/latest/immich.dump\` aus aktuellem Borg-Archiv.
EOF
echo "Immich restore test ok -> $REPORT_FILE"
+128
View File
@@ -0,0 +1,128 @@
# Immich Restore Runbook
## Status
Skript und Test-Compose sind vorbereitet. **Erster echter Lauf steht noch aus.**
Vor dem ersten Lauf muss Operator entscheiden:
- ist genug freier Platz unter `/mnt/user/backups/restore-lab/immich` vorhanden (Dump + Test-Postgres-Datadir + Upload-Dummy)?
- ist genug freier RAM/CPU verfuegbar, um Immich-Server + Test-Postgres parallel zur produktiven Last laufen zu lassen?
- soll der Lauf zuerst mit `--what-if` ausgefuehrt werden, dann mit `--keep-data` zur Zeitmessung?
## Vorbedingungen
- Borg-Quelle ist verfuegbar
- Borg-UI laeuft (`docker ps | grep borg-ui`)
- Borg-Passphrase-Datei vorhanden: `/mnt/user/appdata/secrets/borg_repo_passphrase.txt`
- aktueller Dump `immich.dump` ist Teil des letzten Borg-Archivs (siehe `pre-backup-dumps.sh`)
- Testpfade unter `/mnt/user/backups/restore-lab/` und `/mnt/user/backups/restore-reports/` sind freigegeben
- produktiver Immich-Stack laeuft (oder ist bewusst aus); der Test ist davon unabhaengig
## Bestaetigter Host-Stand (Soll)
- produktiver Immich-Server: `immich_server` Container mit Image `ghcr.io/immich-app/immich-server:release@sha256:c15bff75068effb03f4355997d03dc7e0fc58720c2b54ad6f7f10d1bc57efaa5`
- produktive Postgres: `immich_postgres` mit `tensorchord/pgvecto-rs:pg14-v0.2.0@sha256:739cdd626151ff1f796dc95a6591b55a714f341c737e27f045019ceabf8e8c52`
- produktive Foto-Pfade: `/mnt/user/photos/immich`, `/mnt/user/photos/family_archive`
- aktueller Dump-Pfad: `/mnt/user/backups/borg/dumps/latest/immich.dump`
- Secret: `/mnt/user/appdata/secrets/immich_postgres_password.txt` (wird vom Test **nicht** gebraucht; Test nutzt eigenes Test-Passwort)
## Bestaetigter Teststand
- noch kein echter Mini-Restore gelaufen
- Skript-Set vorbereitet:
- `ops/restore-tests/immich-compose.test.yml`
- `ops/restore-tests/immich-restore-test.sh`
- `ops/restore-tests/immich-restore-test.ps1` (Scaffold, kein Live-Run)
- `ops/restore-tests/immich-plan.md`
- `ops/restore-tests/immich-runbook.md`
## Erster Lauf - trockene Variante
```bash
bash /mnt/user/services/homelab-infra/ops/restore-tests/immich-restore-test.sh --what-if
```
Erwartete Ausgabe: nur Plan-Output, kein Docker-Start, kein Borg-Extract.
## Erster Lauf - echter Test (Operator-freigegeben)
```bash
# Optional: laufende Borg-Jobs pruefen, damit Borg-UI nicht parallel ausgelastet ist
docker exec borg-ui sqlite3 /data/borg.db \
"select status, archive_name, datetime(updated_at,'unixepoch') from backup_jobs order by id desc limit 5;"
# Lauf mit Datenerhalt fuer Zeitmessung
bash /mnt/user/services/homelab-infra/ops/restore-tests/immich-restore-test.sh --keep-data
```
Bei erfolgreichem Lauf:
- Report unter `/mnt/user/backups/restore-reports/immich-YYYY-MM-DD.md`
- Test-Container `restoretest-immich-*` sind nach Lauf bereits `down`
- Restore-Lab-Daten bleiben mit `--keep-data` erhalten; ohne Flag werden sie geloescht
## Smoke-Test-Pruefungen
Minimal erwartet im Report:
- `HTTP status after redirect: 200|302|303`
- `Login page marker: ok`
- `Asset count in test DB`: Zahl, oder `n/a` bei Schema-Drift
- `User count in test DB`: Zahl, oder `n/a` bei Schema-Drift
- Pre-Dump-Hook-Kette (`pg_dump -Fc`) und Restore-Kette (`pg_restore -Fc`) sind kompatibel
- pgvecto-rs Extension ist im Restore-DB sichtbar
Manuelle Folgepruefung (optional):
Das Skript stoppt die Test-Container auch bei `--keep-data`; dieses Flag erhaelt nur die Restore-Lab-Daten. Fuer eine manuelle Folgepruefung nach einem erfolgreichen `--keep-data`-Lauf die Testinstanz kurz wieder hochfahren und danach wieder stoppen:
```bash
docker compose -f /mnt/user/services/homelab-infra/ops/restore-tests/immich-compose.test.yml up -d \
restoretest-immich-postgres restoretest-immich-redis restoretest-immich-server
docker exec restoretest-immich-postgres psql -U immich -d immich -c "\dx"
docker exec restoretest-immich-postgres psql -U immich -d immich -tAc "select count(*) from assets;"
docker logs --tail 100 restoretest-immich-server
docker compose -f /mnt/user/services/homelab-infra/ops/restore-tests/immich-compose.test.yml down
```
## Cleanup nach Lauf ohne `--keep-data`
Das Skript bereinigt:
- Test-Container via `docker compose down`
- Restore-Lab unter `/mnt/user/backups/restore-lab/immich`
- Extract-Cache unter `/mnt/user/appdata/borg-ui/restore/immich-extract`
**Vorsicht:** `rm -rf` arbeitet ausschliesslich auf dem festen Restore-Lab-Pfad. Produktive Immich-Pfade unter `/mnt/user/photos/*` werden vom Skript niemals beschrieben.
## Fehlerfaelle
| Symptom | Ursache | Massnahme |
|---|---|---|
| `pg_restore: error: could not find extension ... vectors` | Test-Postgres-Image nicht pgvecto-rs | Compose-Pin im Test-Compose pruefen |
| HTTP-Timeout nach 120 s | Immich-Migrations laufen noch | Wartezeit im Skript erhoehen oder Logs pruefen |
| `pg_isready` nie healthy | Test-Postgres bricht beim Start ab (Datadir-Konflikt) | Restore-Lab vor Lauf vollstaendig leer; `docker logs restoretest-immich-postgres` |
| Body matcht keine Marker | Immich UI hat sich versioniert; Marker-Liste anpassen | Marker im Skript erweitern (`grep -qiE`) |
| Disk-Space-Mangel | Dump + Postgres-Datadir + Extract-Cache | mehr Platz freigeben oder Lauf abbrechen |
## Schedule-Eintrag (geplant, noch nicht aktiv)
Aktuell in `ops/restore-tests/schedule.md` nur als "spaeter, eigener Sprint" gelistet.
Nach erstem erfolgreichen Lauf vorschlagen:
- quartalsweise (`0 9 1 1,4,7,10 *` o. ae.)
- ohne `--keep-data`
- Report wird automatisch von `monthly-random-restore.sh` mit eingelesen, sobald Immich dort eintragbar ist
## Festgelegte Entscheidungen
- Immich-Restore-Test nutzt isoliertes Test-Postgres mit gleichem Image wie Produktion.
- ML-Container wird im Smoke-Test **nicht** mitgestartet.
- Produktive Foto-Pfade werden **nicht** in den Test gemountet.
- Test-Daten werden nach erfolgreichem Lauf geloescht (`--keep-data` ueberschreibt das).
- Borg-Passphrase wird aus Host-Secret-Datei gelesen und nirgendwo geloggt.
- `ntfy` wird im ersten echten Lauf nicht eingebunden.
+9 -1
View File
@@ -1,5 +1,5 @@
param( param(
[ValidateSet("freshness","vaultwarden","gitea","paperless")] [ValidateSet("freshness","vaultwarden","gitea","paperless","immich")]
[string]$Mode, [string]$Mode,
[switch]$WhatIf [switch]$WhatIf
) )
@@ -35,4 +35,12 @@ switch ($Mode) {
} }
exit $LASTEXITCODE exit $LASTEXITCODE
} }
"immich" {
if ($WhatIf) {
& (Join-Path $base "immich-restore-test.ps1") -WhatIf
} else {
& (Join-Path $base "immich-restore-test.ps1")
}
exit $LASTEXITCODE
}
} }
+7 -1
View File
@@ -28,8 +28,14 @@ case "$MODE" in
fi fi
exec "$SCRIPT_DIR/paperless-restore-test.sh" exec "$SCRIPT_DIR/paperless-restore-test.sh"
;; ;;
immich)
if [ "$WHATIF" = "--what-if" ]; then
exec "$SCRIPT_DIR/immich-restore-test.sh" --what-if
fi
exec "$SCRIPT_DIR/immich-restore-test.sh"
;;
*) *)
echo "Usage: $0 {freshness|vaultwarden|gitea|paperless} [--what-if]" >&2 echo "Usage: $0 {freshness|vaultwarden|gitea|paperless|immich} [--what-if]" >&2
exit 1 exit 1
;; ;;
esac esac
@@ -7,7 +7,7 @@ SUCCESS_TOPIC="${2:-${RESTORE_SUCCESS_TOPIC:-homelab-info}}"
FAILURE_TOPIC="${RESTORE_FAILURE_TOPIC:-homelab-alerts}" FAILURE_TOPIC="${RESTORE_FAILURE_TOPIC:-homelab-alerts}"
if [ -z "$MODE" ]; then if [ -z "$MODE" ]; then
echo "Usage: $0 <freshness|vaultwarden|gitea|paperless> [success_topic]" >&2 echo "Usage: $0 <freshness|vaultwarden|gitea|paperless|immich> [success_topic]" >&2
exit 1 exit 1
fi fi
+1 -1
View File
@@ -35,7 +35,7 @@ Quartalsweise:
Spaeter: Spaeter:
- `immich` als eigener Sprint - `immich` Restore-Smoke-Test ist vorbereitet, aber noch nicht in der Rotation. Erst manuell mit `--what-if`, dann mit `--keep-data` ausfuehren; nach erfolgreichem Report quartalsweise einplanen.
## Konkreter Kalender ## Konkreter Kalender