593 lines
26 KiB
Markdown
593 lines
26 KiB
Markdown
# T15 — RUNBOOK DÉPLOIEMENT PRODUCTION MANUEL OMIKA DIAGNOSTICS
|
||
|
||
> Statut : **PRÉPARATION (aucune action Production effectuée)**.
|
||
> Cible : Windows Server 2025 (`SRV-OMIKA`). PowerShell **administrateur**.
|
||
> Ce runbook est **manuel** : fenêtre de maintenance contrôlée (arrêt → sauvegarde →
|
||
> migration → copie → redémarrage → validation → rollback éventuel) acceptée.
|
||
>
|
||
> **RÈGLES ABSOLUES**
|
||
> 1. NE PAS exécuter le seed local (`seed_diagnostics_local.sql`) ni le clear
|
||
> (`clear_diagnostics_local_seed.sql`) en Production. Ils restent dans le dépôt
|
||
> source (développement uniquement).
|
||
> 2. NE JAMAIS écraser les configurations Production existantes (secrets).
|
||
> 3. NE JAMAIS supprimer les JSONL en attente ni remettre le checkpoint à zéro.
|
||
> 4. NE JAMAIS faire de `TRUNCATE` / `DROP TABLE` / `DELETE` d'événements en rollback.
|
||
> 5. Aucun secret n'apparaît dans ce document (placeholders `<SECRET_*>` uniquement).
|
||
> 6. Toute commande SQL de migration utilise `ON_ERROR_STOP=1` et est suivie d'une
|
||
> vérification avant de passer à la suivante.
|
||
|
||
Identifiant de livraison (convention) :
|
||
`Omika_Diagnostics_<ApplicationConstants.Version>_<YYYYMMDDHHmm>`
|
||
ex. `Omika_Diagnostics_1.0.0.0_20261001_1800`.
|
||
(`ApplicationConstants.Version = "1.0.0.0"` — pas de version inventée.)
|
||
|
||
---
|
||
|
||
## PHASE 0 — Préconditions
|
||
|
||
| # | Précondition | Où | Résultat attendu / STOP |
|
||
|---|---|---|---|
|
||
| 0.1 | Accès PowerShell **administrateur** sur SRV-OMIKA | SRV-OMIKA | `net session` OK ; sinon STOP (droits insuffisants) |
|
||
| 0.2 | Runtime .NET 10 présent | SRV-OMIKA | `dotnet --list-runtimes` montre `Microsoft.NETCore.App 10.x` et `Microsoft.AspNetCore.App 10.x`. Absent → STOP (installer le runtime d'hébergement) |
|
||
| 0.3 | `psql.exe` et `pg_dump.exe` présents | SRV-OMIKA | `C:\Program Files\PostgreSQL\18\bin\` contient `psql.exe` + `pg_dump.exe`. Absent → STOP |
|
||
| 0.4 | PostgreSQL service **Running** | SRV-OMIKA | `Get-Service postgresql*` = Running. Sinon STOP |
|
||
| 0.5 | Packages Release générés et **validés** (secret scan OK) | Poste DEV | Dossier de staging complet (4 composants + `Database` + `Deploy` + `SHA256SUMS.txt`). Sinon STOP (re-générer) |
|
||
| 0.6 | Backup PostgreSQL et binaires **réussis** (Phase 2) | SRV-OMIKA | Voir Phase 2. Échec → NO-GO |
|
||
| 0.7 | Fenêtre de maintenance validée (services arrêtables) | Exploitant | Confirmation humaine. Sinon NO-GO |
|
||
|
||
**GO uniquement si 0.1–0.7 tous OK.**
|
||
|
||
---
|
||
|
||
## PHASE 1 — Audit Production (lecture seule, aucune modification)
|
||
|
||
### 1.1 État du schéma PostgreSQL (à exécuter sur SRV-OMIKA)
|
||
|
||
```powershell
|
||
$env:PGPASSWORD = '<SECRET_POSTGRES_ADMIN>'
|
||
$psql = 'C:\Program Files\PostgreSQL\18\bin\psql.exe'
|
||
& $psql -h 127.0.0.1 -p 5432 -U postgres -d omika_diagnostics -c `
|
||
"SELECT column_name, data_type, is_nullable FROM information_schema.columns WHERE table_name='diagnostic_event' ORDER BY ordinal_position;"
|
||
& $psql -h 127.0.0.1 -p 5432 -U postgres -d omika_diagnostics -c `
|
||
"SELECT indexname FROM pg_indexes WHERE tablename='diagnostic_event' ORDER BY indexname;"
|
||
& $psql -h 127.0.0.1 -p 5432 -U postgres -d omika_diagnostics -c `
|
||
"SELECT rolname FROM pg_roles ORDER BY rolname;"
|
||
Remove-Item Env:PGPASSWORD
|
||
```
|
||
|
||
**Résultat attendu** : liste des colonnes/indexes/roles réels.
|
||
**Décisions** :
|
||
- Colonnes `client_code`, `server_instance`, `environment` présentes → **005 déjà appliqué** (ne pas réappliquer).
|
||
- Index `ix_diagnostic_event_client_code_timestamp` présent → 005 complet.
|
||
- `SELECT` + `DELETE` accordés à `omika_diagnostics_worker` sur `diagnostic_event` → **004 déjà appliqué**.
|
||
- Roles `omika_diagnostics_owner/worker/web` présents → **003 déjà appliqué**.
|
||
**STOP si** : la base `omika_diagnostics` est absente ou la connexion échoue (ne jamais la créer à l'aveugle — résoudre d'abord).
|
||
|
||
### 1.2 État des services Windows (lecture seule)
|
||
|
||
```powershell
|
||
Get-Service OmikaApi,OmikaWorker,OmikaDiagnosticsWorker,OmikaDiagnosticsWeb |
|
||
Select-Object Name,DisplayName,Status,StartType
|
||
sc.exe qc OmikaApi
|
||
sc.exe qc OmikaWorker
|
||
sc.exe qc OmikaDiagnosticsWorker
|
||
sc.exe qc OmikaDiagnosticsWeb
|
||
```
|
||
|
||
**Résultat attendu** : nom réel, chemin binaire, compte, StartupType. Notez tout écart
|
||
avec les valeurs attendues (voir section « Services Windows » du rapport).
|
||
**STOP si** : un service n'existe pas alors que la phase d'activation n'a pas été
|
||
validée (compte de service indéfini) — ne pas créer de service dans ce runbook.
|
||
|
||
### 1.3 Chemins réels des dossiers (lecture seule)
|
||
|
||
```powershell
|
||
Test-Path 'C:\Omika\Apps\OmikaApi'
|
||
Test-Path 'C:\Omika\Apps\OmikaWorker'
|
||
Test-Path 'C:\Omika\Apps\DiagnosticsWorker'
|
||
Test-Path 'C:\Omika\Apps\DiagnosticsWeb'
|
||
Test-Path 'C:\Omika\Data\Api\Diagnostics\Jsonl'
|
||
Test-Path 'C:\Omika\Data\DiagnosticsWorker\Checkpoint'
|
||
```
|
||
|
||
**Résultat attendu** : chemins confirmés. Tout chemin absent/différent → **À CONFIRMER**
|
||
et ajuster les phases suivantes (ne pas inventer).
|
||
|
||
### 1.4 JSONL en attente + checkpoint (lecture seule, ne rien modifier)
|
||
|
||
```powershell
|
||
Get-ChildItem 'C:\Omika\Data\Api\Diagnostics\Jsonl\omika-mobile-diagnostic-*.jsonl' |
|
||
Select-Object Name,Length,LastWriteTime
|
||
Get-Content 'C:\Omika\Data\DiagnosticsWorker\Checkpoint\import-checkpoint.json'
|
||
```
|
||
|
||
**Résultat attendu** : liste des JSONL en attente + contenu du checkpoint
|
||
(`{"fileName":...,"lineIndex":...}`). **Notez ces valeurs** (utiles pour la Phase 10).
|
||
**STOP si** : vous êtes tenté de supprimer un JSONL ou de réinitialiser le checkpoint —
|
||
interdit (§26).
|
||
|
||
---
|
||
|
||
## PHASE 2 — Sauvegardes
|
||
|
||
### 2.1 Sauvegarde PostgreSQL (AVANT toute migration)
|
||
|
||
```powershell
|
||
$env:PGPASSWORD = '<SECRET_POSTGRES_ADMIN>'
|
||
$stamp = Get-Date -Format 'yyyyMMdd_HHmm'
|
||
$out = "C:\Omika\Backups\omika_diagnostics_${stamp}_before_upgrade.backup"
|
||
& 'C:\Program Files\PostgreSQL\18\bin\pg_dump.exe' `
|
||
-h 127.0.0.1 -p 5432 -U postgres -d omika_diagnostics -F c -v -f $out
|
||
Remove-Item Env:PGPASSWORD
|
||
```
|
||
|
||
**Résultat attendu** : `pg_dump` se termine sans erreur (exit 0), fichier `.backup` créé.
|
||
**Vérification obligatoire (§17)** :
|
||
```powershell
|
||
$f = Get-Item $out
|
||
$f.Exists -and $f.Length -gt 0
|
||
```
|
||
**STOP / NO-GO si** : `pg_dump` échoue, exit ≠ 0, fichier absent ou taille 0.
|
||
Aucune migration ne commence tant que ce backup n'est pas vérifié.
|
||
|
||
### 2.2 Sauvegarde des binaires (AVANT remplacement)
|
||
|
||
```powershell
|
||
$stamp = Get-Date -Format 'yyyyMMdd-HHmmss'
|
||
$root = 'C:\Omika\Backups'
|
||
Copy-Item 'C:\Omika\Apps\OmikaApi' -Destination "$root\OmikaApi_${stamp}" -Recurse -Force
|
||
Copy-Item 'C:\Omika\Apps\OmikaWorker' -Destination "$root\OmikaWorker_${stamp}" -Recurse -Force
|
||
Copy-Item 'C:\Omika\Apps\DiagnosticsWorker' -Destination "$root\DiagnosticsWorker_${stamp}" -Recurse -Force
|
||
Copy-Item 'C:\Omika\Apps\DiagnosticsWeb' -Destination "$root\DiagnosticsWeb_${stamp}" -Recurse -Force
|
||
```
|
||
|
||
**Note** : exclure les logs très volumineux si présents sous `Apps\*` (copier uniquement
|
||
le contenu publié). Le script existant `Install-OmikaServerFiles.ps1` fait déjà cette
|
||
sauvegarde horodatée (convention `OmikaApi-yyyyMMdd-HHmmss`).
|
||
**STOP / NO-GO si** : copie incomplète (comparer le nombre de fichiers source/cible).
|
||
|
||
### 2.3 Vérification globale GO/NO-GO partiel
|
||
|
||
```text
|
||
[ ] Backup PostgreSQL présent + taille > 0
|
||
[ ] Backup binaires (4 dossiers) présent + complet
|
||
[ ] Packages Release validés (Phase 0.5)
|
||
[ ] Secrets Development absents des packages
|
||
[ ] Migrations nécessaires identifiées (Phase 1.1)
|
||
[ ] JSONL/checkpoint inventoriés (Phase 1.4)
|
||
[ ] Services arrêtables + fenêtre validée
|
||
[ ] Procédure rollback prête (Phase R)
|
||
```
|
||
|
||
Tout prérequis critique faux → **NO-GO, STOP**.
|
||
|
||
---
|
||
|
||
## PHASE 3 — Arrêt des services
|
||
|
||
Ordre retenu (justifié par le flux de données) :
|
||
|
||
| Ordre | Service | Rôle dans le flux | Pourquoi maintenant |
|
||
|---|---|---|---|
|
||
| 1 | `OmikaDiagnosticsWeb` | lecture seule (SELECT) | figer l'accès utilisateur/support d'abord |
|
||
| 2 | `OmikaApi` | écrit JSONL | figer l'écrivain JSONL #1 |
|
||
| 3 | `OmikaWorker` | métier HFSQL + écrit diagnostics | figer l'écrivain #2 |
|
||
| 4 | `OmikaDiagnosticsWorker` | import JSONL → PG | arrêté en dernier pour drainer les dernières lignes |
|
||
|
||
```powershell
|
||
Stop-Service OmikaDiagnosticsWeb -Force
|
||
Stop-Service OmikaApi -Force
|
||
Stop-Service OmikaWorker -Force
|
||
Stop-Service OmikaDiagnosticsWorker -Force
|
||
# Vérifier :
|
||
Get-Service OmikaApi,OmikaWorker,OmikaDiagnosticsWorker,OmikaDiagnosticsWeb | Select Name,Status
|
||
```
|
||
|
||
**Résultat attendu** : les 4 services = `Stopped`.
|
||
**STOP si** : un service ne s'arrête pas (résoudre — ne pas forcer le remplacement de
|
||
fichiers d'un service encore actif).
|
||
|
||
---
|
||
|
||
## PHASE 4 — Migration PostgreSQL (manuelle, auditée)
|
||
|
||
### 4.0 Principe
|
||
|
||
Appliquer **uniquement** les migrations manquantes, **dans l'ordre**, avec
|
||
`ON_ERROR_STOP=1`, et **vérifier après chaque fichier**. Toutes sont idempotentes
|
||
(`CREATE/ADD IF NOT EXISTS`, `GRANT`, `DO $$`). Ne jamais appliquer aveuglément.
|
||
|
||
Table des migrations (voir rapport section F) :
|
||
|
||
| Fichier | Objet | Idempotent | Nécessaire cette release |
|
||
|---|---|---|---|
|
||
| 001 | `diagnostic_event` + `telemetry_metric_hourly` + index | OUI | OUI (si absent) |
|
||
| 002 | `actor_key`, `actor_key_version` + index | OUI | OUI (si absent) |
|
||
| 003 | rôles least-privilege + ownership + grants | OUI | OUI (si absent) |
|
||
| 004 | `GRANT SELECT, DELETE` à `omika_diagnostics_worker` | OUI | OUI (rétention) |
|
||
| 005 | `client_code`/`server_instance`/`environment` + index | OUI | **OUI (indispensable)** |
|
||
|
||
### 4.1 Appliquer 005 (client context) — si absent
|
||
|
||
```powershell
|
||
$env:PGPASSWORD = '<SECRET_POSTGRES_ADMIN>'
|
||
& 'C:\Program Files\PostgreSQL\18\bin\psql.exe' -h 127.0.0.1 -p 5432 -U postgres `
|
||
-d omika_diagnostics -v ON_ERROR_STOP=1 `
|
||
-f '<staging>\Database\005_diagnostics_client_context.sql'
|
||
Remove-Item Env:PGPASSWORD
|
||
```
|
||
|
||
**Vérifier ensuite** (doit renvoyer les 3 colonnes + l'index) :
|
||
```sql
|
||
SELECT column_name FROM information_schema.columns
|
||
WHERE table_name='diagnostic_event'
|
||
AND column_name IN ('client_code','server_instance','environment');
|
||
SELECT indexname FROM pg_indexes
|
||
WHERE tablename='diagnostic_event' AND indexname='ix_diagnostic_event_client_code_timestamp';
|
||
```
|
||
**STOP si** : psql exit ≠ 0, ou la vérification ne renvoie pas les 3 colonnes + l'index.
|
||
|
||
### 4.2 Appliquer 004 (rétention) — si absent
|
||
|
||
```powershell
|
||
$env:PGPASSWORD = '<SECRET_POSTGRES_ADMIN>'
|
||
& 'C:\Program Files\PostgreSQL\18\bin\psql.exe' -h 127.0.0.1 -p 5432 -U postgres `
|
||
-d omika_diagnostics -v ON_ERROR_STOP=1 `
|
||
-f '<staging>\Database\004_diagnostics_retention.sql'
|
||
Remove-Item Env:PGPASSWORD
|
||
```
|
||
|
||
**Vérifier** :
|
||
```sql
|
||
SELECT has_table_privilege('omika_diagnostics_worker','diagnostic_event','SELECT') AS sel,
|
||
has_table_privilege('omika_diagnostics_worker','diagnostic_event','DELETE') AS del;
|
||
```
|
||
**Résultat attendu** : `sel=t, del=t`. Sinon STOP.
|
||
(Rôle de 004 : autoriser le `DiagnosticRetentionWorker` à purger. Sans 004, la purge
|
||
échoue mais l'import continue — fail-open ; néanmoins 004 est **requis** pour que la
|
||
rétention fonctionne réellement.)
|
||
|
||
### 4.3 Appliquer 003 (rôles) — si absent (contexte superuser, cf. `Harden-DatabaseRoles.ps1`)
|
||
|
||
À n'appliquer que si les rôles `omika_diagnostics_owner/worker/web` sont absents
|
||
(vérifié Phase 1.1). Sinon **ne pas réappliquer**. Après 003, définir les mots de passe
|
||
des rôles LOGIN **manuellement** via `\password` (jamais dans un script/log).
|
||
**STOP si** : 003 a déjà été appliqué — ne pas rejouer l'ownership inutilement.
|
||
|
||
### 4.4 Règle d'ordre Migration ↔ Binaires (§27)
|
||
|
||
**La migration 005 doit être appliquée AVANT le déploiement des nouveaux binaires.**
|
||
Justification : les nouveaux binaires (Web + Worker) lisent `client_code`/`server_instance`/
|
||
`environment`. Anciens binaires + nouveau schéma = compatible (colonnes additives
|
||
nullable, ignorées) ; **nouveaux binaires + ancien schéma = échec des requêtes**
|
||
(colonnes absentes). L'ordre sûr est donc : **migration d'abord, binaires ensuite**.
|
||
|
||
---
|
||
|
||
## PHASE 5 — Déploiement des binaires (copie)
|
||
|
||
### 5.1 Vérifier l'intégrité du package (SHA256)
|
||
|
||
```powershell
|
||
cd '<staging>'
|
||
# Install-OmikaServerFiles.ps1 fait cette vérification automatiquement.
|
||
Get-Content SHA256SUMS.txt | Select-Object -First 5 # aperçu
|
||
```
|
||
|
||
**Résultat attendu** : `SHA256SUMS.txt` présent et chaque empreinte correspond au fichier.
|
||
**STOP si** : un hash diffère ou un fichier manque.
|
||
|
||
### 5.2 Copie avec préservation de la configuration
|
||
|
||
Deux options (choisir **une seule**) :
|
||
|
||
**Option A — script existant (recommandé, idempotent, `-WhatIf` possible) :**
|
||
```powershell
|
||
cd '<staging>\Deploy'
|
||
.\Install-OmikaServerFiles.ps1 -PackageRoot '<staging>'
|
||
```
|
||
Ce script sauvegarde l'ancienne version, copie, **préserve** `appsettings.json` et
|
||
`appsettings.Production.json` du serveur, puis vérifie le nombre de fichiers et les EXE.
|
||
|
||
**Option B — copie manuelle (si le script ne convient pas) :**
|
||
```powershell
|
||
Copy-Item '<staging>\OmikaApi\*' 'C:\Omika\Apps\OmikaApi' -Recurse -Force
|
||
Copy-Item '<staging>\OmikaWorker\*' 'C:\Omika\Apps\OmikaWorker' -Recurse -Force
|
||
Copy-Item '<staging>\DiagnosticsWorker\*' 'C:\Omika\Apps\DiagnosticsWorker' -Recurse -Force
|
||
Copy-Item '<staging>\DiagnosticsWeb\*' 'C:\Omika\Apps\DiagnosticsWeb' -Recurse -Force
|
||
```
|
||
|
||
**STOP si** : le nombre de fichiers cible < source (copie incomplète) ou un EXE attendu
|
||
est absent (`Omika.Api.exe`, `Omika.Worker.exe`, `Omika.Diagnostics.Worker.exe`,
|
||
`Omika.Diagnostics.Web.exe`).
|
||
|
||
### 5.3 Ce qui ne doit JAMAIS être écrasé (§19)
|
||
|
||
- `appsettings.json` / `appsettings.Production.json` de chaque composant (secrets serveur).
|
||
- Variables d'environnement MACHINE (secrets) : jamais touchées.
|
||
- `C:\Omika\Data\Api\Diagnostics\Jsonl\**` (JSONL en attente) : jamais supprimés.
|
||
- `C:\Omika\Data\DiagnosticsWorker\Checkpoint\import-checkpoint.json` : jamais réinitialisé.
|
||
- `C:\Omika\Config\**` (config serveur) : jamais écrasé.
|
||
- Certificats TLS : jamais touchés.
|
||
|
||
---
|
||
|
||
## PHASE 6 — Vérification des configurations (avant redémarrage)
|
||
|
||
### 6.1 Clés de configuration attendues (placeholders — injectées SUR LE SERVEUR)
|
||
|
||
**Omika.Api** (Kestrel, service `OmikaApi`) :
|
||
```
|
||
ConnectionStrings__DiagnosticsDatabase = <SECRET> (si l'API lit aussi PG — voir architecture)
|
||
DiagnosticsIdentity__HmacKey = <SECRET_SERVEUR> (ActorKey, min 256 bits Base64)
|
||
MobileAuth__SigningKey = <SECRET_SERVEUR> (obligatoire, sinon fail-fast)
|
||
DiagnosticDeployment__ClientCode = <CODE_CLIENT ou vide → fallback HFSQL CODE_CLIENT>
|
||
DiagnosticDeployment__ServerInstanceId = <ex. API-SRV-01 ou vide → MachineName>
|
||
HFSQL / LicenseConnection / ServiceConnection / FilePasswords = <SECRETS_EXISTANTS>
|
||
Observability__Remote__Directory = <CHEMIN_JSONL> (doit être identique au Worker)
|
||
Observability__External__Enabled = false (ne pas activer le forwarder)
|
||
ASPNETCORE_ENVIRONMENT = Production
|
||
```
|
||
|
||
**Omika.Worker** (service `OmikaWorker`) :
|
||
```
|
||
Hfsql / SyncConnection = <SECRETS_EXISTANTS>
|
||
DiagnosticDeployment__ClientCode = <idem Api>
|
||
DiagnosticDeployment__ServerInstanceId = <ex. WORKER-SRV-01 ou vide → MachineName>
|
||
ASPNETCORE_ENVIRONMENT = Production
|
||
```
|
||
|
||
**Omika.Diagnostics.Worker** (service `OmikaDiagnosticsWorker`) :
|
||
```
|
||
ConnectionStrings__DiagnosticsDatabase = Host=127.0.0.1;Port=5432;Database=omika_diagnostics;Username=omika_diagnostics_worker;Password=<SECRET_LOCAL>
|
||
Observability__Remote__Directory = <CHEMIN_JSONL — IDENTIQUE à l'Api>
|
||
Diagnostics__Import__CheckpointDirectory = C:\Omika\Data\DiagnosticsWorker\Checkpoint
|
||
Diagnostics__Import__CheckpointFileName = import-checkpoint.json
|
||
Diagnostics__RetentionDays = 60
|
||
Diagnostics__RetentionIntervalHours = 24
|
||
Diagnostics__RetentionBatchSize = 1000
|
||
```
|
||
(Le Worker n'enregistre pas la source HFSQL → `DiagnosticDeployment__ClientCode` est
|
||
**config-only** ici ; `ServerInstance` = MachineName si non configuré.)
|
||
|
||
**Omika.Diagnostics.Web** (service `OmikaDiagnosticsWeb`) :
|
||
```
|
||
ConnectionStrings__DiagnosticsDatabase = Host=127.0.0.1;Port=5432;Database=omika_diagnostics;Username=omika_diagnostics_web;Password=<SECRET_LOCAL>
|
||
DiagnosticsAuth__Operators = <LISTE_OPERATEURS_PRODUCTION>
|
||
ASPNETCORE_ENVIRONMENT = Production (default-deny, PAS Development)
|
||
```
|
||
|
||
### 6.2 Contrôles
|
||
|
||
```text
|
||
[ ] Observability__Remote__Directory IDENTIQUE entre OmikaApi et DiagnosticsWorker (chemin absolu)
|
||
[ ] ASPNETCORE_ENVIRONMENT = Production pour les 4 services
|
||
[ ] ConnectionStrings__DiagnosticsDatabase présent pour DiagnosticsWorker + DiagnosticsWeb
|
||
[ ] DiagnosticsIdentity__HmacKey + MobileAuth__SigningKey présents pour Omika.Api
|
||
[ ] DiagnosticDeployment__ClientCode/ServerInstanceId cohérents (ou vide → fallback)
|
||
[ ] Aucune valeur de test dev (admin-local, smtp.example.com, postgres/postgres) dans les configs serveur
|
||
[ ] appsettings.Development.json ABSENT du dossier publié de DiagnosticsWeb (vérifié)
|
||
```
|
||
|
||
**STOP si** : `MobileAuth__SigningKey` absent (l'API refuse de démarrer), ou un secret
|
||
Production a été écrasé (restaurer depuis le backup Phase 2.2).
|
||
|
||
---
|
||
|
||
## PHASE 7 — Redémarrage (ordre recommandé)
|
||
|
||
PostgreSQL **déjà Running** (vérifié Phase 0.4). Ordre = démarrer les **consommateurs
|
||
avant les producteurs** :
|
||
|
||
```powershell
|
||
Start-Service OmikaDiagnosticsWorker # 1. importeur (consommateur JSONL)
|
||
Start-Service OmikaDiagnosticsWeb # 2. UI (lecture seule)
|
||
Start-Service OmikaWorker # 3. sync métier (producteur #2)
|
||
Start-Service OmikaApi # 4. API (producteur #1, dernier)
|
||
Get-Service OmikaApi,OmikaWorker,OmikaDiagnosticsWorker,OmikaDiagnosticsWeb | Select Name,Status
|
||
```
|
||
|
||
**Résultat attendu** : les 4 services = `Running`.
|
||
**STOP si** : un service passe en `Stopped`/`Failed` immédiatement → ne pas démarrer les
|
||
suivants, passer en diagnostic (Phase 8) puis rollback (Phase R) si non résolu.
|
||
|
||
---
|
||
|
||
## PHASE 8 — Smoke tests (non destructifs)
|
||
|
||
### 8.1 Health checks par service
|
||
|
||
```text
|
||
OmikaDiagnosticsWorker : Running ; log = "Contexte de déploiement Diagnostics initialisé"
|
||
puis import/retention sans exception PostgreSQL ;
|
||
checkpoint stable ou progressant ; aucun JSONL supprimé.
|
||
OmikaDiagnosticsWeb : Running ; HTTP 200 sur la page d'accueil ; login OK ; Dashboard OK.
|
||
OmikaWorker : Running ; pas de boucle d'erreur (pas de rafale d'exceptions HFSQL).
|
||
OmikaApi : Running ; endpoint de contrôle OK ; pas d'erreur de démarrage
|
||
(fail-fast SigningKey absent = NOK).
|
||
```
|
||
|
||
```powershell
|
||
# Web (local au serveur) :
|
||
Invoke-WebRequest -UseBasicParsing http://127.0.0.1:<PORT_WEB>/ | Select StatusCode
|
||
# Logs (Event Viewer ou fichiers) :
|
||
Get-EventLog -LogName Application -Newest 50 | Where-Object { $_.Source -match 'Omika' }
|
||
```
|
||
|
||
**Résultat attendu** : aucune exception PostgreSQL, aucun HTTP 500, aucune boucle d'erreur.
|
||
|
||
### 8.2 Contrôle PostgreSQL non destructif (après redémarrage)
|
||
|
||
```sql
|
||
SELECT count(*) FROM diagnostic_event;
|
||
SELECT max(timestamp_utc) AS dernier_evenement,
|
||
max(ingested_at_utc) AS dernier_ingere
|
||
FROM diagnostic_event;
|
||
SELECT client_code, server_instance, environment, count(*)
|
||
FROM diagnostic_event
|
||
WHERE ingested_at_utc > now() - interval '1 hour'
|
||
GROUP BY client_code, server_instance, environment
|
||
ORDER BY count(*) DESC;
|
||
```
|
||
|
||
**Résultat attendu** : des événements récents (`ingested_at_utc` proche de `now()`),
|
||
avec `client_code`/`server_instance`/`environment` renseignés sur les nouveaux événements
|
||
(les anciens restent NULL — normal, pas de backfill).
|
||
|
||
### 8.3 Test ClientCode Production (§32) — sur des événements réellement générés
|
||
|
||
Ouvrir Diagnostics.Web → Dashboard → Diagnostics, puis filtrer un client réel :
|
||
```text
|
||
ClientCode : non NULL (CodeClient de l'instance)
|
||
ServerInstance : identifiant d'instance (MachineName ou override)
|
||
Environment : Production
|
||
Component : Omika.Api / Omika.Worker selon le cas
|
||
AppVersion : 1.0.0.0 (ApplicationConstants.Version)
|
||
```
|
||
**Ne pas** injecter de faux payload de test sur Production.
|
||
|
||
### 8.4 Test synchronisation (§33) — sur une synchronisation métier existante
|
||
|
||
Sur `/Synchronisation`, prendre une opération réelle existante et vérifier :
|
||
```text
|
||
OperationId présent
|
||
ClientCode renseigné
|
||
Component Omika.Worker (et Omika.App)
|
||
ServerInstance instance Worker (jamais l'instance API)
|
||
Environment Production
|
||
```
|
||
**Ne créer aucune donnée métier artificielle risquée.**
|
||
|
||
---
|
||
|
||
## PHASE 9 — Validation Diagnostics (équivalent production de la recette T14-B)
|
||
|
||
| Contrôle | Page / SQL | Attendu |
|
||
|---|---|---|
|
||
| Dashboard | `/` | les compteurs reflètent les données réelles (24h/7j/30j) |
|
||
| Diagnostics | `/Diagnostics` | filtres Client/Serveur/Environnement fonctionnels |
|
||
| Pagination | `/Diagnostics?pageNumber=2` | « page 2 / N », filtres conservés dans l'URL |
|
||
| Détail | `/Diagnostics/{id}` | contexte client/serveur/env affiché (« Non renseigné » si NULL) |
|
||
| Erreurs | `/Erreurs` | groupes par EventCode/exception, multi-clients sans casser le fingerprint |
|
||
| Traces | `/Traces` | timeline par `trace_id`, opérations par `operation_id` |
|
||
| Synchronisation | `/Synchronisation` | différenciation App/Worker (serveurs distincts) |
|
||
| Telemetry | `/Telemetry` | agrégats cohérents avec SQL |
|
||
| Versions | `/Versions` | versions réelles distinctes |
|
||
|
||
**Résultat attendu** : équivalent au verdict T14-B (avec réserves connues : responsive
|
||
statique, producteurs réels non testables). Tout écart → STOP + analyse + rollback si bloquant.
|
||
|
||
---
|
||
|
||
## PHASE 10 — Surveillance post-déploiement
|
||
|
||
Période de surveillance manuelle (durée décidée par l'opérateur, ex. 24–48 h) :
|
||
|
||
```text
|
||
[ ] Services toujours Running (Get-Service, aucune transition vers Stopped/Failed)
|
||
[ ] Pas d'explosion de fichiers JSONL (nombre/taille stables, MaxFiles non atteint en continu)
|
||
[ ] Pas d'erreurs PostgreSQL dans les logs Worker/Web
|
||
[ ] Pas de HTTP 500 sur DiagnosticsWeb
|
||
[ ] Pas d'erreurs API anormales (pas de rafale 4xx/5xx)
|
||
[ ] Pas de boucle Worker (backoff borné, pas de redémarrage en rafale)
|
||
[ ] Checkpoint import : stable ou progressant (jamais régressif)
|
||
[ ] Dashboard : événements récents présents (pas de trou d'ingestion)
|
||
```
|
||
|
||
Signaux d'alerte (→ Phase R) : service qui ne démarre pas, migration échouée, 500 Web,
|
||
API KO, Worker en boucle, import cassé, hausse anormale d'erreurs.
|
||
|
||
---
|
||
|
||
## PHASE R — Rollback
|
||
|
||
### R.1 Conditions de déclenchement (§35)
|
||
|
||
```text
|
||
service ne démarre pas / Failed
|
||
migration échoue (ON_ERROR_STOP)
|
||
DiagnosticsWeb retourne 500
|
||
API ne démarre plus (fail-fast SigningKey, etc.)
|
||
Worker entre en boucle d'erreur
|
||
import Diagnostics cassé (checkpoint bloqué, erreurs Npgsql)
|
||
augmentation anormale d'erreurs
|
||
```
|
||
|
||
Dès qu'une condition est réunie : **STOP** puis **ROLLBACK**.
|
||
|
||
### R.2 Rollback binaires (§36)
|
||
|
||
```powershell
|
||
Stop-Service OmikaApi,OmikaWorker,OmikaDiagnosticsWorker,OmikaDiagnosticsWeb -Force
|
||
$stamp = '<horodatage_du_backup>' # ex. 20261001-180000
|
||
# Restaurer les dossiers précédents :
|
||
Remove-Item 'C:\Omika\Apps\OmikaApi' -Recurse -Force
|
||
Copy-Item "C:\Omika\Backups\OmikaApi_${stamp}" 'C:\Omika\Apps\OmikaApi' -Recurse -Force
|
||
Copy-Item "C:\Omika\Backups\OmikaWorker_${stamp}" 'C:\Omika\Apps\OmikaWorker' -Recurse -Force
|
||
Copy-Item "C:\Omika\Backups\DiagnosticsWorker_${stamp}" 'C:\Omika\Apps\DiagnosticsWorker' -Recurse -Force
|
||
Copy-Item "C:\Omika\Backups\DiagnosticsWeb_${stamp}" 'C:\Omika\Apps\DiagnosticsWeb' -Recurse -Force
|
||
# Vérifier la config préservée, puis :
|
||
Start-Service OmikaDiagnosticsWorker
|
||
Start-Service OmikaDiagnosticsWeb
|
||
Start-Service OmikaWorker
|
||
Start-Service OmikaApi
|
||
Get-Service OmikaApi,OmikaWorker,OmikaDiagnosticsWorker,OmikaDiagnosticsWeb | Select Name,Status
|
||
```
|
||
|
||
**Résultat attendu** : 4 services Running avec la version précédente.
|
||
**Préserver** : `appsettings*.json`, JSONL, checkpoint (jamais supprimés par ce rollback).
|
||
|
||
### R.3 Rollback base de données (§37/§38)
|
||
|
||
**Décision documentée : PAS de rollback SQL automatique.**
|
||
- 004/005 sont **additifs** (colonnes nullable + index + GRANT). Les anciens binaires
|
||
restent **compatibles** avec ces colonnes (ils ne les lisent pas).
|
||
- Aucun `DROP COLUMN`, aucun `DROP INDEX`, aucun `REVOKE` automatique.
|
||
- **Ne jamais** `TRUNCATE` / `DROP TABLE` / `DELETE` les événements créés après migration.
|
||
|
||
```text
|
||
Rollback privilégié : rollback APPLICATIF (binaires) + laisser la migration additive en place.
|
||
```
|
||
|
||
Restaurer la base via `pg_restore` est réservé au cas exceptionnel où une migration
|
||
aurait été destructrice (impossible ici) ou mal appliquée — et uniquement sur décision
|
||
humaine explicite, en restaurant le backup Phase 2.1 :
|
||
```powershell
|
||
& 'C:\Program Files\PostgreSQL\18\bin\pg_restore.exe' `
|
||
-h 127.0.0.1 -p 5432 -U postgres -d omika_diagnostics -c `
|
||
'C:\Omika\Backups\omika_diagnostics_<stamp>_before_upgrade.backup'
|
||
```
|
||
(Avertissement : `-c` supprime les objets existants — à n'utiliser qu'en dernier recours
|
||
et sur décision explicite. Jamais en rollback de routine.)
|
||
|
||
---
|
||
|
||
## Checklist GO / NO-GO finale (avant exécution)
|
||
|
||
```text
|
||
[ ] Backup PostgreSQL terminé et vérifié (présent + taille > 0)
|
||
[ ] Backup binaires (4 composants) terminé et vérifié
|
||
[ ] Packages Release validés (4 composants + Database + Deploy + SHA256SUMS)
|
||
[ ] Secrets Development absents des packages (appsettings.Development.json absent)
|
||
[ ] Migrations nécessaires identifiées (Phase 1.1 : 003/004/005 manquants ou non)
|
||
[ ] JSONL/checkpoint sauvegardés et inventoriés (jamais supprimés/réinitialisés)
|
||
[ ] Services arrêtables + fenêtre de maintenance validée
|
||
[ ] Ordre d'arrêt / redémarrage compris et validé
|
||
[ ] Procédure rollback prête (binaires + décision DB additive)
|
||
[ ] Commandes vérifiées (psql/pg_dump/copy paths confirmés)
|
||
[ ] Fenêtre de maintenance approuvée par l'exploitant
|
||
```
|
||
|
||
Un prérequis critique faux → **NO-GO**.
|
||
|
||
---
|
||
|
||
## Dettes restant hors périmètre (§43)
|
||
|
||
- `Install-DatabaseSchema.ps1` n'applique que **001 + 002** ; **004 et 005 ne sont
|
||
couverts par aucun script** (application manuelle documentée dans ce runbook). Dette
|
||
connue — pas corrigée dans T15.
|
||
- `Harden-DatabaseRoles.ps1` n'applique que **003**.
|
||
- Pas de DeviceInfo, pas de télémétrie d'écrans, pas de nouveaux EventCodes, pas de
|
||
refonte UI/dashboard — hors périmètre.
|