# AUDIT COMPLET: veza-backend-api Broken Pipe & Fichier .env Manquant dans Incus **Date**: 2025-01-27 **Problème**: veza-backend-api broken pipe. can't find .env file inside the dedicated incus container for the incus deploy **Priorité**: P0 - CRITIQUE (bloque le déploiement) --- ## 1. RÉSUMÉ EXÉCUTIF ### 1.1 Problème Principal Le service `veza-backend-api` déployé dans un conteneur Incus rencontre deux problèmes critiques: 1. **Broken Pipe Error**: Erreurs de "broken pipe" lors de l'écriture des logs 2. **Fichier .env Manquant**: L'application ne trouve pas le fichier `.env` dans le conteneur ### 1.2 Impact - ❌ Le service ne démarre pas correctement - ❌ Les logs ne sont pas capturés par systemd/journald - ❌ L'application ne peut pas charger les variables d'environnement - ❌ Le déploiement Incus échoue silencieusement --- ## 2. ANALYSE DÉTAILLÉE ### 2.1 Architecture Actuelle du Déploiement #### 2.1.1 Fichiers de Configuration ``` config/incus/env/backend-api.env # Source (sur l'hôte) ↓ (copié via incus file push) /etc/veza/backend-api.env # Destination (dans le conteneur) ``` #### 2.1.2 Service Systemd ```ini [Service] WorkingDirectory=/opt/veza/backend-api ExecStart=/opt/veza/backend-api/start-backend-api.sh EnvironmentFile=/etc/veza/backend-api.env StandardOutput=journal StandardError=journal ``` #### 2.1.3 Script Wrapper ```bash #!/bin/bash # start-backend-api.sh source /etc/veza/backend-api.env exec /usr/local/bin/veza-backend-api ``` #### 2.1.4 Application Go ```go // main.go _ = godotenv.Load() // Cherche .env dans le working directory // config.NewConfig() LoadEnvFiles(env) // Cherche .env.{env} et .env dans le working directory ``` ### 2.2 Problèmes Identifiés #### 2.2.1 Problème #1: Incohérence des Chemins de Fichiers .env **Symptôme**: L'application Go cherche un fichier `.env` dans `/opt/veza/backend-api/` mais le fichier est à `/etc/veza/backend-api.env` **Cause Racine**: - Le script de déploiement copie le fichier à `/etc/veza/backend-api.env` - Systemd charge les variables via `EnvironmentFile=/etc/veza/backend-api.env` - Le wrapper script charge les variables via `source /etc/veza/backend-api.env` - **MAIS** l'application Go fait `godotenv.Load()` qui cherche `.env` dans le working directory (`/opt/veza/backend-api/`) - L'application Go fait aussi `LoadEnvFiles(env)` qui cherche `.env.production` et `.env` dans le working directory **Impact**: - Si les variables sont déjà chargées par systemd/wrapper, `godotenv.Load()` échoue silencieusement (pas d'erreur si fichier absent) - Si les variables ne sont PAS chargées par systemd/wrapper, l'application ne trouve pas le fichier `.env` et échoue **Preuve**: ```go // veza-backend-api/cmd/api/main.go:50 _ = godotenv.Load() // Cherche .env dans le répertoire courant // veza-backend-api/internal/config/env_loader.go:24 _ = godotenv.Load() // Cherche .env dans le répertoire courant ``` #### 2.2.2 Problème #2: Broken Pipe Error **Symptôme**: Erreurs "broken pipe" lors de l'écriture des logs **Cause Racine**: - Systemd/journald peut fermer le pipe stdout/stderr si le service démarre trop vite - Le wrapper script peut écrire sur stdout/stderr avant que systemd ne soit prêt - L'application Go peut essayer d'écrire sur stdout/stderr après que journald ait fermé le pipe **Impact**: - Les logs ne sont pas capturés correctement - L'application peut crasher si les erreurs de broken pipe ne sont pas gérées **Preuve**: - Le code Go a déjà des mécanismes pour gérer les broken pipe (dans `logging/logger.go`) - Mais le wrapper script et les premières lignes de `main.go` peuvent écrire sur stdout/stderr avant que le logger ne soit initialisé #### 2.2.3 Problème #3: Vérification Insuffisante du Fichier .env **Symptôme**: Le script de déploiement vérifie que le fichier existe mais ne vérifie pas qu'il est accessible par l'application **Cause Racine**: - Le script `deploy-service-native.sh` vérifie que le fichier existe (`test -f`) - Le script vérifie que le fichier est lisible (`test -r`) - **MAIS** le script ne vérifie pas que le fichier est accessible depuis le working directory de l'application - Le script ne crée pas de lien symbolique ou copie vers le working directory **Impact**: - L'application Go ne peut pas charger le fichier `.env` même si les variables sont chargées par systemd #### 2.2.4 Problème #4: Ordre de Chargement des Variables d'Environnement **Symptôme**: Les variables peuvent être chargées dans le mauvais ordre **Ordre Actuel**: 1. Systemd charge `/etc/veza/backend-api.env` (via `EnvironmentFile`) 2. Wrapper script charge `/etc/veza/backend-api.env` (via `source`) 3. Application Go charge `.env` dans working directory (via `godotenv.Load()`) 4. Application Go charge `.env.{env}` et `.env` (via `LoadEnvFiles()`) **Problème**: - Si le fichier `.env` n'existe pas dans le working directory, `godotenv.Load()` échoue silencieusement - Si les variables ne sont pas chargées par systemd/wrapper, l'application échoue --- ## 3. CAUSES RACINES ### 3.1 Cause Racine #1: Architecture Incohérente - **Problème**: Mélange de deux approches (systemd EnvironmentFile + godotenv.Load()) - **Solution**: Choisir une seule approche et l'appliquer de manière cohérente ### 3.2 Cause Racine #2: Broken Pipe Non Géré au Démarrage - **Problème**: Écriture sur stdout/stderr avant que journald ne soit prêt - **Solution**: Rediriger stdout/stderr vers journald dès le début ou utiliser un logger qui gère les broken pipe ### 3.3 Cause Racine #3: Vérification Incomplète - **Problème**: Le script de déploiement ne vérifie pas que l'application peut accéder au fichier .env - **Solution**: Créer un fichier `.env` dans le working directory ou modifier l'application pour chercher le fichier au bon endroit ### 3.4 Cause Racine #4: Documentation Manquante - **Problème**: Pas de documentation claire sur où le fichier .env doit être et comment il est chargé - **Solution**: Documenter clairement l'architecture de chargement des variables d'environnement --- ## 4. SCÉNARIOS D'ÉCHEC ### 4.1 Scénario 1: Fichier .env Non Copié ``` 1. Script de déploiement échoue silencieusement lors de la copie 2. Fichier /etc/veza/backend-api.env n'existe pas 3. Systemd ne charge pas les variables (mais ne signale pas d'erreur si EnvironmentFile est optionnel) 4. Wrapper script échoue avec "No such file or directory" 5. Application ne démarre pas ``` ### 4.2 Scénario 2: Fichier .env Non Lisible ``` 1. Fichier copié mais permissions incorrectes 2. Systemd peut charger les variables (root) 3. Wrapper script peut charger les variables (root) 4. Application Go cherche .env dans working directory (n'existe pas) 5. Application démarre mais avec variables manquantes ``` ### 4.3 Scénario 3: Broken Pipe au Démarrage ``` 1. Systemd démarre le service 2. Wrapper script écrit sur stdout/stderr 3. Journald n'est pas encore prêt → broken pipe 4. Application Go essaie d'écrire sur stdout/stderr → broken pipe 5. Application crash ou logs perdus ``` ### 4.4 Scénario 4: Variables Non Chargées ``` 1. Fichier .env copié correctement 2. Systemd charge les variables 3. Wrapper script charge les variables 4. Application Go cherche .env dans working directory (n'existe pas) 5. Si variables critiques manquantes → application échoue ``` --- ## 5. POINTS DE VÉRIFICATION ### 5.1 Vérifications à Effectuer #### 5.1.1 Dans le Conteneur ```bash # Vérifier que le fichier existe incus exec veza-backend-api -- test -f /etc/veza/backend-api.env && echo "OK" || echo "FAIL" # Vérifier les permissions incus exec veza-backend-api -- ls -la /etc/veza/backend-api.env # Vérifier le contenu incus exec veza-backend-api -- cat /etc/veza/backend-api.env # Vérifier que systemd peut le charger incus exec veza-backend-api -- systemctl show veza-backend-api --property=EnvironmentFiles # Vérifier le working directory incus exec veza-backend-api -- pwd incus exec veza-backend-api -- ls -la /opt/veza/backend-api/ # Vérifier les variables d'environnement chargées incus exec veza-backend-api -- systemctl show veza-backend-api --property=Environment ``` #### 5.1.2 Logs Systemd ```bash # Vérifier les logs du service incus exec veza-backend-api -- journalctl -u veza-backend-api -n 50 # Vérifier les erreurs incus exec veza-backend-api -- journalctl -u veza-backend-api -p err # Vérifier les broken pipe incus exec veza-backend-api -- journalctl -u veza-backend-api | grep -i "broken pipe" ``` #### 5.1.3 Status du Service ```bash # Vérifier le status incus exec veza-backend-api -- systemctl status veza-backend-api # Vérifier si le service est actif incus exec veza-backend-api -- systemctl is-active veza-backend-api # Vérifier les erreurs de démarrage incus exec veza-backend-api -- systemctl status veza-backend-api | grep -i error ``` --- ## 6. SOLUTIONS PROPOSÉES ### 6.1 Solution 1: Créer un Fichier .env dans le Working Directory (RECOMMANDÉ) **Avantages**: - Compatible avec l'application Go existante - Pas de modification du code Go - Simple à implémenter **Inconvénients**: - Duplication du fichier (mais acceptable pour la cohérence) **Implémentation**: 1. Modifier `deploy-service-native.sh` pour copier aussi le fichier vers `/opt/veza/backend-api/.env` 2. Vérifier que le fichier est créé et lisible ### 6.2 Solution 2: Modifier l'Application Go pour Chercher le Fichier au Bon Endroit **Avantages**: - Pas de duplication - Architecture plus propre **Inconvénients**: - Modification du code Go - Plus complexe à maintenir **Implémentation**: 1. Modifier `env_loader.go` pour chercher le fichier à `/etc/veza/backend-api.env` en premier 2. Fallback vers `.env` dans le working directory ### 6.3 Solution 3: Utiliser Uniquement Systemd EnvironmentFile **Avantages**: - Architecture simple - Pas de duplication - Variables chargées avant le démarrage de l'application **Inconvénients**: - Nécessite de supprimer `godotenv.Load()` du code Go - Peut casser le développement local **Implémentation**: 1. Supprimer `godotenv.Load()` de `main.go` 2. Supprimer `LoadEnvFiles()` de `config.NewConfig()` 3. S'assurer que systemd charge toutes les variables nécessaires ### 6.4 Solution 4: Gérer les Broken Pipe au Démarrage **Avantages**: - Résout le problème des broken pipe - Améliore la robustesse **Implémentation**: 1. Rediriger stdout/stderr vers journald dès le début du wrapper script 2. S'assurer que le logger Go gère les broken pipe (déjà fait) 3. Éviter d'écrire sur stdout/stderr avant que le logger ne soit initialisé --- ## 7. RECOMMANDATION FINALE **Solution Recommandée**: Combinaison de Solution 1 + Solution 4 1. **Créer un fichier `.env` dans le working directory** pour compatibilité avec l'application Go 2. **Gérer les broken pipe** en redirigeant stdout/stderr vers journald dès le début 3. **Améliorer les vérifications** dans le script de déploiement 4. **Documenter** l'architecture de chargement des variables d'environnement **Raison**: - Compatible avec l'application Go existante - Résout les deux problèmes (broken pipe + fichier .env) - Minimal impact sur le code existant - Facile à maintenir --- ## 8. RISQUES ET MITIGATION ### 8.1 Risque 1: Duplication du Fichier .env **Mitigation**: Le fichier est petit et la duplication est acceptable pour la cohérence ### 8.2 Risque 2: Modification du Script de Déploiement **Mitigation**: Tester le script sur un conteneur de test avant production ### 8.3 Risque 3: Régression sur le Développement Local **Mitigation**: S'assurer que les modifications n'affectent pas le développement local --- ## 9. TESTS DE VALIDATION ### 9.1 Tests à Effectuer 1. **Test 1: Déploiement Complet** ```bash ./config/incus/deploy-service-native.sh backend-api ./config/incus/verify-deployment.sh ``` 2. **Test 2: Vérification du Fichier .env** ```bash incus exec veza-backend-api -- test -f /etc/veza/backend-api.env incus exec veza-backend-api -- test -f /opt/veza/backend-api/.env ``` 3. **Test 3: Vérification des Variables** ```bash incus exec veza-backend-api -- env | grep DATABASE_URL incus exec veza-backend-api -- env | grep JWT_SECRET ``` 4. **Test 4: Vérification des Logs** ```bash incus exec veza-backend-api -- journalctl -u veza-backend-api -n 20 incus exec veza-backend-api -- journalctl -u veza-backend-api | grep -i "broken pipe" ``` 5. **Test 5: Test de Santé** ```bash incus exec veza-backend-api -- curl -f http://localhost:8080/health ``` --- ## 10. RÉFÉRENCES ### 10.1 Fichiers Concernés - `config/incus/deploy-service-native.sh` (lignes 315-358) - `config/incus/systemd/veza-backend-api.service` - `config/incus/scripts/start-backend-api.sh` - `veza-backend-api/cmd/api/main.go` (ligne 50) - `veza-backend-api/internal/config/env_loader.go` - `veza-backend-api/internal/config/config.go` (ligne 210) ### 10.2 Documentation - Systemd EnvironmentFile: https://www.freedesktop.org/software/systemd/man/systemd.exec.html#EnvironmentFile= - godotenv: https://github.com/joho/godotenv - Incus file push: https://linuxcontainers.org/incus/docs/main/instances/#file-management --- **Fin de l'Audit**