# Runbook — Migration Client Pilote vers Production

**Applicable à :** Clients A (TechVision ESN) et B (RH Conseil)
**Durée estimée :** 2–4 heures par client (fenêtre de maintenance recommandée)
**Prérequis :** Cluster production opérationnel (EKS eu-west-3), Keycloak prod configuré

---

## Vue d'ensemble

```
Staging (api-staging.veylant.ai)          Production (api.veylant.ai)
  │                                              │
  ├── PostgreSQL staging DB        →→→→→→→→     ├── PostgreSQL production DB
  ├── Keycloak staging realm       →→→→→→→→     ├── Keycloak production realm
  ├── Redis staging                             ├── Redis production
  └── Utilisateurs staging                      └── Utilisateurs production
```

---

## Phase 1 — Pré-migration (J-1)

### 1.1 Backup complet du staging

```bash
# Backup PostgreSQL staging
kubectl exec -n veylant deploy/postgres -- \
  pg_dump -U veylant veylant_db | gzip > backup_staging_$(date +%Y%m%d).sql.gz

# Vérifier le backup
gunzip -t backup_staging_$(date +%Y%m%d).sql.gz && echo "Backup OK"

# Uploader vers S3 (conservation pendant la migration)
aws s3 cp backup_staging_$(date +%Y%m%d).sql.gz \
  s3://veylant-backups-production/migration/
```

### 1.2 Inventaire des utilisateurs à migrer

```bash
# Exporter la liste des utilisateurs Keycloak staging
kubectl exec -n keycloak deploy/keycloak -- \
  /opt/keycloak/bin/kcadm.sh get users \
  -r veylant-staging \
  --server http://localhost:8080 \
  --realm master \
  --user admin --password admin \
  > users_staging.json

# Compter les utilisateurs actifs (30 derniers jours)
psql "$STAGING_DB_URL" -c \
  "SELECT COUNT(*) FROM users WHERE last_login > NOW() - INTERVAL '30 days';"
```

### 1.3 Validation de l'environnement production

```bash
# Vérifier que le cluster production est opérationnel
kubectl get nodes -n veylant --context=production
kubectl get pods -n veylant --context=production

# Vérifier la connectivité API production
curl -sf https://api.veylant.ai/healthz | jq .

# Vérifier Keycloak production
curl -sf https://auth.veylant.ai/realms/veylant/.well-known/openid-configuration | jq .issuer

# Confirmer le backup automatique actif
kubectl get cronjob veylant-postgres-backup -n veylant --context=production
```

### 1.4 Communication client

- [ ] Envoyer email de notification J-1 (fenêtre de maintenance, impact estimé)
- [ ] Confirmer contact technique côté client disponible pendant la migration
- [ ] Partager le runbook rollback avec le client

---

## Phase 2 — Migration des données PostgreSQL

### 2.1 Export depuis staging

```bash
# Export complet avec données clients seulement (pas les configs système)
pg_dump \
  --host="$STAGING_DB_HOST" \
  --username="$STAGING_DB_USER" \
  --dbname="$STAGING_DB_NAME" \
  --table=users \
  --table=api_keys \
  --table=routing_rules \
  --table=gdpr_processing_registry \
  --table=ai_act_classifications \
  --format=custom \
  --no-privileges \
  --no-owner \
  -f migration_data.dump

echo "Export size: $(du -sh migration_data.dump)"
```

### 2.2 Import vers production

```bash
# Appliquer les migrations DDL d'abord (production doit être à jour)
kubectl exec -n veylant deploy/veylant-proxy --context=production -- \
  /app/proxy migrate up

# Import des données
pg_restore \
  --host="$PROD_DB_HOST" \
  --username="$PROD_DB_USER" \
  --dbname="$PROD_DB_NAME" \
  --no-privileges \
  --no-owner \
  --clean \
  --if-exists \
  -v \
  migration_data.dump

# Vérifier l'intégrité
psql "$PROD_DB_URL" -c "SELECT COUNT(*) FROM users;"
psql "$PROD_DB_URL" -c "SELECT COUNT(*) FROM routing_rules;"
```

### 2.3 Vérification post-import

```bash
# Comparer les compteurs staging vs production
STAGING_USERS=$(psql "$STAGING_DB_URL" -t -c "SELECT COUNT(*) FROM users;")
PROD_USERS=$(psql "$PROD_DB_URL" -t -c "SELECT COUNT(*) FROM users;")

echo "Staging users: $STAGING_USERS | Production users: $PROD_USERS"

if [ "$STAGING_USERS" != "$PROD_USERS" ]; then
  echo "ERROR: User count mismatch — abort migration"
  exit 1
fi
```

---

## Phase 3 — Reconfiguration Keycloak Production

### 3.1 Création du realm production

```bash
# Se connecter à Keycloak production
KEYCLOAK_URL="https://auth.veylant.ai"
KEYCLOAK_ADMIN_TOKEN=$(curl -s \
  -d "client_id=admin-cli" \
  -d "username=admin" \
  -d "password=$KEYCLOAK_ADMIN_PASSWORD" \
  -d "grant_type=password" \
  "$KEYCLOAK_URL/realms/master/protocol/openid-connect/token" | jq -r .access_token)

# Importer la configuration du realm depuis staging
# (exportée au format JSON lors de la phase 1.2)
curl -sf -X POST \
  -H "Authorization: Bearer $KEYCLOAK_ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d @realm-export.json \
  "$KEYCLOAK_URL/admin/realms"
```

### 3.2 Import des utilisateurs

```bash
# Importer les utilisateurs avec leurs rôles
# Note: les mots de passe ne peuvent pas être migrés — les utilisateurs recevront un email de reset
for user in $(jq -r '.[].id' users_staging.json); do
  USER_DATA=$(jq --arg id "$user" '.[] | select(.id == $id)' users_staging.json)
  curl -sf -X POST \
    -H "Authorization: Bearer $KEYCLOAK_ADMIN_TOKEN" \
    -H "Content-Type: application/json" \
    -d "$USER_DATA" \
    "$KEYCLOAK_URL/admin/realms/veylant/users"
done

echo "Imported $(jq length users_staging.json) users"
```

### 3.3 Réinitialisation des mots de passe

```bash
# Envoyer un email de reset de mot de passe à tous les utilisateurs migrés
USER_IDS=$(curl -sf \
  -H "Authorization: Bearer $KEYCLOAK_ADMIN_TOKEN" \
  "$KEYCLOAK_URL/admin/realms/veylant/users?max=1000" | jq -r '.[].id')

for USER_ID in $USER_IDS; do
  curl -sf -X PUT \
    -H "Authorization: Bearer $KEYCLOAK_ADMIN_TOKEN" \
    -H "Content-Type: application/json" \
    -d '["UPDATE_PASSWORD"]' \
    "$KEYCLOAK_URL/admin/realms/veylant/users/$USER_ID/execute-actions-email"
  sleep 0.1  # Rate limit emails
done
```

---

## Phase 4 — Validation

### 4.1 Smoke tests API

```bash
# Obtenir un token de test (compte admin pré-créé)
TOKEN=$(curl -sf \
  -d "client_id=veylant-api" \
  -d "username=admin-test@veylant.ai" \
  -d "password=$TEST_ADMIN_PASSWORD" \
  -d "grant_type=password" \
  "https://auth.veylant.ai/realms/veylant/protocol/openid-connect/token" | jq -r .access_token)

# Test endpoints principaux
curl -sf -H "Authorization: Bearer $TOKEN" https://api.veylant.ai/v1/admin/users | jq length
curl -sf -H "Authorization: Bearer $TOKEN" https://api.veylant.ai/v1/admin/routing-rules | jq length

# Test proxy (avec model user-role)
curl -sf -X POST \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"model":"gpt-4o-mini","messages":[{"role":"user","content":"Hello"}]}' \
  https://api.veylant.ai/v1/chat/completions | jq .choices[0].message.content

echo "Smoke tests passed"
```

### 4.2 Validation des audit logs

```bash
# Vérifier que les logs sont bien envoyés à ClickHouse
curl -sf -H "Authorization: Bearer $TOKEN" \
  "https://api.veylant.ai/v1/admin/logs?limit=5" | jq '.[].request_id'
```

### 4.3 Validation du dashboard

```bash
# Ouvrir le dashboard client et vérifier les métriques
open "https://dashboard.veylant.ai"
# Vérifier manuellement : graphiques RPS, latence, erreurs, PII
```

---

## Phase 5 — Cutover SSO (Go-Live)

### 5.1 Mise à jour des URLs côté client

Informer le contact technique du client de mettre à jour :

| Paramètre | Staging | Production |
|-----------|---------|------------|
| `base_url` OpenAI SDK | `https://api-staging.veylant.ai/v1` | `https://api.veylant.ai/v1` |
| OIDC Issuer (si SAML) | `https://auth-staging.veylant.ai/realms/veylant` | `https://auth.veylant.ai/realms/veylant` |
| Dashboard | `https://dashboard-staging.veylant.ai` | `https://dashboard.veylant.ai` |

### 5.2 Mise à jour CORS production

```bash
# Ajouter le domaine dashboard client dans config.yaml production
# Exemple Client B (RH Conseil) : dashboard sur dashboard.rh-conseil.fr
kubectl edit configmap veylant-proxy-config -n veylant --context=production
# Ajouter sous server.allowed_origins:
#   - "https://dashboard.rh-conseil.fr"

# Redémarrer le proxy pour prendre en compte la nouvelle config
kubectl rollout restart deployment/veylant-proxy-blue -n veylant --context=production
kubectl rollout status deployment/veylant-proxy-blue -n veylant --context=production
```

### 5.3 Confirmation Go-Live

- [ ] Envoyer email de confirmation au client : migration réussie
- [ ] Planifier NPS de suivi J+7
- [ ] Archiver le dump staging utilisé pour la migration

---

## Rollback

### Rollback Phase 2 (avant cutover)

```bash
# Restaurer la base production depuis le backup staging
pg_restore \
  --host="$PROD_DB_HOST" \
  --username="$PROD_DB_USER" \
  --dbname="$PROD_DB_NAME" \
  --clean \
  migration_data.dump

echo "Rollback Phase 2 terminé — base production restaurée"
```

### Rollback Phase 5 (après cutover)

```bash
# Rediriger le trafic vers staging (intervention DNS)
# Contact ops@veylant.ai immédiatement

# Informer le client : retour en staging, investigation en cours
# ETA rollback DNS : < 5 minutes (TTL court configuré en préparation)
```

---

## Checklist finale

- [ ] Backup staging conservé 30 jours
- [ ] Tous les utilisateurs ont reçu l'email de reset mot de passe
- [ ] Smoke tests API passés
- [ ] Dashboard client accessible
- [ ] CORS mis à jour avec domaine client
- [ ] NPS suivi planifié J+7
- [ ] Staging désactivé après 2 semaines (coûts)
