From f9094f9a82eef27e8650af6c7fcb3660f994d7df Mon Sep 17 00:00:00 2001 From: Nathan Tesseyre Date: Thu, 21 May 2026 09:35:00 +0200 Subject: [PATCH 01/18] chore: add .venv to .gitignore --- .github/workflows/ci.yml | 36 +- .gitignore | 11 +- CODES-HTTP.md | 378 ++++++------ CONFLITS.md | 548 ++++++++--------- README.md | 1120 +++++++++++++++++------------------ RESSOURCES.md | 312 +++++----- config.json | 22 +- node-client/app.js | 75 +-- node-client/package.json | 24 +- python-api/app.py | 107 ++-- python-api/requirements.txt | 3 +- python-api/server.log | 38 +- 12 files changed, 1339 insertions(+), 1335 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8127191..61a1d71 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,18 +1,18 @@ -name: CI - -on: - push: - branches: [ "main" ] - pull_request: - branches: [ "main" ] - -jobs: - build: - runs-on: ubuntu-latest - - steps: - - name: Checkout du code - uses: actions/checkout@v4 - - - name: Exemple d'étape - run: echo "Ajoute tes étapes de build/test ici !" +name: CI + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout du code + uses: actions/checkout@v4 + + - name: Exemple d'étape + run: echo "Ajoute tes étapes de build/test ici !" diff --git a/.gitignore b/.gitignore index b8a7072..dc7c725 100644 --- a/.gitignore +++ b/.gitignore @@ -46,13 +46,4 @@ terraform.rc .DS_Store # Ignorer les .venv -.venv - -# Ignorer le dossier node_modules -node_modules - -logs - -email-template.md - -venv \ No newline at end of file +.venv \ No newline at end of file diff --git a/CODES-HTTP.md b/CODES-HTTP.md index 772fc66..e6247be 100644 --- a/CODES-HTTP.md +++ b/CODES-HTTP.md @@ -1,189 +1,189 @@ -# Les codes HTTP — Guide pratique DevOps - -Quand votre client Node.js appelle l'API Python, les deux programmes communiquent via HTTP. -Chaque réponse contient un **code à 3 chiffres** qui indique si la requête a réussi ou échoué. - -Le premier chiffre indique la catégorie : -- **2xx** — Succès -- **4xx** — Erreur due au client (mauvaise requête, mauvaise URL, pas autorisé…) -- **5xx** — Erreur due au serveur (le code a planté, la base de données est inaccessible…) - ---- - -## 2xx — Succès - -### 200 OK -La requête a réussi. C'est la réponse attendue quand tout fonctionne. - -``` -GET /api/logs → 200 OK -{ "error_count": 5, "warning_count": 4 ... } -``` - -Dans ce TP, c'est le code que retourne Flask quand les logs sont analysés sans erreur. - -### 201 Created -La ressource a été créée avec succès. Utilisé en réponse à une requête POST qui crée un objet (un utilisateur, un ticket, un déploiement…). Vous ne le verrez pas dans ce TP, mais vous le croiserez souvent dans des APIs Azure. - -### 204 No Content -La requête a réussi mais le serveur ne retourne rien. Courant pour les requêtes de suppression (DELETE). - ---- - -## 4xx — Erreur côté client - -Ces erreurs signifient que **vous avez fait quelque chose d'incorrect** dans votre requête. - -### 400 Bad Request -La requête est malformée — paramètre manquant, format incorrect, JSON invalide. - -``` -Exemple : vous envoyez un JSON avec une virgule en trop -→ 400 Bad Request : "invalid JSON body" -``` - -### 401 Unauthorized -Vous n'êtes pas authentifié. Le serveur vous demande de vous identifier avant d'accéder à cette ressource. - -``` -Exemple : appel à une API Azure sans token d'authentification -→ 401 Unauthorized : "Missing or invalid Bearer token" -``` - -### 403 Forbidden -Vous êtes authentifié, mais vous n'avez pas les droits pour cette action. Contrairement au 401, s'identifier à nouveau ne changera rien. - -``` -Exemple : un compte de service sans les permissions RBAC Azure nécessaires -→ 403 Forbidden : "Insufficient permissions on resource group" -``` - -> En DevSecOps, 401 et 403 sont des codes à surveiller de près dans les logs : une accumulation peut signaler une tentative d'intrusion. - -### 404 Not Found -La ressource demandée n'existe pas à cette URL. - -``` -Exemple : curl http://localhost:5000/api/log (sans 's') -→ 404 Not Found -``` - -C'est une erreur fréquente quand on tape mal une route Flask. Vérifiez l'URL et le décorateur `@app.route()` dans le code. - -### 405 Method Not Allowed -Vous utilisez la mauvaise méthode HTTP (GET, POST, PUT, DELETE…). - -``` -Exemple : envoyer un POST sur une route qui n'accepte que GET -→ 405 Method Not Allowed -``` - -### 422 Unprocessable Entity -La requête est bien formée mais les données envoyées sont sémantiquement incorrectes. - -``` -Exemple : envoyer un champ "age" avec la valeur "bonjour" au lieu d'un nombre -→ 422 Unprocessable Entity -``` - ---- - -## 5xx — Erreur côté serveur - -Ces erreurs signifient que **le serveur a rencontré un problème**. Ce n'est pas de votre faute en tant que client — c'est le code serveur qui a planté. - -### 500 Internal Server Error -Le serveur a rencontré une erreur inattendue. C'est le code générique quand une exception Python non gérée survient dans Flask. - -``` -Exemple : votre app.py a un bug Python non corrigé -→ 500 Internal Server Error -``` - -Dans ce TP, si vous voyez un 500, cela signifie que l'API Flask tourne mais qu'elle plante au moment de traiter la requête. Lisez les logs dans le terminal où Python s'exécute — l'erreur Python y sera affichée. - -### 502 Bad Gateway -Un serveur intermédiaire (proxy, load balancer) n'a pas pu obtenir de réponse valide du serveur en amont. - -``` -Exemple courant : votre conteneur Docker ou pod Kubernetes a planté, -mais le load balancer Azure est toujours actif -→ 502 Bad Gateway -``` - -### 503 Service Unavailable -Le serveur est temporairement indisponible — surchargé ou en cours de maintenance. - -``` -Exemple : Azure App Service redémarre après un déploiement -→ 503 Service Unavailable (pendant quelques secondes) -``` - -### 504 Gateway Timeout -Le serveur intermédiaire n'a pas reçu de réponse du serveur en amont dans le temps imparti. - -``` -Exemple : une requête à une base de données Azure SQL prend trop longtemps -→ 504 Gateway Timeout -``` - ---- - -## Erreurs de connexion Node.js — avant même le code HTTP - -Certaines erreurs surviennent avant qu'une réponse HTTP soit reçue. Vous les verrez dans le terminal Node sous forme de codes d'erreur système. - -### ECONNREFUSED -Le serveur n'accepte aucune connexion sur ce port. Cela signifie que le serveur n'est pas démarré, ou que vous utilisez le mauvais port. - -``` -Error: connect ECONNREFUSED 127.0.0.1:5001 -→ L'API Python n'est pas démarrée, ou elle écoute sur un autre port -``` - -C'est l'erreur que vous obtiendrez dans ce TP si le bug du port (5001 vs 5000) n'est pas corrigé. - -### ENOTFOUND -Le nom de domaine ne peut pas être résolu — l'adresse n'existe pas sur le réseau. - -``` -Error: getaddrinfo ENOTFOUND mon-api.azuretech.fr -→ L'URL est incorrecte ou le service n'est pas déployé -``` - -### ETIMEDOUT -La connexion a été tentée mais aucune réponse n'est arrivée dans le délai imparti. - -``` -Error: connect ETIMEDOUT -→ Le serveur existe mais ne répond pas (pare-feu, surcharge, réseau lent) -``` - ---- - -## Résumé visuel - -``` -Requête HTTP - │ - ├── 2xx ──► Succès ✅ Tout va bien - │ - ├── 4xx ──► Erreur client ⚠️ Vérifiez votre requête (URL, méthode, auth) - │ - ├── 5xx ──► Erreur serveur ❌ Le serveur a planté — lisez ses logs - │ - └── ERR_ ──► Pas de réponse 🔌 Le serveur est inaccessible (port, réseau) -``` - ---- - -## Dans Azure, où voir ces codes ? - -En production sur Azure, ces codes apparaissent dans plusieurs endroits : - -- **Azure Monitor** — tableau de bord des codes HTTP par période -- **Application Insights** — détail requête par requête avec la durée -- **Log Analytics** — requêtes KQL pour filtrer les 5xx ou les 4xx -- **Azure API Management** — gateway qui centralise tous les appels API et leurs codes de retour - -Apprendre à lire ces codes maintenant, c'est poser les bases de la surveillance d'infrastructure que vous pratiquerez tout au long de cette formation. +# Les codes HTTP — Guide pratique DevOps + +Quand votre client Node.js appelle l'API Python, les deux programmes communiquent via HTTP. +Chaque réponse contient un **code à 3 chiffres** qui indique si la requête a réussi ou échoué. + +Le premier chiffre indique la catégorie : +- **2xx** — Succès +- **4xx** — Erreur due au client (mauvaise requête, mauvaise URL, pas autorisé…) +- **5xx** — Erreur due au serveur (le code a planté, la base de données est inaccessible…) + +--- + +## 2xx — Succès + +### 200 OK +La requête a réussi. C'est la réponse attendue quand tout fonctionne. + +``` +GET /api/logs → 200 OK +{ "error_count": 5, "warning_count": 4 ... } +``` + +Dans ce TP, c'est le code que retourne Flask quand les logs sont analysés sans erreur. + +### 201 Created +La ressource a été créée avec succès. Utilisé en réponse à une requête POST qui crée un objet (un utilisateur, un ticket, un déploiement…). Vous ne le verrez pas dans ce TP, mais vous le croiserez souvent dans des APIs Azure. + +### 204 No Content +La requête a réussi mais le serveur ne retourne rien. Courant pour les requêtes de suppression (DELETE). + +--- + +## 4xx — Erreur côté client + +Ces erreurs signifient que **vous avez fait quelque chose d'incorrect** dans votre requête. + +### 400 Bad Request +La requête est malformée — paramètre manquant, format incorrect, JSON invalide. + +``` +Exemple : vous envoyez un JSON avec une virgule en trop +→ 400 Bad Request : "invalid JSON body" +``` + +### 401 Unauthorized +Vous n'êtes pas authentifié. Le serveur vous demande de vous identifier avant d'accéder à cette ressource. + +``` +Exemple : appel à une API Azure sans token d'authentification +→ 401 Unauthorized : "Missing or invalid Bearer token" +``` + +### 403 Forbidden +Vous êtes authentifié, mais vous n'avez pas les droits pour cette action. Contrairement au 401, s'identifier à nouveau ne changera rien. + +``` +Exemple : un compte de service sans les permissions RBAC Azure nécessaires +→ 403 Forbidden : "Insufficient permissions on resource group" +``` + +> En DevSecOps, 401 et 403 sont des codes à surveiller de près dans les logs : une accumulation peut signaler une tentative d'intrusion. + +### 404 Not Found +La ressource demandée n'existe pas à cette URL. + +``` +Exemple : curl http://localhost:5000/api/log (sans 's') +→ 404 Not Found +``` + +C'est une erreur fréquente quand on tape mal une route Flask. Vérifiez l'URL et le décorateur `@app.route()` dans le code. + +### 405 Method Not Allowed +Vous utilisez la mauvaise méthode HTTP (GET, POST, PUT, DELETE…). + +``` +Exemple : envoyer un POST sur une route qui n'accepte que GET +→ 405 Method Not Allowed +``` + +### 422 Unprocessable Entity +La requête est bien formée mais les données envoyées sont sémantiquement incorrectes. + +``` +Exemple : envoyer un champ "age" avec la valeur "bonjour" au lieu d'un nombre +→ 422 Unprocessable Entity +``` + +--- + +## 5xx — Erreur côté serveur + +Ces erreurs signifient que **le serveur a rencontré un problème**. Ce n'est pas de votre faute en tant que client — c'est le code serveur qui a planté. + +### 500 Internal Server Error +Le serveur a rencontré une erreur inattendue. C'est le code générique quand une exception Python non gérée survient dans Flask. + +``` +Exemple : votre app.py a un bug Python non corrigé +→ 500 Internal Server Error +``` + +Dans ce TP, si vous voyez un 500, cela signifie que l'API Flask tourne mais qu'elle plante au moment de traiter la requête. Lisez les logs dans le terminal où Python s'exécute — l'erreur Python y sera affichée. + +### 502 Bad Gateway +Un serveur intermédiaire (proxy, load balancer) n'a pas pu obtenir de réponse valide du serveur en amont. + +``` +Exemple courant : votre conteneur Docker ou pod Kubernetes a planté, +mais le load balancer Azure est toujours actif +→ 502 Bad Gateway +``` + +### 503 Service Unavailable +Le serveur est temporairement indisponible — surchargé ou en cours de maintenance. + +``` +Exemple : Azure App Service redémarre après un déploiement +→ 503 Service Unavailable (pendant quelques secondes) +``` + +### 504 Gateway Timeout +Le serveur intermédiaire n'a pas reçu de réponse du serveur en amont dans le temps imparti. + +``` +Exemple : une requête à une base de données Azure SQL prend trop longtemps +→ 504 Gateway Timeout +``` + +--- + +## Erreurs de connexion Node.js — avant même le code HTTP + +Certaines erreurs surviennent avant qu'une réponse HTTP soit reçue. Vous les verrez dans le terminal Node sous forme de codes d'erreur système. + +### ECONNREFUSED +Le serveur n'accepte aucune connexion sur ce port. Cela signifie que le serveur n'est pas démarré, ou que vous utilisez le mauvais port. + +``` +Error: connect ECONNREFUSED 127.0.0.1:5001 +→ L'API Python n'est pas démarrée, ou elle écoute sur un autre port +``` + +C'est l'erreur que vous obtiendrez dans ce TP si le bug du port (5001 vs 5000) n'est pas corrigé. + +### ENOTFOUND +Le nom de domaine ne peut pas être résolu — l'adresse n'existe pas sur le réseau. + +``` +Error: getaddrinfo ENOTFOUND mon-api.azuretech.fr +→ L'URL est incorrecte ou le service n'est pas déployé +``` + +### ETIMEDOUT +La connexion a été tentée mais aucune réponse n'est arrivée dans le délai imparti. + +``` +Error: connect ETIMEDOUT +→ Le serveur existe mais ne répond pas (pare-feu, surcharge, réseau lent) +``` + +--- + +## Résumé visuel + +``` +Requête HTTP + │ + ├── 2xx ──► Succès ✅ Tout va bien + │ + ├── 4xx ──► Erreur client ⚠️ Vérifiez votre requête (URL, méthode, auth) + │ + ├── 5xx ──► Erreur serveur ❌ Le serveur a planté — lisez ses logs + │ + └── ERR_ ──► Pas de réponse 🔌 Le serveur est inaccessible (port, réseau) +``` + +--- + +## Dans Azure, où voir ces codes ? + +En production sur Azure, ces codes apparaissent dans plusieurs endroits : + +- **Azure Monitor** — tableau de bord des codes HTTP par période +- **Application Insights** — détail requête par requête avec la durée +- **Log Analytics** — requêtes KQL pour filtrer les 5xx ou les 4xx +- **Azure API Management** — gateway qui centralise tous les appels API et leurs codes de retour + +Apprendre à lire ces codes maintenant, c'est poser les bases de la surveillance d'infrastructure que vous pratiquerez tout au long de cette formation. diff --git a/CONFLITS.md b/CONFLITS.md index f365e10..882409d 100644 --- a/CONFLITS.md +++ b/CONFLITS.md @@ -1,274 +1,274 @@ -# Gestion des conflits Git — Étape 7 - -**Durée estimée :** 45 min -**Prérequis :** avoir terminé les étapes 1 à 6 (bugs corrigés, PR ouverte) - ---- - -## Pourquoi les conflits arrivent-ils ? - -En équipe, plusieurs développeurs travaillent en parallèle sur les mêmes fichiers. -Quand deux personnes modifient les mêmes lignes d'un fichier sur des branches différentes, -Git ne sait pas quelle version garder — il vous demande de trancher. C'est un **conflit de fusion**. - -``` -Branche fix/marie Branche fix/karim - │ │ - modifie config.json modifie config.json - (même lignes) (même lignes) - │ │ - └──────────┬───────────────┘ - │ - CONFLIT - Git ne peut pas - merger seul -``` - -Ce n'est pas une erreur — c'est un mécanisme de sécurité. Git refuse d'écraser -le travail de quelqu'un sans votre accord explicite. - ---- - -## Ce qui va se passer dans ce TP - -1. Tout le monde a corrigé les bugs et ouvert une PR sur GitHub -2. Le formateur merge **la PR d'une seule personne** vers `main` -3. `main` est maintenant différent de votre branche locale -4. Quand vous essayez de mettre votre branche à jour → **conflit sur `config.json`** -5. Vous résolvez le conflit, poussez à nouveau → votre PR peut être mergée - ---- - -## Étape 7.1 — Préparer le conflit : compléter config.json - -Avant que le formateur merge la première PR, chaque apprenant doit -**ajouter son prénom** dans le tableau `"apprenants"` de `config.json`. - -Ouvrez `config.json` et modifiez le tableau : - -```json -{ - "projet": "TP-Git-Collaboratif", - "promotion": "DevSecOps Azure — Simplon", - "apprenants": ["Votre Prénom"], - "api": { - "port": 5000, - "host": "localhost", - "route": "/api/logs", - "log_file": "server.log" - } -} -``` - -Commitez cette modification : - -```bash -git add config.json -git commit -m "chore: ajout prénom dans config.json + correction port" -git push origin fix/prenom-debug-python-node -``` - ---- - -## Étape 7.2 — Le formateur merge une PR (signal de départ) - -Le formateur annonce quelle PR il merge sur `main`. -À partir de ce moment, `main` contient les corrections de cette personne — -y compris son prénom dans `config.json`. - -Votre branche, elle, contient votre prénom. -Les deux versions sont incompatibles sur la même ligne → conflit garanti. - ---- - -## Étape 7.3 — Récupérer les changements de main - -```bash -# Récupérer l'état actuel du dépôt distant sans modifier vos fichiers -git fetch origin - -# Vérifier la différence entre votre branche et main -git log --oneline origin/main..HEAD -``` - ---- - -## Étape 7.4 — Rebaser votre branche sur main - -Le **rebase** réapplique vos commits par-dessus les derniers commits de `main`. -C'est la méthode préférée en DevOps car elle produit un historique linéaire et propre. - -```bash -git rebase origin/main -``` - -Git va s'arrêter et afficher un message comme celui-ci : - -``` -CONFLICT (content): Merge conflict in config.json -error: could not apply abc1234... chore: ajout prénom dans config.json -hint: Resolve all conflicts manually, mark them as resolved with -hint: "git add/rm ", then run "git rebase --continue". -``` - ---- - -## Étape 7.5 — Comprendre les marqueurs de conflit - -Ouvrez `config.json` dans votre éditeur. Vous verrez quelque chose comme : - -```json -{ - "projet": "TP-Git-Collaboratif", - "promotion": "DevSecOps Azure — Simplon", -<<<<<<< HEAD - "apprenants": ["Marie"], -======= - "apprenants": ["Karim"], ->>>>>>> abc1234 (chore: ajout prénom dans config.json + correction port) - "api": { - "port": 5000, -``` - -**Décryptage des marqueurs :** - -| Marqueur | Signification | -|----------|---------------| -| `<<<<<<< HEAD` | Début de VOTRE version (celle sur main après rebase) | -| `=======` | Séparateur entre les deux versions | -| `>>>>>>> abc1234` | Fin de VOTRE commit en cours d'application | - -> Les deux versions ont corrigé le port à 5000 — c'est cohérent. -> Le seul vrai conflit est sur les prénoms : chacun n'a ajouté que le sien. - ---- - -## Étape 7.6 — Résoudre le conflit - -La résolution correcte ici est de **garder les deux prénoms**. -Supprimez les marqueurs et fusionnez manuellement le contenu : - -```json -{ - "projet": "TP-Git-Collaboratif", - "promotion": "DevSecOps Azure — Simplon", - "apprenants": ["Marie", "Karim"], - "api": { - "port": 5000, - "host": "localhost", - "route": "/api/logs", - "log_file": "server.log" - } -} -``` - -> Règle d'or : un conflit résolu ne doit contenir aucun marqueur -> (`<<<<<<<`, `=======`, `>>>>>>>`). S'il en reste un, le fichier est invalide. - ---- - -## Étape 7.7 — Valider et continuer le rebase - -```bash -# Vérifier que config.json ne contient plus de marqueurs -cat config.json - -# Marquer le conflit comme résolu -git add config.json - -# Continuer le rebase -git rebase --continue -``` - -Git vous demandera éventuellement de valider le message de commit (éditeur qui s'ouvre) — vous pouvez le laisser tel quel et fermer. - ---- - -## Étape 7.8 — Pousser la branche mise à jour - -Après un rebase, l'historique de votre branche a changé. -Il faut forcer le push — en utilisant `--force-with-lease` qui est plus sûr -que `--force` car il vérifie que personne d'autre n'a pushé entre-temps. - -```bash -git push --force-with-lease origin fix/prenom-debug-python-node -``` - -Votre PR sur GitHub se mettra à jour automatiquement. - ---- - -## Rebase vs Merge — quelle différence ? - -Il existe deux façons d'intégrer les changements de `main` dans votre branche : - -**`git rebase origin/main`** (recommandé en équipe) -- Réapplique vos commits par-dessus `main` -- Historique linéaire, plus lisible -- Nécessite un `push --force-with-lease` - -**`git merge origin/main`** (plus simple à apprendre) -- Crée un commit de fusion supplémentaire -- Historique non-linéaire mais plus sûr pour les débutants -- Push normal possible - -``` -Avant rebase / merge : - -main : A --- B --- C (commit de Karim) - \ -votre branche : D (votre commit) - -Après REBASE : -main : A --- B --- C --- D' (votre commit réappliqué) - -Après MERGE : -main : A --- B --- C ------- M (commit de merge) - \ / -votre branche : D -``` - -Pour ce TP, les deux approches sont acceptées. -En entreprise, la convention dépend de l'équipe — demandez toujours avant de pousser. - ---- - -## Commandes de secours - -Si vous êtes bloqué et voulez tout annuler : - -```bash -# Annuler le rebase en cours et revenir à l'état avant -git rebase --abort - -# Voir l'état actuel des fichiers en conflit -git status - -# Voir les différences dans un fichier conflictuel -git diff config.json -``` - ---- - -## Résumé des commandes - -```bash -git fetch origin # Récupérer les changements distants -git rebase origin/main # Rebaser sur main (peut créer un conflit) -# → éditer les fichiers conflictuels -git add config.json # Marquer comme résolu -git rebase --continue # Continuer le rebase -git push --force-with-lease origin # Pousser la branche mise à jour -``` - ---- - -## Bonnes pratiques à retenir - -**Faites des petits commits fréquents** — moins de risque de conflits massifs. - -**Rebasez régulièrement** — ne laissez pas votre branche diverger trop longtemps de `main`. Plus vous attendez, plus la résolution sera complexe. - -**Communiquez** — si vous savez que vous allez modifier un fichier critique partagé, prévenez l'équipe pour éviter de travailler simultanément sur les mêmes lignes. - -**Ne forcez jamais sur `main`** — le `push --force` n'est acceptable que sur votre propre branche de travail. Sur `main`, c'est une faute grave en équipe. +# Gestion des conflits Git — Étape 7 + +**Durée estimée :** 45 min +**Prérequis :** avoir terminé les étapes 1 à 6 (bugs corrigés, PR ouverte) + +--- + +## Pourquoi les conflits arrivent-ils ? + +En équipe, plusieurs développeurs travaillent en parallèle sur les mêmes fichiers. +Quand deux personnes modifient les mêmes lignes d'un fichier sur des branches différentes, +Git ne sait pas quelle version garder — il vous demande de trancher. C'est un **conflit de fusion**. + +``` +Branche fix/marie Branche fix/karim + │ │ + modifie config.json modifie config.json + (même lignes) (même lignes) + │ │ + └──────────┬───────────────┘ + │ + CONFLIT + Git ne peut pas + merger seul +``` + +Ce n'est pas une erreur — c'est un mécanisme de sécurité. Git refuse d'écraser +le travail de quelqu'un sans votre accord explicite. + +--- + +## Ce qui va se passer dans ce TP + +1. Tout le monde a corrigé les bugs et ouvert une PR sur GitHub +2. Le formateur merge **la PR d'une seule personne** vers `main` +3. `main` est maintenant différent de votre branche locale +4. Quand vous essayez de mettre votre branche à jour → **conflit sur `config.json`** +5. Vous résolvez le conflit, poussez à nouveau → votre PR peut être mergée + +--- + +## Étape 7.1 — Préparer le conflit : compléter config.json + +Avant que le formateur merge la première PR, chaque apprenant doit +**ajouter son prénom** dans le tableau `"apprenants"` de `config.json`. + +Ouvrez `config.json` et modifiez le tableau : + +```json +{ + "projet": "TP-Git-Collaboratif", + "promotion": "DevSecOps Azure — Simplon", + "apprenants": ["Votre Prénom"], + "api": { + "port": 5000, + "host": "localhost", + "route": "/api/logs", + "log_file": "server.log" + } +} +``` + +Commitez cette modification : + +```bash +git add config.json +git commit -m "chore: ajout prénom dans config.json + correction port" +git push origin fix/prenom-debug-python-node +``` + +--- + +## Étape 7.2 — Le formateur merge une PR (signal de départ) + +Le formateur annonce quelle PR il merge sur `main`. +À partir de ce moment, `main` contient les corrections de cette personne — +y compris son prénom dans `config.json`. + +Votre branche, elle, contient votre prénom. +Les deux versions sont incompatibles sur la même ligne → conflit garanti. + +--- + +## Étape 7.3 — Récupérer les changements de main + +```bash +# Récupérer l'état actuel du dépôt distant sans modifier vos fichiers +git fetch origin + +# Vérifier la différence entre votre branche et main +git log --oneline origin/main..HEAD +``` + +--- + +## Étape 7.4 — Rebaser votre branche sur main + +Le **rebase** réapplique vos commits par-dessus les derniers commits de `main`. +C'est la méthode préférée en DevOps car elle produit un historique linéaire et propre. + +```bash +git rebase origin/main +``` + +Git va s'arrêter et afficher un message comme celui-ci : + +``` +CONFLICT (content): Merge conflict in config.json +error: could not apply abc1234... chore: ajout prénom dans config.json +hint: Resolve all conflicts manually, mark them as resolved with +hint: "git add/rm ", then run "git rebase --continue". +``` + +--- + +## Étape 7.5 — Comprendre les marqueurs de conflit + +Ouvrez `config.json` dans votre éditeur. Vous verrez quelque chose comme : + +```json +{ + "projet": "TP-Git-Collaboratif", + "promotion": "DevSecOps Azure — Simplon", +<<<<<<< HEAD + "apprenants": ["Marie"], +======= + "apprenants": ["Karim"], +>>>>>>> abc1234 (chore: ajout prénom dans config.json + correction port) + "api": { + "port": 5000, +``` + +**Décryptage des marqueurs :** + +| Marqueur | Signification | +|----------|---------------| +| `<<<<<<< HEAD` | Début de VOTRE version (celle sur main après rebase) | +| `=======` | Séparateur entre les deux versions | +| `>>>>>>> abc1234` | Fin de VOTRE commit en cours d'application | + +> Les deux versions ont corrigé le port à 5000 — c'est cohérent. +> Le seul vrai conflit est sur les prénoms : chacun n'a ajouté que le sien. + +--- + +## Étape 7.6 — Résoudre le conflit + +La résolution correcte ici est de **garder les deux prénoms**. +Supprimez les marqueurs et fusionnez manuellement le contenu : + +```json +{ + "projet": "TP-Git-Collaboratif", + "promotion": "DevSecOps Azure — Simplon", + "apprenants": ["Marie", "Karim"], + "api": { + "port": 5000, + "host": "localhost", + "route": "/api/logs", + "log_file": "server.log" + } +} +``` + +> Règle d'or : un conflit résolu ne doit contenir aucun marqueur +> (`<<<<<<<`, `=======`, `>>>>>>>`). S'il en reste un, le fichier est invalide. + +--- + +## Étape 7.7 — Valider et continuer le rebase + +```bash +# Vérifier que config.json ne contient plus de marqueurs +cat config.json + +# Marquer le conflit comme résolu +git add config.json + +# Continuer le rebase +git rebase --continue +``` + +Git vous demandera éventuellement de valider le message de commit (éditeur qui s'ouvre) — vous pouvez le laisser tel quel et fermer. + +--- + +## Étape 7.8 — Pousser la branche mise à jour + +Après un rebase, l'historique de votre branche a changé. +Il faut forcer le push — en utilisant `--force-with-lease` qui est plus sûr +que `--force` car il vérifie que personne d'autre n'a pushé entre-temps. + +```bash +git push --force-with-lease origin fix/prenom-debug-python-node +``` + +Votre PR sur GitHub se mettra à jour automatiquement. + +--- + +## Rebase vs Merge — quelle différence ? + +Il existe deux façons d'intégrer les changements de `main` dans votre branche : + +**`git rebase origin/main`** (recommandé en équipe) +- Réapplique vos commits par-dessus `main` +- Historique linéaire, plus lisible +- Nécessite un `push --force-with-lease` + +**`git merge origin/main`** (plus simple à apprendre) +- Crée un commit de fusion supplémentaire +- Historique non-linéaire mais plus sûr pour les débutants +- Push normal possible + +``` +Avant rebase / merge : + +main : A --- B --- C (commit de Karim) + \ +votre branche : D (votre commit) + +Après REBASE : +main : A --- B --- C --- D' (votre commit réappliqué) + +Après MERGE : +main : A --- B --- C ------- M (commit de merge) + \ / +votre branche : D +``` + +Pour ce TP, les deux approches sont acceptées. +En entreprise, la convention dépend de l'équipe — demandez toujours avant de pousser. + +--- + +## Commandes de secours + +Si vous êtes bloqué et voulez tout annuler : + +```bash +# Annuler le rebase en cours et revenir à l'état avant +git rebase --abort + +# Voir l'état actuel des fichiers en conflit +git status + +# Voir les différences dans un fichier conflictuel +git diff config.json +``` + +--- + +## Résumé des commandes + +```bash +git fetch origin # Récupérer les changements distants +git rebase origin/main # Rebaser sur main (peut créer un conflit) +# → éditer les fichiers conflictuels +git add config.json # Marquer comme résolu +git rebase --continue # Continuer le rebase +git push --force-with-lease origin # Pousser la branche mise à jour +``` + +--- + +## Bonnes pratiques à retenir + +**Faites des petits commits fréquents** — moins de risque de conflits massifs. + +**Rebasez régulièrement** — ne laissez pas votre branche diverger trop longtemps de `main`. Plus vous attendez, plus la résolution sera complexe. + +**Communiquez** — si vous savez que vous allez modifier un fichier critique partagé, prévenez l'équipe pour éviter de travailler simultanément sur les mêmes lignes. + +**Ne forcez jamais sur `main`** — le `push --force` n'est acceptable que sur votre propre branche de travail. Sur `main`, c'est une faute grave en équipe. diff --git a/README.md b/README.md index 47fb19f..4c2e171 100644 --- a/README.md +++ b/README.md @@ -1,560 +1,560 @@ -# TP Git Collaboratif — Débogage Full-Stack Python + Node.js - -**Durée estimée :** 3h30 -**Niveau :** Intermédiaire -**Effectif :** 12 apprenants - -> Les ressources et explications complémentaires sont disponibles dans le fichier **[RESSOURCES.md](./RESSOURCES.md)**. - ---- - -## Avant de commencer — Comprendre les outils utilisés - -Si vous êtes en reconversion et que certains termes vous sont nouveaux, pas de panique. Voici l'essentiel à savoir avant de démarrer. - -**Python** est un langage de programmation très utilisé en DevOps pour automatiser des tâches : analyser des fichiers, interagir avec des APIs, lancer des scripts de déploiement. Dans ce TP, un script Python est chargé de lire un fichier de logs et d'en extraire les informations importantes. - -**Flask** est un framework Python qui permet de créer très simplement une API web. Concrètement, Flask transforme votre script Python en un petit serveur web que l'on peut interroger via une URL, comme on interrogerait un site. Ici, Flask va exposer les résultats de l'analyse des logs à l'adresse `http://localhost:5000/api/logs`. - -**Node.js** est un environnement qui permet d'exécuter du JavaScript en dehors d'un navigateur, directement dans le terminal. Il est très utilisé pour créer des outils en ligne de commande, des serveurs web ou, comme dans ce TP, des scripts qui consomment des APIs. - -**npm** (Node Package Manager) est le gestionnaire de paquets de Node.js. Il permet d'installer des bibliothèques tierces listées dans un fichier `package.json`, exactement comme `pip` installe les dépendances Python listées dans `requirements.txt`. - -**Une API REST** est un service web qui reçoit des requêtes HTTP et retourne des données, généralement au format JSON. Dans ce TP, Python joue le rôle du serveur (il fournit les données) et Node.js joue le rôle du client (il demande les données et les affiche). - -**Les logs serveur** sont des fichiers texte que les applications génèrent automatiquement pour enregistrer ce qui se passe : démarrages, erreurs, avertissements, requêtes reçues… En DevOps, analyser ces logs est une tâche quotidienne pour surveiller l'état d'une infrastructure. - ---- - -## Contexte du TP - -Vous êtes développeur·se DevOps chez **NexaCloud**. - -Suite à une mise à jour en production, deux scripts critiques tombent en erreur : - -- `python-api/app.py` — une API Flask qui lit les logs du serveur Azure et retourne une analyse JSON -- `node-client/app.js` — un client Node.js qui interroge cette API et affiche un rapport dans le terminal - -Ces deux scripts **communiquent ensemble** : Node appelle Python via HTTP. -Aucun des deux ne fonctionnera correctement tant que tous les bugs ne sont pas corrigés. - -Votre responsable technique vous demande de : -1. Cloner le dépôt sur votre machine -2. Reproduire les erreurs en exécutant les scripts -3. Identifier et corriger les bugs (8 au total) -4. Pousser vos corrections via une Pull Request -5. Envoyer un email de rapport à votre responsable - ---- - -## Architecture du projet - -``` -[Fichier de logs Azure] - server.log - | - v - python-api/app.py <- API Flask (port 5000) - (lit les logs, compte <- Lance avec : python app.py - les ERROR / WARNING / INFO) - | - | HTTP GET /api/logs - v - node-client/app.js <- Client Node.js - (affiche le rapport <- Lance avec : node app.js - dans le terminal) -``` - ---- - -## Structure du dépôt - -``` -TP-Git-Collaboratif/ -├── README.md <- Ce fichier -├── config.json <- Configuration partagée Python + Node (contient un bug) -├── email-template.md <- Template pour votre email de rapport -├── CONFLITS.md <- Guide de résolution des conflits Git (étape 7) -├── CORRECTION_FORMATEUR.md <- Réservé au formateur -├── python-api/ -│ ├── app.py <- API Flask (contient des bugs) -│ ├── requirements.txt <- Dépendances Python (contient un bug) -│ └── server.log <- Fichier de logs Azure (ne pas modifier) -└── node-client/ - ├── app.js <- Client Node.js (contient des bugs) - └── package.json <- Dépendances Node (contient un bug) -``` - ---- - -## Prérequis - -Avant de commencer, vérifiez que vous avez installé : - -```bash -python --version # Python 3.8 ou supérieur -node --version # Node.js 18 ou supérieur -npm --version # npm 9 ou supérieur -git --version # Git 2.x -``` - ---- - -## Étapes du TP - -### Étape 1 — Setup Git (15 min) - -**1.1 — Cloner le dépôt** - -```bash -git clone -cd TP-Git-Collaboratif -``` - -**1.2 — Créer votre branche de travail** - -Respectez impérativement cette convention de nommage : - -```bash -git checkout -b fix/prenom-debug-python-node -# Exemple : git checkout -b fix/alderic-debug-python-node -``` - -**1.3 — Vérifier que votre branche est active** - -```bash -git branch -# La branche active est précédée d'une étoile (*) -``` - ---- - -### Étape 2 — Débogage Python (60 min) - -> Consigne : lancez les commandes, lisez attentivement les messages d'erreur, -> corrigez UN bug à la fois, relancez après chaque correction. - -**2.1 — Installer les dépendances Python** - -Placez-vous dans le dossier `python-api/` : - -```bash -cd python-api -pip install -r requirements.txt -``` - -Si une erreur apparaît ici, lisez le message. Ouvrez `requirements.txt` et cherchez ce qui est incorrect. - -**2.2 — Lancer l'API Flask** - -```bash -python app.py -``` - -Chaque message d'erreur vous indique : -- Le **type d'erreur** (SyntaxError, NameError, FileNotFoundError…) -- Le **fichier** concerné -- Le **numéro de ligne** précis - -Notez chaque erreur dans le template email au fur et à mesure. - -**2.3 — Vérifier que l'API répond correctement** - -Une fois tous les bugs Python corrigés, vous devriez voir : - -``` - * Running on http://127.0.0.1:5000 - * Debug mode: on -``` - -Testez l'API avec curl (dans un autre terminal) : - -```bash -curl http://localhost:5000/api/logs -``` - -Résultat attendu — un JSON de cette forme : - -```json -{ - "error_count": 5, - "errors": ["..."], - "info_count": 10, - "warning_count": 4, - "warnings": ["..."] -} -``` - ---- - -### Étape 3 — Débogage Node.js (50 min) - -> Laissez l'API Python en cours d'exécution dans votre premier terminal. -> Ouvrez un second terminal pour cette étape. - -**3.1 — Installer les dépendances Node** - -```bash -cd node-client -npm install -``` - -Si npm affiche une erreur ou un avertissement "404 Not Found", examinez `package.json`. -Le nom du paquet dans `dependencies` est-il correct ? - -**3.2 — Lancer le client Node** - -```bash -node app.js -``` - -Analysez chaque message d'erreur. Node.js vous indique le type d'erreur et la ligne. -Corrigez un bug à la fois, relancez après chaque correction. - -**3.3 — Résultat attendu quand tout fonctionne** - -``` -======================================== - RAPPORT D'ANALYSE DES LOGS AZURE -======================================== - Erreurs detectees : 5 - Avertissements : 4 - Messages info : 10 - ---- Detail des erreurs --- - > 2024-01-15 08:02:45 ERROR Failed to connect to Azure Storage: connection timeout - > 2024-01-15 08:05:33 ERROR Authentication failed for service account: deploy_svc - > 2024-01-15 08:07:42 ERROR Database query timeout after 30s on table: audit_logs - > 2024-01-15 08:09:00 ERROR Max retries exceeded - Azure Storage service unavailable - > 2024-01-15 08:12:45 ERROR Backup failed: insufficient permissions on /var/backup/azure -======================================== -``` - ---- - -### Étape 4 — Test de communication complète (20 min) - -Vérifiez que les deux projets fonctionnent ensemble : - -1. Terminal 1 : `cd python-api && python app.py` -2. Terminal 2 : `cd node-client && node app.js` -3. Le rapport complet doit s'afficher dans le terminal 2 - -Si Node affiche `Erreur de connexion à l'API Python`, vérifiez que : -- L'API Python est bien démarrée (terminal 1) -- Les ports correspondent entre `app.py` et `app.js` - ---- - -### Étape 5 — Rédiger l'email de rapport (20 min) - -Ouvrez le fichier `email-template.md` et complétez chaque section : -- Contexte de l'incident -- Tableau des bugs identifiés (fichier, ligne, type, description) -- Corrections apportées avec justification -- Tests de validation réalisés -- Lien vers votre Pull Request -- Recommandations pour éviter ces bugs à l'avenir - ---- - -### Étape 6 — Pull Request (15 min) - -**6.1 — Commiter vos corrections** - -```bash -cd .. # Revenir à la racine du projet -git add . -git status # Vérifiez les fichiers modifiés -git commit -m "fix: correction des 8 bugs Python et Node - analyse logs Azure" -``` - -**6.2 — Pousser votre branche** - -```bash -git push origin fix/prenom-debug-python-node -``` - -**6.3 — Ouvrir une Pull Request sur GitHub** - -1. Rendez-vous sur le dépôt GitHub -2. Cliquez sur "Compare & pull request" -3. Titre de la PR : `fix: débogage API Python + client Node - ` -4. Description : collez un résumé des bugs corrigés -5. Soumettez la PR - -**6.4 — Réviser la PR d'un·e camarade** - -Trouvez la PR d'un·e autre apprenant·e et ajoutez au minimum un commentaire constructif. - ---- - -### Étape 7 — Gestion des conflits Git (45 min) - -> Cette étape se fait **en groupe**, une fois que tout le monde a ouvert sa PR. -> Le formateur merge la PR d'une première personne sur `main`, puis chacun doit -> mettre sa branche à jour et résoudre le conflit qui en résulte. - -Le guide complet pas à pas est dans le fichier **[CONFLITS.md](./CONFLITS.md)**. - -En résumé, vous allez : -1. Ajouter votre prénom dans le tableau `"apprenants"` de `config.json` -2. Commiter et pousser cette modification -3. Attendre que le formateur merge une première PR sur `main` -4. Récupérer les changements et rebaser votre branche : `git rebase origin/main` -5. Résoudre le conflit dans `config.json` (garder tous les prénoms + le bon port) -6. Continuer le rebase et forcer le push : `git push --force-with-lease` - - ---- - -## Pour aller plus loin — Profils avancés - -> Cette section est destinée aux apprenants qui ont terminé les 6 étapes principales avant la fin du TP. -> Les trois défis suivants sont indépendants — vous pouvez les traiter dans l'ordre de votre choix. -> Ils correspondent directement aux modules Azure qui arrivent dans la suite de la formation. - ---- - -### Défi 1 — Conteneuriser les deux services avec Docker - -**Objectif :** packager l'API Python et le client Node dans des conteneurs Docker, puis les faire communiquer via Docker Compose. C'est la base du déploiement sur Azure Container Apps. - -**Branche à créer :** `feat/prenom-docker` - -**1.1 — Créer le Dockerfile pour l'API Python** - -Créez le fichier `python-api/Dockerfile` : - -```dockerfile -FROM python:3.11-slim - -WORKDIR /app - -COPY requirements.txt . -RUN pip install --no-cache-dir -r requirements.txt - -COPY . . - -EXPOSE 5000 - -CMD ["python", "app.py"] -``` - -**1.2 — Créer le Dockerfile pour le client Node** - -Créez le fichier `node-client/Dockerfile` : - -```dockerfile -FROM node:18-alpine - -WORKDIR /app - -COPY package.json . -RUN npm install - -COPY . . - -CMD ["node", "app.js"] -``` - -**1.3 — Créer le fichier docker-compose.yml** - -Créez `docker-compose.yml` à la racine du projet : - -```yaml -version: '3.8' - -services: - python-api: - build: ./python-api - ports: - - "5000:5000" - container_name: log-analyser-api - - node-client: - build: ./node-client - depends_on: - - python-api - container_name: log-analyser-client -``` - -> Attention : quand Node tourne dans un conteneur, `localhost` ne désigne plus votre machine mais le conteneur lui-même. Il faudra adapter l'URL dans `app.js` pour utiliser le nom du service Docker (`python-api`) à la place de `localhost`. C'est le principe de la communication inter-conteneurs. - -**1.4 — Lancer les deux services** - -```bash -docker-compose up --build -``` - -**Résultat attendu :** le rapport des logs s'affiche dans les logs du conteneur `node-client`. - ---- - -### Défi 2 — Ajouter un pipeline CI avec GitHub Actions - -**Objectif :** créer un workflow GitHub Actions qui vérifie automatiquement que le code s'installe et démarre sans erreur à chaque push ou Pull Request. - -> **CI ou CD ?** Ce pipeline couvre uniquement la partie **CI (Intégration Continue)** : il vérifie que le code est valide. Il tourne entièrement sur les serveurs de GitHub — aucun abonnement Azure n'est nécessaire. La partie **CD (Déploiement Continu)** vers Azure App Service ou Azure Container Apps sera ajoutée dans le module Azure DevOps de la formation, quand vous aurez accès à un abonnement Azure. -> -> Retenez la distinction : **CI = vérifier le code** (GitHub suffit) — **CD = déployer le code** (Azure intervient ici). - -**Branche à créer :** `feat/prenom-github-actions` - -**2.1 — Créer la structure du workflow** - -```bash -mkdir -p .github/workflows -``` - -Créez le fichier `.github/workflows/ci.yml` : - -```yaml -name: CI — Vérification API Python - -on: - push: - branches: [ main, 'fix/**', 'feat/**' ] - pull_request: - branches: [ main ] - -jobs: - test-python-api: - runs-on: ubuntu-latest - - steps: - - name: Récupérer le code - uses: actions/checkout@v4 - - - name: Installer Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: '3.11' - - - name: Installer les dépendances - run: | - cd python-api - pip install -r requirements.txt - - - name: Vérifier que Flask démarre sans erreur - run: | - cd python-api - timeout 5 python app.py || true - echo "Vérification terminée" - - test-node-client: - runs-on: ubuntu-latest - - steps: - - name: Récupérer le code - uses: actions/checkout@v4 - - - name: Installer Node.js 18 - uses: actions/setup-node@v4 - with: - node-version: '18' - - - name: Installer les dépendances npm - run: | - cd node-client - npm install - - - name: Vérifier la syntaxe JavaScript - run: | - cd node-client - node --check app.js - echo "Syntaxe JavaScript valide" -``` - -**2.2 — Pousser et observer** - -```bash -git add .github/ -git commit -m "ci: ajout pipeline GitHub Actions vérification Python et Node" -git push origin feat/prenom-github-actions -``` - -Rendez-vous dans l'onglet **Actions** de votre dépôt GitHub pour observer le pipeline s'exécuter en temps réel. - -**Questions de réflexion :** - -1. Que se passe-t-il si vous pushez volontairement un bug dans `app.py` (par exemple, remettez `flaskk` dans `requirements.txt`) ? Le pipeline détecte-t-il l'erreur ? -2. Comment configurer GitHub pour qu'une PR ne puisse pas être fusionnée si le pipeline échoue ? (Indice : Settings → Branches → Branch protection rules) -3. Quelle étape faudrait-il ajouter à ce pipeline pour déployer automatiquement l'API sur Azure App Service ? Que vous manque-t-il actuellement pour pouvoir le faire ? (Cette question sera répondue dans le module Azure DevOps de la formation.) - ---- - -### Défi 3 — Sécuriser la communication entre les deux services - -**Objectif :** ajouter une couche de sécurité entre Node et Flask — une clé API simple en header HTTP. Sans cette clé, l'API refuse de répondre. C'est l'introduction au principe d'authentification entre microservices, fondamental en DevSecOps. - -**Branche à créer :** `feat/prenom-securite-api` - -**3.1 — Côté Python : vérifier la clé API dans chaque requête** - -Modifiez `python-api/app.py` pour ajouter la vérification : - -```python -from flask import Flask, jsonify, request - -app = Flask(__name__) - -# Clé API attendue — en production, cette valeur viendrait -# d'une variable d'environnement Azure Key Vault, jamais en dur dans le code -API_KEY = "devsecops-simplon-2024" - -def verifier_cle_api(): - cle = request.headers.get("X-API-Key") - if cle != API_KEY: - return jsonify({"erreur": "Clé API invalide ou manquante"}), 401 - return None - -@app.route("/api/logs", methods=["GET"]) -def get_logs(): - erreur = verifier_cle_api() - if erreur: - return erreur - result = parse_logs("server.log") - return jsonify(result), 200 -``` - -**3.2 — Côté Node : envoyer la clé dans les headers** - -Modifiez `node-client/app.js` pour inclure la clé dans chaque requête : - -```javascript -const response = await axios.get(API_URL, { - headers: { - 'X-API-Key': 'devsecops-simplon-2024' - } -}); -``` - -**3.3 — Tester que la sécurité fonctionne** - -Testez d'abord sans la clé : - -```bash -curl http://localhost:5000/api/logs -# Attendu → 401 Unauthorized -``` - -Puis avec la bonne clé : - -```bash -curl http://localhost:5000/api/logs -H "X-API-Key: devsecops-simplon-2024" -# Attendu → 200 OK avec les données JSON -``` - -**3.4 — Réflexion sécurité (à noter dans votre commit)** - -Ajoutez un commentaire dans votre code qui répond à cette question : pourquoi ne faut-il **jamais** stocker une clé API directement dans le code source ? Quelle alternative Azure propose-t-elle pour stocker ces secrets de façon sécurisée ? - -> Indice : cherchez "Azure Key Vault" dans la documentation Azure. - - ---- - -## Ressources utiles - -Toutes les ressources (documentation, vidéos, guides) sont regroupées dans le fichier **[RESSOURCES.md](./RESSOURCES.md)**. +# TP Git Collaboratif — Débogage Full-Stack Python + Node.js + +**Durée estimée :** 3h30 +**Niveau :** Intermédiaire +**Effectif :** 12 apprenants + +> Les ressources et explications complémentaires sont disponibles dans le fichier **[RESSOURCES.md](./RESSOURCES.md)**. + +--- + +## Avant de commencer — Comprendre les outils utilisés + +Si vous êtes en reconversion et que certains termes vous sont nouveaux, pas de panique. Voici l'essentiel à savoir avant de démarrer. + +**Python** est un langage de programmation très utilisé en DevOps pour automatiser des tâches : analyser des fichiers, interagir avec des APIs, lancer des scripts de déploiement. Dans ce TP, un script Python est chargé de lire un fichier de logs et d'en extraire les informations importantes. + +**Flask** est un framework Python qui permet de créer très simplement une API web. Concrètement, Flask transforme votre script Python en un petit serveur web que l'on peut interroger via une URL, comme on interrogerait un site. Ici, Flask va exposer les résultats de l'analyse des logs à l'adresse `http://localhost:5000/api/logs`. + +**Node.js** est un environnement qui permet d'exécuter du JavaScript en dehors d'un navigateur, directement dans le terminal. Il est très utilisé pour créer des outils en ligne de commande, des serveurs web ou, comme dans ce TP, des scripts qui consomment des APIs. + +**npm** (Node Package Manager) est le gestionnaire de paquets de Node.js. Il permet d'installer des bibliothèques tierces listées dans un fichier `package.json`, exactement comme `pip` installe les dépendances Python listées dans `requirements.txt`. + +**Une API REST** est un service web qui reçoit des requêtes HTTP et retourne des données, généralement au format JSON. Dans ce TP, Python joue le rôle du serveur (il fournit les données) et Node.js joue le rôle du client (il demande les données et les affiche). + +**Les logs serveur** sont des fichiers texte que les applications génèrent automatiquement pour enregistrer ce qui se passe : démarrages, erreurs, avertissements, requêtes reçues… En DevOps, analyser ces logs est une tâche quotidienne pour surveiller l'état d'une infrastructure. + +--- + +## Contexte du TP + +Vous êtes développeur·se DevOps chez **NexaCloud**. + +Suite à une mise à jour en production, deux scripts critiques tombent en erreur : + +- `python-api/app.py` — une API Flask qui lit les logs du serveur Azure et retourne une analyse JSON +- `node-client/app.js` — un client Node.js qui interroge cette API et affiche un rapport dans le terminal + +Ces deux scripts **communiquent ensemble** : Node appelle Python via HTTP. +Aucun des deux ne fonctionnera correctement tant que tous les bugs ne sont pas corrigés. + +Votre responsable technique vous demande de : +1. Cloner le dépôt sur votre machine +2. Reproduire les erreurs en exécutant les scripts +3. Identifier et corriger les bugs (8 au total) +4. Pousser vos corrections via une Pull Request +5. Envoyer un email de rapport à votre responsable + +--- + +## Architecture du projet + +``` +[Fichier de logs Azure] + server.log + | + v + python-api/app.py <- API Flask (port 5000) + (lit les logs, compte <- Lance avec : python app.py + les ERROR / WARNING / INFO) + | + | HTTP GET /api/logs + v + node-client/app.js <- Client Node.js + (affiche le rapport <- Lance avec : node app.js + dans le terminal) +``` + +--- + +## Structure du dépôt + +``` +TP-Git-Collaboratif/ +├── README.md <- Ce fichier +├── config.json <- Configuration partagée Python + Node (contient un bug) +├── email-template.md <- Template pour votre email de rapport +├── CONFLITS.md <- Guide de résolution des conflits Git (étape 7) +├── CORRECTION_FORMATEUR.md <- Réservé au formateur +├── python-api/ +│ ├── app.py <- API Flask (contient des bugs) +│ ├── requirements.txt <- Dépendances Python (contient un bug) +│ └── server.log <- Fichier de logs Azure (ne pas modifier) +└── node-client/ + ├── app.js <- Client Node.js (contient des bugs) + └── package.json <- Dépendances Node (contient un bug) +``` + +--- + +## Prérequis + +Avant de commencer, vérifiez que vous avez installé : + +```bash +python --version # Python 3.8 ou supérieur +node --version # Node.js 18 ou supérieur +npm --version # npm 9 ou supérieur +git --version # Git 2.x +``` + +--- + +## Étapes du TP + +### Étape 1 — Setup Git (15 min) + +**1.1 — Cloner le dépôt** + +```bash +git clone +cd TP-Git-Collaboratif +``` + +**1.2 — Créer votre branche de travail** + +Respectez impérativement cette convention de nommage : + +```bash +git checkout -b fix/prenom-debug-python-node +# Exemple : git checkout -b fix/alderic-debug-python-node +``` + +**1.3 — Vérifier que votre branche est active** + +```bash +git branch +# La branche active est précédée d'une étoile (*) +``` + +--- + +### Étape 2 — Débogage Python (60 min) + +> Consigne : lancez les commandes, lisez attentivement les messages d'erreur, +> corrigez UN bug à la fois, relancez après chaque correction. + +**2.1 — Installer les dépendances Python** + +Placez-vous dans le dossier `python-api/` : + +```bash +cd python-api +pip install -r requirements.txt +``` + +Si une erreur apparaît ici, lisez le message. Ouvrez `requirements.txt` et cherchez ce qui est incorrect. + +**2.2 — Lancer l'API Flask** + +```bash +python app.py +``` + +Chaque message d'erreur vous indique : +- Le **type d'erreur** (SyntaxError, NameError, FileNotFoundError…) +- Le **fichier** concerné +- Le **numéro de ligne** précis + +Notez chaque erreur dans le template email au fur et à mesure. + +**2.3 — Vérifier que l'API répond correctement** + +Une fois tous les bugs Python corrigés, vous devriez voir : + +``` + * Running on http://127.0.0.1:5000 + * Debug mode: on +``` + +Testez l'API avec curl (dans un autre terminal) : + +```bash +curl http://localhost:5000/api/logs +``` + +Résultat attendu — un JSON de cette forme : + +```json +{ + "error_count": 5, + "errors": ["..."], + "info_count": 10, + "warning_count": 4, + "warnings": ["..."] +} +``` + +--- + +### Étape 3 — Débogage Node.js (50 min) + +> Laissez l'API Python en cours d'exécution dans votre premier terminal. +> Ouvrez un second terminal pour cette étape. + +**3.1 — Installer les dépendances Node** + +```bash +cd node-client +npm install +``` + +Si npm affiche une erreur ou un avertissement "404 Not Found", examinez `package.json`. +Le nom du paquet dans `dependencies` est-il correct ? + +**3.2 — Lancer le client Node** + +```bash +node app.js +``` + +Analysez chaque message d'erreur. Node.js vous indique le type d'erreur et la ligne. +Corrigez un bug à la fois, relancez après chaque correction. + +**3.3 — Résultat attendu quand tout fonctionne** + +``` +======================================== + RAPPORT D'ANALYSE DES LOGS AZURE +======================================== + Erreurs detectees : 5 + Avertissements : 4 + Messages info : 10 + +--- Detail des erreurs --- + > 2024-01-15 08:02:45 ERROR Failed to connect to Azure Storage: connection timeout + > 2024-01-15 08:05:33 ERROR Authentication failed for service account: deploy_svc + > 2024-01-15 08:07:42 ERROR Database query timeout after 30s on table: audit_logs + > 2024-01-15 08:09:00 ERROR Max retries exceeded - Azure Storage service unavailable + > 2024-01-15 08:12:45 ERROR Backup failed: insufficient permissions on /var/backup/azure +======================================== +``` + +--- + +### Étape 4 — Test de communication complète (20 min) + +Vérifiez que les deux projets fonctionnent ensemble : + +1. Terminal 1 : `cd python-api && python app.py` +2. Terminal 2 : `cd node-client && node app.js` +3. Le rapport complet doit s'afficher dans le terminal 2 + +Si Node affiche `Erreur de connexion à l'API Python`, vérifiez que : +- L'API Python est bien démarrée (terminal 1) +- Les ports correspondent entre `app.py` et `app.js` + +--- + +### Étape 5 — Rédiger l'email de rapport (20 min) + +Ouvrez le fichier `email-template.md` et complétez chaque section : +- Contexte de l'incident +- Tableau des bugs identifiés (fichier, ligne, type, description) +- Corrections apportées avec justification +- Tests de validation réalisés +- Lien vers votre Pull Request +- Recommandations pour éviter ces bugs à l'avenir + +--- + +### Étape 6 — Pull Request (15 min) + +**6.1 — Commiter vos corrections** + +```bash +cd .. # Revenir à la racine du projet +git add . +git status # Vérifiez les fichiers modifiés +git commit -m "fix: correction des 8 bugs Python et Node - analyse logs Azure" +``` + +**6.2 — Pousser votre branche** + +```bash +git push origin fix/prenom-debug-python-node +``` + +**6.3 — Ouvrir une Pull Request sur GitHub** + +1. Rendez-vous sur le dépôt GitHub +2. Cliquez sur "Compare & pull request" +3. Titre de la PR : `fix: débogage API Python + client Node - ` +4. Description : collez un résumé des bugs corrigés +5. Soumettez la PR + +**6.4 — Réviser la PR d'un·e camarade** + +Trouvez la PR d'un·e autre apprenant·e et ajoutez au minimum un commentaire constructif. + +--- + +### Étape 7 — Gestion des conflits Git (45 min) + +> Cette étape se fait **en groupe**, une fois que tout le monde a ouvert sa PR. +> Le formateur merge la PR d'une première personne sur `main`, puis chacun doit +> mettre sa branche à jour et résoudre le conflit qui en résulte. + +Le guide complet pas à pas est dans le fichier **[CONFLITS.md](./CONFLITS.md)**. + +En résumé, vous allez : +1. Ajouter votre prénom dans le tableau `"apprenants"` de `config.json` +2. Commiter et pousser cette modification +3. Attendre que le formateur merge une première PR sur `main` +4. Récupérer les changements et rebaser votre branche : `git rebase origin/main` +5. Résoudre le conflit dans `config.json` (garder tous les prénoms + le bon port) +6. Continuer le rebase et forcer le push : `git push --force-with-lease` + + +--- + +## Pour aller plus loin — Profils avancés + +> Cette section est destinée aux apprenants qui ont terminé les 6 étapes principales avant la fin du TP. +> Les trois défis suivants sont indépendants — vous pouvez les traiter dans l'ordre de votre choix. +> Ils correspondent directement aux modules Azure qui arrivent dans la suite de la formation. + +--- + +### Défi 1 — Conteneuriser les deux services avec Docker + +**Objectif :** packager l'API Python et le client Node dans des conteneurs Docker, puis les faire communiquer via Docker Compose. C'est la base du déploiement sur Azure Container Apps. + +**Branche à créer :** `feat/prenom-docker` + +**1.1 — Créer le Dockerfile pour l'API Python** + +Créez le fichier `python-api/Dockerfile` : + +```dockerfile +FROM python:3.11-slim + +WORKDIR /app + +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +COPY . . + +EXPOSE 5000 + +CMD ["python", "app.py"] +``` + +**1.2 — Créer le Dockerfile pour le client Node** + +Créez le fichier `node-client/Dockerfile` : + +```dockerfile +FROM node:18-alpine + +WORKDIR /app + +COPY package.json . +RUN npm install + +COPY . . + +CMD ["node", "app.js"] +``` + +**1.3 — Créer le fichier docker-compose.yml** + +Créez `docker-compose.yml` à la racine du projet : + +```yaml +version: '3.8' + +services: + python-api: + build: ./python-api + ports: + - "5000:5000" + container_name: log-analyser-api + + node-client: + build: ./node-client + depends_on: + - python-api + container_name: log-analyser-client +``` + +> Attention : quand Node tourne dans un conteneur, `localhost` ne désigne plus votre machine mais le conteneur lui-même. Il faudra adapter l'URL dans `app.js` pour utiliser le nom du service Docker (`python-api`) à la place de `localhost`. C'est le principe de la communication inter-conteneurs. + +**1.4 — Lancer les deux services** + +```bash +docker-compose up --build +``` + +**Résultat attendu :** le rapport des logs s'affiche dans les logs du conteneur `node-client`. + +--- + +### Défi 2 — Ajouter un pipeline CI avec GitHub Actions + +**Objectif :** créer un workflow GitHub Actions qui vérifie automatiquement que le code s'installe et démarre sans erreur à chaque push ou Pull Request. + +> **CI ou CD ?** Ce pipeline couvre uniquement la partie **CI (Intégration Continue)** : il vérifie que le code est valide. Il tourne entièrement sur les serveurs de GitHub — aucun abonnement Azure n'est nécessaire. La partie **CD (Déploiement Continu)** vers Azure App Service ou Azure Container Apps sera ajoutée dans le module Azure DevOps de la formation, quand vous aurez accès à un abonnement Azure. +> +> Retenez la distinction : **CI = vérifier le code** (GitHub suffit) — **CD = déployer le code** (Azure intervient ici). + +**Branche à créer :** `feat/prenom-github-actions` + +**2.1 — Créer la structure du workflow** + +```bash +mkdir -p .github/workflows +``` + +Créez le fichier `.github/workflows/ci.yml` : + +```yaml +name: CI — Vérification API Python + +on: + push: + branches: [ main, 'fix/**', 'feat/**' ] + pull_request: + branches: [ main ] + +jobs: + test-python-api: + runs-on: ubuntu-latest + + steps: + - name: Récupérer le code + uses: actions/checkout@v4 + + - name: Installer Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: '3.11' + + - name: Installer les dépendances + run: | + cd python-api + pip install -r requirements.txt + + - name: Vérifier que Flask démarre sans erreur + run: | + cd python-api + timeout 5 python app.py || true + echo "Vérification terminée" + + test-node-client: + runs-on: ubuntu-latest + + steps: + - name: Récupérer le code + uses: actions/checkout@v4 + + - name: Installer Node.js 18 + uses: actions/setup-node@v4 + with: + node-version: '18' + + - name: Installer les dépendances npm + run: | + cd node-client + npm install + + - name: Vérifier la syntaxe JavaScript + run: | + cd node-client + node --check app.js + echo "Syntaxe JavaScript valide" +``` + +**2.2 — Pousser et observer** + +```bash +git add .github/ +git commit -m "ci: ajout pipeline GitHub Actions vérification Python et Node" +git push origin feat/prenom-github-actions +``` + +Rendez-vous dans l'onglet **Actions** de votre dépôt GitHub pour observer le pipeline s'exécuter en temps réel. + +**Questions de réflexion :** + +1. Que se passe-t-il si vous pushez volontairement un bug dans `app.py` (par exemple, remettez `flaskk` dans `requirements.txt`) ? Le pipeline détecte-t-il l'erreur ? +2. Comment configurer GitHub pour qu'une PR ne puisse pas être fusionnée si le pipeline échoue ? (Indice : Settings → Branches → Branch protection rules) +3. Quelle étape faudrait-il ajouter à ce pipeline pour déployer automatiquement l'API sur Azure App Service ? Que vous manque-t-il actuellement pour pouvoir le faire ? (Cette question sera répondue dans le module Azure DevOps de la formation.) + +--- + +### Défi 3 — Sécuriser la communication entre les deux services + +**Objectif :** ajouter une couche de sécurité entre Node et Flask — une clé API simple en header HTTP. Sans cette clé, l'API refuse de répondre. C'est l'introduction au principe d'authentification entre microservices, fondamental en DevSecOps. + +**Branche à créer :** `feat/prenom-securite-api` + +**3.1 — Côté Python : vérifier la clé API dans chaque requête** + +Modifiez `python-api/app.py` pour ajouter la vérification : + +```python +from flask import Flask, jsonify, request + +app = Flask(__name__) + +# Clé API attendue — en production, cette valeur viendrait +# d'une variable d'environnement Azure Key Vault, jamais en dur dans le code +API_KEY = "devsecops-simplon-2024" + +def verifier_cle_api(): + cle = request.headers.get("X-API-Key") + if cle != API_KEY: + return jsonify({"erreur": "Clé API invalide ou manquante"}), 401 + return None + +@app.route("/api/logs", methods=["GET"]) +def get_logs(): + erreur = verifier_cle_api() + if erreur: + return erreur + result = parse_logs("server.log") + return jsonify(result), 200 +``` + +**3.2 — Côté Node : envoyer la clé dans les headers** + +Modifiez `node-client/app.js` pour inclure la clé dans chaque requête : + +```javascript +const response = await axios.get(API_URL, { + headers: { + 'X-API-Key': 'devsecops-simplon-2024' + } +}); +``` + +**3.3 — Tester que la sécurité fonctionne** + +Testez d'abord sans la clé : + +```bash +curl http://localhost:5000/api/logs +# Attendu → 401 Unauthorized +``` + +Puis avec la bonne clé : + +```bash +curl http://localhost:5000/api/logs -H "X-API-Key: devsecops-simplon-2024" +# Attendu → 200 OK avec les données JSON +``` + +**3.4 — Réflexion sécurité (à noter dans votre commit)** + +Ajoutez un commentaire dans votre code qui répond à cette question : pourquoi ne faut-il **jamais** stocker une clé API directement dans le code source ? Quelle alternative Azure propose-t-elle pour stocker ces secrets de façon sécurisée ? + +> Indice : cherchez "Azure Key Vault" dans la documentation Azure. + + +--- + +## Ressources utiles + +Toutes les ressources (documentation, vidéos, guides) sont regroupées dans le fichier **[RESSOURCES.md](./RESSOURCES.md)**. diff --git a/RESSOURCES.md b/RESSOURCES.md index f820eda..da55c69 100644 --- a/RESSOURCES.md +++ b/RESSOURCES.md @@ -1,156 +1,156 @@ -# Ressources utiles — TP Git Collaboratif - -Ce fichier regroupe toutes les ressources pour vous aider pendant et après le TP. -N'hésitez pas à y revenir à tout moment. - ---- - -## Python - -**Comprendre les messages d'erreur Python** -Quand Python plante, il affiche un "traceback" : lisez-le de bas en haut. La dernière ligne indique le type d'erreur et le message, les lignes au-dessus montrent le chemin qui y a mené. - -- [Types d'erreurs Python — documentation officielle (français)](https://docs.python.org/fr/3/tutorial/errors.html) -- [Comprendre un traceback Python — RealPython (anglais)](https://realpython.com/python-traceback/) - -Les erreurs les plus fréquentes que vous rencontrerez : - -- `SyntaxError` — le code n'est pas valide syntaxiquement (parenthèse manquante, deux-points oublié…) -- `NameError` — vous utilisez une variable ou une fonction qui n'a pas été définie -- `FileNotFoundError` — le fichier demandé n'existe pas à l'emplacement indiqué -- `ModuleNotFoundError` — un paquet importé n'est pas installé - ---- - -## Flask - -Flask est le framework Python utilisé pour créer l'API de ce TP. - -- [Documentation officielle Flask (anglais)](https://flask.palletsprojects.com/en/3.0.x/) -- [Tutoriel Flask pour débutants — OpenClassrooms (français)](https://openclassrooms.com/fr/courses/4425066-concevez-un-site-avec-flask) - -Commandes utiles : - -```bash -# Installer Flask -pip install flask - -# Lancer une application Flask -python app.py - -# Tester une route depuis le terminal -curl http://localhost:5000/api/logs -``` - ---- - -## Node.js et npm - -Node.js permet d'exécuter du JavaScript dans le terminal. npm est son gestionnaire de paquets. - -- [Documentation officielle Node.js (français)](https://nodejs.org/fr/docs) -- [Guide npm pour débutants (français)](https://docs.npmjs.com/about-npm) - -Commandes utiles : - -```bash -# Vérifier la version installée -node --version -npm --version - -# Installer les dépendances d'un projet (lit package.json) -npm install - -# Lancer un script Node -node app.js - -# Lancer via le script "start" défini dans package.json -npm start -``` - -Erreurs fréquentes sous Node.js : - -- `Cannot find module 'xxx'` — le paquet n'est pas installé ou son nom est incorrect dans `require()` -- `TypeError: Cannot read properties of undefined` — vous essayez d'accéder à une propriété d'une variable qui vaut `undefined` -- `ECONNREFUSED` — le client ne peut pas se connecter au serveur (serveur non démarré ou mauvais port) - ---- - -## Codes HTTP - -Quand Node.js appelle l'API Python, la réponse contient toujours un code HTTP (200, 404, 500…). -Le guide complet avec les explications et les cas concrets de ce TP est dans **[CODES-HTTP.md](./CODES-HTTP.md)**. - ---- - -## Axios - -Axios est la bibliothèque Node.js utilisée dans ce TP pour faire des requêtes HTTP vers l'API Python. - -- [Documentation Axios — réponse d'une requête](https://axios-http.com/docs/res_schema) - -Structure d'une réponse Axios : - -```javascript -const response = await axios.get('http://localhost:5000/api/logs'); -// Les données retournées par le serveur sont dans : -response.data // <- c'est ici que se trouve le JSON -response.status // <- code HTTP (200, 404, 500...) -response.headers // <- en-têtes HTTP -``` - ---- - -## Git et GitHub - -**Conventions de nommage des branches** - -Dans ce TP, la convention est : `fix/prenom-description` -Exemples : `fix/marie-debug-python`, `fix/kevin-correction-node` - -**Messages de commit — Conventional Commits** -Un bon message de commit suit ce format : `type: description courte` - -- `fix:` — correction d'un bug -- `feat:` — ajout d'une nouvelle fonctionnalité -- `docs:` — modification de documentation -- `chore:` — tâche de maintenance (mise à jour de dépendances…) - -Exemples : `fix: correction du port Flask dans app.py` ou `fix: remplacement axioss par axios` - -Ressources Git : -- [Conventional Commits (français)](https://www.conventionalcommits.org/fr/v1.0.0/) -- [Créer une Pull Request sur GitHub (français)](https://docs.github.com/fr/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request) -- [Comprendre Git — série complète par Grafikart (vidéo français)](https://www.youtube.com/playlist?list=PLjwdMgw5TTLXuY5i7RW0QqGdW0NZntqiP) - ---- - -## Vidéos recommandées (français) - -Ces vidéos ont été sélectionnées par votre formateur pour accompagner la formation. - -**Git & GitHub — bases** -- [Apprendre GIT en 1 heure (2025)](https://www.youtube.com/watch?v=_WBBiGiCOEA) -- [Formation Git complète — Grafikart](https://www.youtube.com/playlist?list=PLjwdMgw5TTLXuY5i7RW0QqGdW0NZntqiP) -- [Débuter avec Git et GitHub en 30 min](https://www.youtube.com/watch?v=hPfgekYUKgk) - -**Gitflow et DevOps** -- [Devenir DevOps — Git et Gitflow — Xavki](https://www.youtube.com/watch?v=ro3ouEyzFzY) -- [Pipeline DevOps — Gitflow en pratique — Xavki](https://www.youtube.com/watch?v=pXWU0iNubk0) -- [Comprendre Git Flow — Grafikart](https://www.youtube.com/watch?v=ZQAQ4HcskAY) - ---- - -## Glossaire rapide - -| Terme | Définition simple | -|-------|-------------------| -| API | Service web qui reçoit des requêtes et retourne des données (souvent en JSON) | -| JSON | Format de données texte, lisible par les humains et les machines | -| Framework | Boîte à outils qui facilite le développement (Flask, Express…) | -| Dépendance | Bibliothèque externe dont votre projet a besoin pour fonctionner | -| Port | Numéro qui identifie un service sur une machine (ex: port 5000 pour Flask) | -| Localhost | Désigne votre propre machine (équivalent à 127.0.0.1) | -| Log | Fichier texte enregistrant les événements d'une application | -| Pull Request | Proposition de fusion d'une branche vers une autre, avec revue de code | -| Traceback | Message d'erreur Python qui montre la chaîne d'appels ayant mené à l'erreur | +# Ressources utiles — TP Git Collaboratif + +Ce fichier regroupe toutes les ressources pour vous aider pendant et après le TP. +N'hésitez pas à y revenir à tout moment. + +--- + +## Python + +**Comprendre les messages d'erreur Python** +Quand Python plante, il affiche un "traceback" : lisez-le de bas en haut. La dernière ligne indique le type d'erreur et le message, les lignes au-dessus montrent le chemin qui y a mené. + +- [Types d'erreurs Python — documentation officielle (français)](https://docs.python.org/fr/3/tutorial/errors.html) +- [Comprendre un traceback Python — RealPython (anglais)](https://realpython.com/python-traceback/) + +Les erreurs les plus fréquentes que vous rencontrerez : + +- `SyntaxError` — le code n'est pas valide syntaxiquement (parenthèse manquante, deux-points oublié…) +- `NameError` — vous utilisez une variable ou une fonction qui n'a pas été définie +- `FileNotFoundError` — le fichier demandé n'existe pas à l'emplacement indiqué +- `ModuleNotFoundError` — un paquet importé n'est pas installé + +--- + +## Flask + +Flask est le framework Python utilisé pour créer l'API de ce TP. + +- [Documentation officielle Flask (anglais)](https://flask.palletsprojects.com/en/3.0.x/) +- [Tutoriel Flask pour débutants — OpenClassrooms (français)](https://openclassrooms.com/fr/courses/4425066-concevez-un-site-avec-flask) + +Commandes utiles : + +```bash +# Installer Flask +pip install flask + +# Lancer une application Flask +python app.py + +# Tester une route depuis le terminal +curl http://localhost:5000/api/logs +``` + +--- + +## Node.js et npm + +Node.js permet d'exécuter du JavaScript dans le terminal. npm est son gestionnaire de paquets. + +- [Documentation officielle Node.js (français)](https://nodejs.org/fr/docs) +- [Guide npm pour débutants (français)](https://docs.npmjs.com/about-npm) + +Commandes utiles : + +```bash +# Vérifier la version installée +node --version +npm --version + +# Installer les dépendances d'un projet (lit package.json) +npm install + +# Lancer un script Node +node app.js + +# Lancer via le script "start" défini dans package.json +npm start +``` + +Erreurs fréquentes sous Node.js : + +- `Cannot find module 'xxx'` — le paquet n'est pas installé ou son nom est incorrect dans `require()` +- `TypeError: Cannot read properties of undefined` — vous essayez d'accéder à une propriété d'une variable qui vaut `undefined` +- `ECONNREFUSED` — le client ne peut pas se connecter au serveur (serveur non démarré ou mauvais port) + +--- + +## Codes HTTP + +Quand Node.js appelle l'API Python, la réponse contient toujours un code HTTP (200, 404, 500…). +Le guide complet avec les explications et les cas concrets de ce TP est dans **[CODES-HTTP.md](./CODES-HTTP.md)**. + +--- + +## Axios + +Axios est la bibliothèque Node.js utilisée dans ce TP pour faire des requêtes HTTP vers l'API Python. + +- [Documentation Axios — réponse d'une requête](https://axios-http.com/docs/res_schema) + +Structure d'une réponse Axios : + +```javascript +const response = await axios.get('http://localhost:5000/api/logs'); +// Les données retournées par le serveur sont dans : +response.data // <- c'est ici que se trouve le JSON +response.status // <- code HTTP (200, 404, 500...) +response.headers // <- en-têtes HTTP +``` + +--- + +## Git et GitHub + +**Conventions de nommage des branches** + +Dans ce TP, la convention est : `fix/prenom-description` +Exemples : `fix/marie-debug-python`, `fix/kevin-correction-node` + +**Messages de commit — Conventional Commits** +Un bon message de commit suit ce format : `type: description courte` + +- `fix:` — correction d'un bug +- `feat:` — ajout d'une nouvelle fonctionnalité +- `docs:` — modification de documentation +- `chore:` — tâche de maintenance (mise à jour de dépendances…) + +Exemples : `fix: correction du port Flask dans app.py` ou `fix: remplacement axioss par axios` + +Ressources Git : +- [Conventional Commits (français)](https://www.conventionalcommits.org/fr/v1.0.0/) +- [Créer une Pull Request sur GitHub (français)](https://docs.github.com/fr/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request) +- [Comprendre Git — série complète par Grafikart (vidéo français)](https://www.youtube.com/playlist?list=PLjwdMgw5TTLXuY5i7RW0QqGdW0NZntqiP) + +--- + +## Vidéos recommandées (français) + +Ces vidéos ont été sélectionnées par votre formateur pour accompagner la formation. + +**Git & GitHub — bases** +- [Apprendre GIT en 1 heure (2025)](https://www.youtube.com/watch?v=_WBBiGiCOEA) +- [Formation Git complète — Grafikart](https://www.youtube.com/playlist?list=PLjwdMgw5TTLXuY5i7RW0QqGdW0NZntqiP) +- [Débuter avec Git et GitHub en 30 min](https://www.youtube.com/watch?v=hPfgekYUKgk) + +**Gitflow et DevOps** +- [Devenir DevOps — Git et Gitflow — Xavki](https://www.youtube.com/watch?v=ro3ouEyzFzY) +- [Pipeline DevOps — Gitflow en pratique — Xavki](https://www.youtube.com/watch?v=pXWU0iNubk0) +- [Comprendre Git Flow — Grafikart](https://www.youtube.com/watch?v=ZQAQ4HcskAY) + +--- + +## Glossaire rapide + +| Terme | Définition simple | +|-------|-------------------| +| API | Service web qui reçoit des requêtes et retourne des données (souvent en JSON) | +| JSON | Format de données texte, lisible par les humains et les machines | +| Framework | Boîte à outils qui facilite le développement (Flask, Express…) | +| Dépendance | Bibliothèque externe dont votre projet a besoin pour fonctionner | +| Port | Numéro qui identifie un service sur une machine (ex: port 5000 pour Flask) | +| Localhost | Désigne votre propre machine (équivalent à 127.0.0.1) | +| Log | Fichier texte enregistrant les événements d'une application | +| Pull Request | Proposition de fusion d'une branche vers une autre, avec revue de code | +| Traceback | Message d'erreur Python qui montre la chaîne d'appels ayant mené à l'erreur | diff --git a/config.json b/config.json index c62f739..5343695 100644 --- a/config.json +++ b/config.json @@ -1,11 +1,11 @@ -{ - "projet": "TP-Git-Collaboratif", - "promotion": "DevSecOps Azure — Simplon", - "apprenants": [], - "api": { - "port": 5000, - "host": "localhost", - "route": "/api/logs", - "log_file": "server.log" - } -} +{ + "projet": "TP-Git-Collaboratif", + "promotion": "DevSecOps Azure — Simplon", + "apprenants": [], + "api": { + "port": 50001, + "host": "localhost", + "route": "/api/logs", + "log_file": "server.log" + } +} diff --git a/node-client/app.js b/node-client/app.js index 47cf0a2..8f8bcca 100644 --- a/node-client/app.js +++ b/node-client/app.js @@ -1,35 +1,40 @@ -// Client Node.js — Interroge l'API Python et affiche un rapport des logs Azure -// ----------------------------------------------------------------- - -const path = require('path'); - -// Configuration partagée chargée depuis config.json (à la racine du projet) -const config = require(path.join(__dirname, '..', 'config.json')); -const API_URL = `http://${config.api.host}:${config.api.port}${config.api.route}`; - -const axios = require('axios'); - -async function getLogs() { - try { - const response = await axios.get(API_URL); - - const data = response.data; - - console.log('\n========================================'); - console.log(' RAPPORT D\'ANALYSE DES LOGS AZURE '); - console.log('========================================'); - console.log(` Erreurs detectees : ${data.error_count}`); - console.log(` Avertissements : ${data.warning_count}`); - console.log(` Messages info : ${data.info_count}`); - console.log('\n--- Detail des erreurs ---'); - data.errors.forEach(err => console.log(` > ${err}`)); - console.log('\n--- Detail des avertissements ---'); - data.warnings.forEach(warn => console.log(` > ${warn}`)); - console.log('========================================\n'); - - } catch (error) { - console.error('Erreur de connexion a l\'API Python :', error.message); - } -} - -getLogs(); +// Client Node.js — Interroge l'API Python et affiche un rapport des logs Azure +// ----------------------------------------------------------------- + +const path = require('path'); + +// Configuration partagée chargée depuis config.json (à la racine du projet) +// BUG 5 (suite) — Si l'API Python ne répond pas, vérifiez le port dans config.json +const config = require(path.join(__dirname, '..', 'config.json')); +const API_URL = `http://${config.api.host}:${config.api.port}${config.api.route}`; + +// BUG 6 — Le nom du module importé ici est incorrect +const axioss = require('axioss'); + +async function getLogs() { + try { + const response = await axioss.get(API_URL); + + // BUG 7 — La propriété pour accéder au corps de la réponse avec axios + // ne s'appelle pas .body — cherchez dans la doc axios comment + // accéder aux données de la réponse + const data = response.body; + + console.log('\n========================================'); + console.log(' RAPPORT D\'ANALYSE DES LOGS AZURE '); + console.log('========================================'); + console.log(` Erreurs detectees : ${data.error_count}`); + console.log(` Avertissements : ${data.warning_count}`); + console.log(` Messages info : ${data.info_count}`); + console.log('\n--- Detail des erreurs ---'); + data.errors.forEach(err => console.log(` > ${err}`)); + console.log('\n--- Detail des avertissements ---'); + data.warnings.forEach(warn => console.log(` > ${warn}`)); + console.log('========================================\n'); + + } catch (error) { + console.error('Erreur de connexion a l\'API Python :', error.message); + } +} + +getLogs(); diff --git a/node-client/package.json b/node-client/package.json index 84280d9..10789eb 100644 --- a/node-client/package.json +++ b/node-client/package.json @@ -1,12 +1,12 @@ -{ - "name": "log-analyser-client", - "version": "1.0.0", - "description": "Client Node.js qui interroge l'API Python d'analyse de logs Azure", - "main": "app.js", - "scripts": { - "start": "node app.js" - }, - "dependencies": { - "axios": "^1.6.0" - } -} +{ + "name": "log-analyser-client", + "version": "1.0.0", + "description": "Client Node.js qui interroge l'API Python d'analyse de logs Azure", + "main": "app.js", + "scripts": { + "start": "node app.js" + }, + "dependencies": { + "axioss": "^1.6.0" + } +} diff --git a/python-api/app.py b/python-api/app.py index bc7c4b5..7afd548 100644 --- a/python-api/app.py +++ b/python-api/app.py @@ -1,50 +1,57 @@ -from flask import Flask, jsonify -import json -import os - -app = Flask(__name__) - -# Chargement de la configuration partagée (config.json à la racine du projet) -config_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'config.json') -with open(config_path, 'r') as f: - config = json.load(f) - -# ------------------------------------------------------- -# Analyse un fichier de logs serveur et retourne -# le nombre d'errors, warnings et infos détectés. -# ------------------------------------------------------- - -def parse_logs(filepath): - errors = [] - warnings = [] - infos = [] - - with open(filepath, "r") as f: - for line in f: - line = line.strip() - if not line: - continue - if "ERROR" in line: - errors.append(line) - elif "WARNING" in line: - warnings.append(line) - elif "INFO" in line: - infos.append(line) - - return { - "error_count": len(errors), - "warning_count": len(warnings), - "info_count": len(infos), - "errors": errors, - "warnings": warnings - } - - -@app.route("/api/logs", methods=["GET"]) -def get_logs(): - result = parse_logs(config["api"]["log_file"]) - return jsonify(result), 200 - - -if __name__ == "__main__": - app.run(debug=True, port=config["api"]["port"]) +from flask import Flask, jsonify +import json +import os + +app = Flask(__name__) + +# Chargement de la configuration partagée (config.json à la racine du projet) +config_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'config.json') +with open(config_path, 'r') as f: + config = json.load(f) + +# ------------------------------------------------------- +# Analyse un fichier de logs serveur et retourne +# le nombre d'erreurs, warnings et infos détectés. +# ------------------------------------------------------- + +# BUG 2 — Il manque un caractère essentiel à la fin de cette ligne +def parse_logs(filepath) + erreurs = [] + warnings = [] + infos = [] + + with open(filepath, "r") as f: + for line in f: + line = line.strip() + if not line: + continue + # BUG 3 — Le nom de la variable utilisée ici ne correspond pas + # à celle déclarée plus haut dans cette fonction + if "ERROR" in line: + errors.append(line) + elif "WARNING" in line: + warnings.append(line) + elif "INFO" in line: + infos.append(line) + + return { + "error_count": len(erreurs), + "warning_count": len(warnings), + "info_count": len(infos), + "errors": erreurs, + "warnings": warnings + } + + +@app.route("/api/logs", methods=["GET"]) +def get_logs(): + # BUG 4 — La variable passée en argument n'est définie nulle part + # Quel fichier de logs doit-on analyser ? + result = parse_logs(log_file) + return jsonify(result), 200 + + +if __name__ == "__main__": + # Le port est chargé depuis config.json + # BUG 5 — Le port est défini dans config.json — est-il correct ? + app.run(debug=True, port=config["api"]["port"]) diff --git a/python-api/requirements.txt b/python-api/requirements.txt index 5bd19d3..9fde49f 100644 --- a/python-api/requirements.txt +++ b/python-api/requirements.txt @@ -1 +1,2 @@ -flask==3.0.0 +# BUG 1 — Le nom du paquet ci-dessous est incorrect. Lisez attentivement. +flaskk==3.0.0 diff --git a/python-api/server.log b/python-api/server.log index 5eac220..3d4fb2e 100644 --- a/python-api/server.log +++ b/python-api/server.log @@ -1,19 +1,19 @@ -2024-01-15 08:00:01 INFO Application started on port 8080 -2024-01-15 08:00:05 INFO Connected to Azure SQL Database successfully -2024-01-15 08:01:22 WARNING High memory usage detected: 78% -2024-01-15 08:02:45 ERROR Failed to connect to Azure Storage: connection timeout -2024-01-15 08:03:10 INFO Request processed: GET /api/health [200] -2024-01-15 08:05:33 ERROR Authentication failed for service account: deploy_svc -2024-01-15 08:06:15 WARNING CPU usage spike detected: 92% -2024-01-15 08:07:42 ERROR Database query timeout after 30s on table: audit_logs -2024-01-15 08:08:00 INFO Retry attempt 1/3 for Azure Storage connection -2024-01-15 08:08:30 INFO Retry attempt 2/3 for Azure Storage connection -2024-01-15 08:09:00 ERROR Max retries exceeded - Azure Storage service unavailable -2024-01-15 08:10:15 WARNING Disk space below threshold: 15% remaining on /dev/sda1 -2024-01-15 08:11:22 INFO Backup job started: daily-backup-2024-01-15 -2024-01-15 08:12:45 ERROR Backup failed: insufficient permissions on /var/backup/azure -2024-01-15 08:14:03 INFO Alert sent to monitoring team via Azure Monitor -2024-01-15 08:15:00 INFO Scheduled maintenance check completed -2024-01-15 08:16:30 WARNING SSL certificate expires in 14 days for api.azuretech.fr -2024-01-15 08:18:55 INFO Kubernetes pod restarted: api-deployment-7d9f8b-xkp2m -2024-01-15 08:20:00 INFO Health check passed: all 3 replicas running +2024-01-15 08:00:01 INFO Application started on port 8080 +2024-01-15 08:00:05 INFO Connected to Azure SQL Database successfully +2024-01-15 08:01:22 WARNING High memory usage detected: 78% +2024-01-15 08:02:45 ERROR Failed to connect to Azure Storage: connection timeout +2024-01-15 08:03:10 INFO Request processed: GET /api/health [200] +2024-01-15 08:05:33 ERROR Authentication failed for service account: deploy_svc +2024-01-15 08:06:15 WARNING CPU usage spike detected: 92% +2024-01-15 08:07:42 ERROR Database query timeout after 30s on table: audit_logs +2024-01-15 08:08:00 INFO Retry attempt 1/3 for Azure Storage connection +2024-01-15 08:08:30 INFO Retry attempt 2/3 for Azure Storage connection +2024-01-15 08:09:00 ERROR Max retries exceeded - Azure Storage service unavailable +2024-01-15 08:10:15 WARNING Disk space below threshold: 15% remaining on /dev/sda1 +2024-01-15 08:11:22 INFO Backup job started: daily-backup-2024-01-15 +2024-01-15 08:12:45 ERROR Backup failed: insufficient permissions on /var/backup/azure +2024-01-15 08:14:03 INFO Alert sent to monitoring team via Azure Monitor +2024-01-15 08:15:00 INFO Scheduled maintenance check completed +2024-01-15 08:16:30 WARNING SSL certificate expires in 14 days for api.azuretech.fr +2024-01-15 08:18:55 INFO Kubernetes pod restarted: api-deployment-7d9f8b-xkp2m +2024-01-15 08:20:00 INFO Health check passed: all 3 replicas running From 6f9cbf16571b2a1164080001e0907c7a3fe0eb8b Mon Sep 17 00:00:00 2001 From: Nathan Tesseyre Date: Thu, 21 May 2026 09:41:57 +0200 Subject: [PATCH 02/18] fix: correct flask package name in requirements.txt --- python-api/requirements.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/python-api/requirements.txt b/python-api/requirements.txt index 9fde49f..7b8e9a8 100644 --- a/python-api/requirements.txt +++ b/python-api/requirements.txt @@ -1,2 +1 @@ -# BUG 1 — Le nom du paquet ci-dessous est incorrect. Lisez attentivement. -flaskk==3.0.0 +flask==3.0.0 From 4cd4cdb69a78566902229bb6813eac9f7efe11df Mon Sep 17 00:00:00 2001 From: Nathan Tesseyre Date: Thu, 21 May 2026 09:43:49 +0200 Subject: [PATCH 03/18] fix: add missing colon in app.py --- python-api/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python-api/app.py b/python-api/app.py index 7afd548..b50af4e 100644 --- a/python-api/app.py +++ b/python-api/app.py @@ -15,7 +15,7 @@ # ------------------------------------------------------- # BUG 2 — Il manque un caractère essentiel à la fin de cette ligne -def parse_logs(filepath) +def parse_logs(filepath): erreurs = [] warnings = [] infos = [] From 89a45997dc5d3f897f7563779c1f4c450aeb5b9b Mon Sep 17 00:00:00 2001 From: Nathan Tesseyre Date: Thu, 21 May 2026 09:45:45 +0200 Subject: [PATCH 04/18] fix: add missing colon in app.py --- python-api/app.py | 1 - 1 file changed, 1 deletion(-) diff --git a/python-api/app.py b/python-api/app.py index b50af4e..f239769 100644 --- a/python-api/app.py +++ b/python-api/app.py @@ -14,7 +14,6 @@ # le nombre d'erreurs, warnings et infos détectés. # ------------------------------------------------------- -# BUG 2 — Il manque un caractère essentiel à la fin de cette ligne def parse_logs(filepath): erreurs = [] warnings = [] From 21f91933287c7e5bbc1add0f18bab47330dbc97e Mon Sep 17 00:00:00 2001 From: Nathan Tesseyre Date: Thu, 21 May 2026 09:48:04 +0200 Subject: [PATCH 05/18] fix: use english variable name errors instead of erreurs in app.py --- python-api/app.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/python-api/app.py b/python-api/app.py index f239769..d33a828 100644 --- a/python-api/app.py +++ b/python-api/app.py @@ -15,7 +15,7 @@ # ------------------------------------------------------- def parse_logs(filepath): - erreurs = [] + errors = [] warnings = [] infos = [] @@ -24,8 +24,6 @@ def parse_logs(filepath): line = line.strip() if not line: continue - # BUG 3 — Le nom de la variable utilisée ici ne correspond pas - # à celle déclarée plus haut dans cette fonction if "ERROR" in line: errors.append(line) elif "WARNING" in line: From 931bc724a5564b6577e224e89127b4209a99679f Mon Sep 17 00:00:00 2001 From: Nathan Tesseyre Date: Thu, 21 May 2026 09:48:47 +0200 Subject: [PATCH 06/18] fix: use english variable name errors instead of erreurs in app.py --- python-api/app.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/python-api/app.py b/python-api/app.py index d33a828..2801ab2 100644 --- a/python-api/app.py +++ b/python-api/app.py @@ -11,7 +11,7 @@ # ------------------------------------------------------- # Analyse un fichier de logs serveur et retourne -# le nombre d'erreurs, warnings et infos détectés. +# le nombre d'errors, warnings et infos détectés. # ------------------------------------------------------- def parse_logs(filepath): @@ -32,10 +32,10 @@ def parse_logs(filepath): infos.append(line) return { - "error_count": len(erreurs), + "error_count": len(errors), "warning_count": len(warnings), "info_count": len(infos), - "errors": erreurs, + "errors": errors, "warnings": warnings } From bb33acba900913a7a0a5cc91c0b566d157858dc3 Mon Sep 17 00:00:00 2001 From: Nathan Tesseyre Date: Thu, 21 May 2026 09:53:12 +0200 Subject: [PATCH 07/18] fix: update API port in config.json --- config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.json b/config.json index 5343695..46dd74d 100644 --- a/config.json +++ b/config.json @@ -3,7 +3,7 @@ "promotion": "DevSecOps Azure — Simplon", "apprenants": [], "api": { - "port": 50001, + "port": 5000, "host": "localhost", "route": "/api/logs", "log_file": "server.log" From 99719557ce39fcb869b0946e6ceb1824e16721b6 Mon Sep 17 00:00:00 2001 From: Nathan Tesseyre Date: Thu, 21 May 2026 09:53:47 +0200 Subject: [PATCH 08/18] fix: update API port in config.json --- node-client/app.js | 1 - 1 file changed, 1 deletion(-) diff --git a/node-client/app.js b/node-client/app.js index 8f8bcca..c6355f5 100644 --- a/node-client/app.js +++ b/node-client/app.js @@ -4,7 +4,6 @@ const path = require('path'); // Configuration partagée chargée depuis config.json (à la racine du projet) -// BUG 5 (suite) — Si l'API Python ne répond pas, vérifiez le port dans config.json const config = require(path.join(__dirname, '..', 'config.json')); const API_URL = `http://${config.api.host}:${config.api.port}${config.api.route}`; From 8e06e83658dfae641726b8e567b545ab6733135e Mon Sep 17 00:00:00 2001 From: Nathan Tesseyre Date: Thu, 21 May 2026 09:57:50 +0200 Subject: [PATCH 09/18] fix: hardcode log filename instead of uninitialized variable in app.py --- python-api/app.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/python-api/app.py b/python-api/app.py index 2801ab2..97a64f0 100644 --- a/python-api/app.py +++ b/python-api/app.py @@ -42,9 +42,7 @@ def parse_logs(filepath): @app.route("/api/logs", methods=["GET"]) def get_logs(): - # BUG 4 — La variable passée en argument n'est définie nulle part - # Quel fichier de logs doit-on analyser ? - result = parse_logs(log_file) + result = parse_logs("server.log") return jsonify(result), 200 From 01d30d91ecd8990b45c8f5a8cb823b7cdbf9c7b0 Mon Sep 17 00:00:00 2001 From: Nathan Tesseyre Date: Thu, 21 May 2026 09:59:33 +0200 Subject: [PATCH 10/18] chore: remove outdated comments in app.py --- python-api/app.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/python-api/app.py b/python-api/app.py index 97a64f0..9ed4cdf 100644 --- a/python-api/app.py +++ b/python-api/app.py @@ -47,6 +47,4 @@ def get_logs(): if __name__ == "__main__": - # Le port est chargé depuis config.json - # BUG 5 — Le port est défini dans config.json — est-il correct ? app.run(debug=True, port=config["api"]["port"]) From 938886670de5292079eb2307ffb30c4391b49746 Mon Sep 17 00:00:00 2001 From: Nathan Tesseyre Date: Thu, 21 May 2026 10:01:49 +0200 Subject: [PATCH 11/18] fix: correct axios import typo --- node-client/app.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/node-client/app.js b/node-client/app.js index c6355f5..a836cbf 100644 --- a/node-client/app.js +++ b/node-client/app.js @@ -7,12 +7,11 @@ const path = require('path'); const config = require(path.join(__dirname, '..', 'config.json')); const API_URL = `http://${config.api.host}:${config.api.port}${config.api.route}`; -// BUG 6 — Le nom du module importé ici est incorrect -const axioss = require('axioss'); +const axios = require('axios'); async function getLogs() { try { - const response = await axioss.get(API_URL); + const response = await axios.get(API_URL); // BUG 7 — La propriété pour accéder au corps de la réponse avec axios // ne s'appelle pas .body — cherchez dans la doc axios comment From 07830686e03c4cdf82c9721fae9fa0c2e16c74c6 Mon Sep 17 00:00:00 2001 From: Nathan Tesseyre Date: Thu, 21 May 2026 10:05:51 +0200 Subject: [PATCH 12/18] chore: use axios conventional response.data instead of response.body --- node-client/app.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/node-client/app.js b/node-client/app.js index a836cbf..38af06e 100644 --- a/node-client/app.js +++ b/node-client/app.js @@ -13,10 +13,7 @@ async function getLogs() { try { const response = await axios.get(API_URL); - // BUG 7 — La propriété pour accéder au corps de la réponse avec axios - // ne s'appelle pas .body — cherchez dans la doc axios comment - // accéder aux données de la réponse - const data = response.body; + const data = response.data; console.log('\n========================================'); console.log(' RAPPORT D\'ANALYSE DES LOGS AZURE '); From cdb6f4beef6bc6f9fd36919e45f2d401441f7589 Mon Sep 17 00:00:00 2001 From: Nathan Tesseyre Date: Thu, 21 May 2026 10:08:55 +0200 Subject: [PATCH 13/18] fix: correct axios dependency name in package.json --- node-client/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node-client/package.json b/node-client/package.json index 10789eb..77cf210 100644 --- a/node-client/package.json +++ b/node-client/package.json @@ -7,6 +7,6 @@ "start": "node app.js" }, "dependencies": { - "axioss": "^1.6.0" + "axios": "^1.6.0" } } From 90fd1afdca08f082896b63d3212df94ea48670ac Mon Sep 17 00:00:00 2001 From: Nathan Tesseyre Date: Thu, 21 May 2026 10:11:26 +0200 Subject: [PATCH 14/18] chore: add node_modules dir in .gitignore --- .gitignore | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index dc7c725..2fbf524 100644 --- a/.gitignore +++ b/.gitignore @@ -46,4 +46,7 @@ terraform.rc .DS_Store # Ignorer les .venv -.venv \ No newline at end of file +.venv + +# Ignorer le dossier node_modules +node_modules \ No newline at end of file From f745dd7afbb9e91f26a070d8c24f236e1894caa8 Mon Sep 17 00:00:00 2001 From: Nathan Tesseyre Date: Thu, 21 May 2026 10:14:10 +0200 Subject: [PATCH 15/18] chore: add package-lock.json --- node-client/package-lock.json | 692 +++++++++++++++++----------------- 1 file changed, 346 insertions(+), 346 deletions(-) diff --git a/node-client/package-lock.json b/node-client/package-lock.json index 6fc99fe..cc86a3c 100644 --- a/node-client/package-lock.json +++ b/node-client/package-lock.json @@ -1,346 +1,346 @@ -{ - "name": "log-analyser-client", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "log-analyser-client", - "version": "1.0.0", - "dependencies": { - "axios": "^1.6.0" - } - }, - "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "license": "MIT", - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "license": "MIT" - }, - "node_modules/axios": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.16.1.tgz", - "integrity": "sha512-caYkukvroVPO8KrzuJEb50Hm07KwfBZPEC3VeFHTsqWHvKTsy54hjJz9BS/cdaypROE2rH6xvm9mHX4fgWkr3A==", - "license": "MIT", - "dependencies": { - "follow-redirects": "^1.16.0", - "form-data": "^4.0.5", - "https-proxy-agent": "^5.0.1", - "proxy-from-env": "^2.1.0" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/follow-redirects": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", - "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "license": "MIT", - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/form-data": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", - "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "es-set-tostringtag": "^2.1.0", - "hasown": "^2.0.2", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "license": "MIT", - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", - "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "license": "MIT", - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/proxy-from-env": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", - "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", - "license": "MIT", - "engines": { - "node": ">=10" - } - } - } -} +{ + "name": "log-analyser-client", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "log-analyser-client", + "version": "1.0.0", + "dependencies": { + "axios": "^1.6.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/axios": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.16.1.tgz", + "integrity": "sha512-caYkukvroVPO8KrzuJEb50Hm07KwfBZPEC3VeFHTsqWHvKTsy54hjJz9BS/cdaypROE2rH6xvm9mHX4fgWkr3A==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.16.0", + "form-data": "^4.0.5", + "https-proxy-agent": "^5.0.1", + "proxy-from-env": "^2.1.0" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/follow-redirects": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", + "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", + "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/proxy-from-env": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", + "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + } + } +} From 6097e7060fdfbd68a3708adea500857ec93e37c0 Mon Sep 17 00:00:00 2001 From: Nathan Tesseyre Date: Thu, 21 May 2026 11:16:31 +0200 Subject: [PATCH 16/18] =?UTF-8?q?ci:=20ajout=20pipeline=20GitHub=20Actions?= =?UTF-8?q?=20v=C3=A9rification=20Python=20et=20Node?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 50 ++++++++++++++++++++++++++++++++++------ 1 file changed, 43 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 61a1d71..f055c5d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,18 +1,54 @@ -name: CI +name: CI — Vérification API Python on: push: - branches: [ "main" ] + branches: [ main, 'fix/**', 'feat/**' ] pull_request: - branches: [ "main" ] + branches: [ main ] jobs: - build: + test-python-api: runs-on: ubuntu-latest steps: - - name: Checkout du code + - name: Récupérer le code uses: actions/checkout@v4 - - name: Exemple d'étape - run: echo "Ajoute tes étapes de build/test ici !" + - name: Installer Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: '3.11' + + - name: Installer les dépendances + run: | + cd python-api + pip install -r requirements.txt + + - name: Vérifier que Flask démarre sans erreur + run: | + cd python-api + timeout 5 python app.py || true + echo "Vérification terminée" + + test-node-client: + runs-on: ubuntu-latest + + steps: + - name: Récupérer le code + uses: actions/checkout@v4 + + - name: Installer Node.js 18 + uses: actions/setup-node@v4 + with: + node-version: '18' + + - name: Installer les dépendances npm + run: | + cd node-client + npm install + + - name: Vérifier la syntaxe JavaScript + run: | + cd node-client + node --check app.js + echo "Syntaxe JavaScript valide" \ No newline at end of file From 54b287e9fac773419fdfe993c6c9b9e09d5eac80 Mon Sep 17 00:00:00 2001 From: Nathan Tesseyre Date: Thu, 21 May 2026 12:23:42 +0200 Subject: [PATCH 17/18] chore: use config.json to load log_file in app.py instead of hardcoded value --- python-api/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python-api/app.py b/python-api/app.py index 9ed4cdf..7f34a1e 100644 --- a/python-api/app.py +++ b/python-api/app.py @@ -42,7 +42,7 @@ def parse_logs(filepath): @app.route("/api/logs", methods=["GET"]) def get_logs(): - result = parse_logs("server.log") + result = parse_logs(config["api"]["log_file"]) return jsonify(result), 200 From 25c4b27a4c2a68ca393238fa38f15fbf149892f5 Mon Sep 17 00:00:00 2001 From: NathanTesseyre Date: Fri, 22 May 2026 13:23:13 +0200 Subject: [PATCH 18/18] Revert "chore: add .venv to .gitignore" This reverts commit f9094f9a82eef27e8650af6c7fcb3660f994d7df. --- .github/workflows/ci.yml | 106 ++-- .gitignore | 8 +- CODES-HTTP.md | 378 +++++------ CONFLITS.md | 548 ++++++++-------- README.md | 1120 ++++++++++++++++----------------- RESSOURCES.md | 312 ++++----- config.json | 22 +- node-client/app.js | 70 +-- node-client/package-lock.json | 692 ++++++++++---------- node-client/package.json | 24 +- python-api/app.py | 100 +-- python-api/requirements.txt | 2 +- python-api/server.log | 38 +- 13 files changed, 1713 insertions(+), 1707 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f055c5d..b7c8cb8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,54 +1,54 @@ -name: CI — Vérification API Python - -on: - push: - branches: [ main, 'fix/**', 'feat/**' ] - pull_request: - branches: [ main ] - -jobs: - test-python-api: - runs-on: ubuntu-latest - - steps: - - name: Récupérer le code - uses: actions/checkout@v4 - - - name: Installer Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: '3.11' - - - name: Installer les dépendances - run: | - cd python-api - pip install -r requirements.txt - - - name: Vérifier que Flask démarre sans erreur - run: | - cd python-api - timeout 5 python app.py || true - echo "Vérification terminée" - - test-node-client: - runs-on: ubuntu-latest - - steps: - - name: Récupérer le code - uses: actions/checkout@v4 - - - name: Installer Node.js 18 - uses: actions/setup-node@v4 - with: - node-version: '18' - - - name: Installer les dépendances npm - run: | - cd node-client - npm install - - - name: Vérifier la syntaxe JavaScript - run: | - cd node-client - node --check app.js +name: CI — Vérification API Python + +on: + push: + branches: [ main, 'fix/**', 'feat/**' ] + pull_request: + branches: [ main ] + +jobs: + test-python-api: + runs-on: ubuntu-latest + + steps: + - name: Récupérer le code + uses: actions/checkout@v4 + + - name: Installer Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: '3.11' + + - name: Installer les dépendances + run: | + cd python-api + pip install -r requirements.txt + + - name: Vérifier que Flask démarre sans erreur + run: | + cd python-api + timeout 5 python app.py || true + echo "Vérification terminée" + + test-node-client: + runs-on: ubuntu-latest + + steps: + - name: Récupérer le code + uses: actions/checkout@v4 + + - name: Installer Node.js 18 + uses: actions/setup-node@v4 + with: + node-version: '18' + + - name: Installer les dépendances npm + run: | + cd node-client + npm install + + - name: Vérifier la syntaxe JavaScript + run: | + cd node-client + node --check app.js echo "Syntaxe JavaScript valide" \ No newline at end of file diff --git a/.gitignore b/.gitignore index 2fbf524..b8a7072 100644 --- a/.gitignore +++ b/.gitignore @@ -49,4 +49,10 @@ terraform.rc .venv # Ignorer le dossier node_modules -node_modules \ No newline at end of file +node_modules + +logs + +email-template.md + +venv \ No newline at end of file diff --git a/CODES-HTTP.md b/CODES-HTTP.md index e6247be..772fc66 100644 --- a/CODES-HTTP.md +++ b/CODES-HTTP.md @@ -1,189 +1,189 @@ -# Les codes HTTP — Guide pratique DevOps - -Quand votre client Node.js appelle l'API Python, les deux programmes communiquent via HTTP. -Chaque réponse contient un **code à 3 chiffres** qui indique si la requête a réussi ou échoué. - -Le premier chiffre indique la catégorie : -- **2xx** — Succès -- **4xx** — Erreur due au client (mauvaise requête, mauvaise URL, pas autorisé…) -- **5xx** — Erreur due au serveur (le code a planté, la base de données est inaccessible…) - ---- - -## 2xx — Succès - -### 200 OK -La requête a réussi. C'est la réponse attendue quand tout fonctionne. - -``` -GET /api/logs → 200 OK -{ "error_count": 5, "warning_count": 4 ... } -``` - -Dans ce TP, c'est le code que retourne Flask quand les logs sont analysés sans erreur. - -### 201 Created -La ressource a été créée avec succès. Utilisé en réponse à une requête POST qui crée un objet (un utilisateur, un ticket, un déploiement…). Vous ne le verrez pas dans ce TP, mais vous le croiserez souvent dans des APIs Azure. - -### 204 No Content -La requête a réussi mais le serveur ne retourne rien. Courant pour les requêtes de suppression (DELETE). - ---- - -## 4xx — Erreur côté client - -Ces erreurs signifient que **vous avez fait quelque chose d'incorrect** dans votre requête. - -### 400 Bad Request -La requête est malformée — paramètre manquant, format incorrect, JSON invalide. - -``` -Exemple : vous envoyez un JSON avec une virgule en trop -→ 400 Bad Request : "invalid JSON body" -``` - -### 401 Unauthorized -Vous n'êtes pas authentifié. Le serveur vous demande de vous identifier avant d'accéder à cette ressource. - -``` -Exemple : appel à une API Azure sans token d'authentification -→ 401 Unauthorized : "Missing or invalid Bearer token" -``` - -### 403 Forbidden -Vous êtes authentifié, mais vous n'avez pas les droits pour cette action. Contrairement au 401, s'identifier à nouveau ne changera rien. - -``` -Exemple : un compte de service sans les permissions RBAC Azure nécessaires -→ 403 Forbidden : "Insufficient permissions on resource group" -``` - -> En DevSecOps, 401 et 403 sont des codes à surveiller de près dans les logs : une accumulation peut signaler une tentative d'intrusion. - -### 404 Not Found -La ressource demandée n'existe pas à cette URL. - -``` -Exemple : curl http://localhost:5000/api/log (sans 's') -→ 404 Not Found -``` - -C'est une erreur fréquente quand on tape mal une route Flask. Vérifiez l'URL et le décorateur `@app.route()` dans le code. - -### 405 Method Not Allowed -Vous utilisez la mauvaise méthode HTTP (GET, POST, PUT, DELETE…). - -``` -Exemple : envoyer un POST sur une route qui n'accepte que GET -→ 405 Method Not Allowed -``` - -### 422 Unprocessable Entity -La requête est bien formée mais les données envoyées sont sémantiquement incorrectes. - -``` -Exemple : envoyer un champ "age" avec la valeur "bonjour" au lieu d'un nombre -→ 422 Unprocessable Entity -``` - ---- - -## 5xx — Erreur côté serveur - -Ces erreurs signifient que **le serveur a rencontré un problème**. Ce n'est pas de votre faute en tant que client — c'est le code serveur qui a planté. - -### 500 Internal Server Error -Le serveur a rencontré une erreur inattendue. C'est le code générique quand une exception Python non gérée survient dans Flask. - -``` -Exemple : votre app.py a un bug Python non corrigé -→ 500 Internal Server Error -``` - -Dans ce TP, si vous voyez un 500, cela signifie que l'API Flask tourne mais qu'elle plante au moment de traiter la requête. Lisez les logs dans le terminal où Python s'exécute — l'erreur Python y sera affichée. - -### 502 Bad Gateway -Un serveur intermédiaire (proxy, load balancer) n'a pas pu obtenir de réponse valide du serveur en amont. - -``` -Exemple courant : votre conteneur Docker ou pod Kubernetes a planté, -mais le load balancer Azure est toujours actif -→ 502 Bad Gateway -``` - -### 503 Service Unavailable -Le serveur est temporairement indisponible — surchargé ou en cours de maintenance. - -``` -Exemple : Azure App Service redémarre après un déploiement -→ 503 Service Unavailable (pendant quelques secondes) -``` - -### 504 Gateway Timeout -Le serveur intermédiaire n'a pas reçu de réponse du serveur en amont dans le temps imparti. - -``` -Exemple : une requête à une base de données Azure SQL prend trop longtemps -→ 504 Gateway Timeout -``` - ---- - -## Erreurs de connexion Node.js — avant même le code HTTP - -Certaines erreurs surviennent avant qu'une réponse HTTP soit reçue. Vous les verrez dans le terminal Node sous forme de codes d'erreur système. - -### ECONNREFUSED -Le serveur n'accepte aucune connexion sur ce port. Cela signifie que le serveur n'est pas démarré, ou que vous utilisez le mauvais port. - -``` -Error: connect ECONNREFUSED 127.0.0.1:5001 -→ L'API Python n'est pas démarrée, ou elle écoute sur un autre port -``` - -C'est l'erreur que vous obtiendrez dans ce TP si le bug du port (5001 vs 5000) n'est pas corrigé. - -### ENOTFOUND -Le nom de domaine ne peut pas être résolu — l'adresse n'existe pas sur le réseau. - -``` -Error: getaddrinfo ENOTFOUND mon-api.azuretech.fr -→ L'URL est incorrecte ou le service n'est pas déployé -``` - -### ETIMEDOUT -La connexion a été tentée mais aucune réponse n'est arrivée dans le délai imparti. - -``` -Error: connect ETIMEDOUT -→ Le serveur existe mais ne répond pas (pare-feu, surcharge, réseau lent) -``` - ---- - -## Résumé visuel - -``` -Requête HTTP - │ - ├── 2xx ──► Succès ✅ Tout va bien - │ - ├── 4xx ──► Erreur client ⚠️ Vérifiez votre requête (URL, méthode, auth) - │ - ├── 5xx ──► Erreur serveur ❌ Le serveur a planté — lisez ses logs - │ - └── ERR_ ──► Pas de réponse 🔌 Le serveur est inaccessible (port, réseau) -``` - ---- - -## Dans Azure, où voir ces codes ? - -En production sur Azure, ces codes apparaissent dans plusieurs endroits : - -- **Azure Monitor** — tableau de bord des codes HTTP par période -- **Application Insights** — détail requête par requête avec la durée -- **Log Analytics** — requêtes KQL pour filtrer les 5xx ou les 4xx -- **Azure API Management** — gateway qui centralise tous les appels API et leurs codes de retour - -Apprendre à lire ces codes maintenant, c'est poser les bases de la surveillance d'infrastructure que vous pratiquerez tout au long de cette formation. +# Les codes HTTP — Guide pratique DevOps + +Quand votre client Node.js appelle l'API Python, les deux programmes communiquent via HTTP. +Chaque réponse contient un **code à 3 chiffres** qui indique si la requête a réussi ou échoué. + +Le premier chiffre indique la catégorie : +- **2xx** — Succès +- **4xx** — Erreur due au client (mauvaise requête, mauvaise URL, pas autorisé…) +- **5xx** — Erreur due au serveur (le code a planté, la base de données est inaccessible…) + +--- + +## 2xx — Succès + +### 200 OK +La requête a réussi. C'est la réponse attendue quand tout fonctionne. + +``` +GET /api/logs → 200 OK +{ "error_count": 5, "warning_count": 4 ... } +``` + +Dans ce TP, c'est le code que retourne Flask quand les logs sont analysés sans erreur. + +### 201 Created +La ressource a été créée avec succès. Utilisé en réponse à une requête POST qui crée un objet (un utilisateur, un ticket, un déploiement…). Vous ne le verrez pas dans ce TP, mais vous le croiserez souvent dans des APIs Azure. + +### 204 No Content +La requête a réussi mais le serveur ne retourne rien. Courant pour les requêtes de suppression (DELETE). + +--- + +## 4xx — Erreur côté client + +Ces erreurs signifient que **vous avez fait quelque chose d'incorrect** dans votre requête. + +### 400 Bad Request +La requête est malformée — paramètre manquant, format incorrect, JSON invalide. + +``` +Exemple : vous envoyez un JSON avec une virgule en trop +→ 400 Bad Request : "invalid JSON body" +``` + +### 401 Unauthorized +Vous n'êtes pas authentifié. Le serveur vous demande de vous identifier avant d'accéder à cette ressource. + +``` +Exemple : appel à une API Azure sans token d'authentification +→ 401 Unauthorized : "Missing or invalid Bearer token" +``` + +### 403 Forbidden +Vous êtes authentifié, mais vous n'avez pas les droits pour cette action. Contrairement au 401, s'identifier à nouveau ne changera rien. + +``` +Exemple : un compte de service sans les permissions RBAC Azure nécessaires +→ 403 Forbidden : "Insufficient permissions on resource group" +``` + +> En DevSecOps, 401 et 403 sont des codes à surveiller de près dans les logs : une accumulation peut signaler une tentative d'intrusion. + +### 404 Not Found +La ressource demandée n'existe pas à cette URL. + +``` +Exemple : curl http://localhost:5000/api/log (sans 's') +→ 404 Not Found +``` + +C'est une erreur fréquente quand on tape mal une route Flask. Vérifiez l'URL et le décorateur `@app.route()` dans le code. + +### 405 Method Not Allowed +Vous utilisez la mauvaise méthode HTTP (GET, POST, PUT, DELETE…). + +``` +Exemple : envoyer un POST sur une route qui n'accepte que GET +→ 405 Method Not Allowed +``` + +### 422 Unprocessable Entity +La requête est bien formée mais les données envoyées sont sémantiquement incorrectes. + +``` +Exemple : envoyer un champ "age" avec la valeur "bonjour" au lieu d'un nombre +→ 422 Unprocessable Entity +``` + +--- + +## 5xx — Erreur côté serveur + +Ces erreurs signifient que **le serveur a rencontré un problème**. Ce n'est pas de votre faute en tant que client — c'est le code serveur qui a planté. + +### 500 Internal Server Error +Le serveur a rencontré une erreur inattendue. C'est le code générique quand une exception Python non gérée survient dans Flask. + +``` +Exemple : votre app.py a un bug Python non corrigé +→ 500 Internal Server Error +``` + +Dans ce TP, si vous voyez un 500, cela signifie que l'API Flask tourne mais qu'elle plante au moment de traiter la requête. Lisez les logs dans le terminal où Python s'exécute — l'erreur Python y sera affichée. + +### 502 Bad Gateway +Un serveur intermédiaire (proxy, load balancer) n'a pas pu obtenir de réponse valide du serveur en amont. + +``` +Exemple courant : votre conteneur Docker ou pod Kubernetes a planté, +mais le load balancer Azure est toujours actif +→ 502 Bad Gateway +``` + +### 503 Service Unavailable +Le serveur est temporairement indisponible — surchargé ou en cours de maintenance. + +``` +Exemple : Azure App Service redémarre après un déploiement +→ 503 Service Unavailable (pendant quelques secondes) +``` + +### 504 Gateway Timeout +Le serveur intermédiaire n'a pas reçu de réponse du serveur en amont dans le temps imparti. + +``` +Exemple : une requête à une base de données Azure SQL prend trop longtemps +→ 504 Gateway Timeout +``` + +--- + +## Erreurs de connexion Node.js — avant même le code HTTP + +Certaines erreurs surviennent avant qu'une réponse HTTP soit reçue. Vous les verrez dans le terminal Node sous forme de codes d'erreur système. + +### ECONNREFUSED +Le serveur n'accepte aucune connexion sur ce port. Cela signifie que le serveur n'est pas démarré, ou que vous utilisez le mauvais port. + +``` +Error: connect ECONNREFUSED 127.0.0.1:5001 +→ L'API Python n'est pas démarrée, ou elle écoute sur un autre port +``` + +C'est l'erreur que vous obtiendrez dans ce TP si le bug du port (5001 vs 5000) n'est pas corrigé. + +### ENOTFOUND +Le nom de domaine ne peut pas être résolu — l'adresse n'existe pas sur le réseau. + +``` +Error: getaddrinfo ENOTFOUND mon-api.azuretech.fr +→ L'URL est incorrecte ou le service n'est pas déployé +``` + +### ETIMEDOUT +La connexion a été tentée mais aucune réponse n'est arrivée dans le délai imparti. + +``` +Error: connect ETIMEDOUT +→ Le serveur existe mais ne répond pas (pare-feu, surcharge, réseau lent) +``` + +--- + +## Résumé visuel + +``` +Requête HTTP + │ + ├── 2xx ──► Succès ✅ Tout va bien + │ + ├── 4xx ──► Erreur client ⚠️ Vérifiez votre requête (URL, méthode, auth) + │ + ├── 5xx ──► Erreur serveur ❌ Le serveur a planté — lisez ses logs + │ + └── ERR_ ──► Pas de réponse 🔌 Le serveur est inaccessible (port, réseau) +``` + +--- + +## Dans Azure, où voir ces codes ? + +En production sur Azure, ces codes apparaissent dans plusieurs endroits : + +- **Azure Monitor** — tableau de bord des codes HTTP par période +- **Application Insights** — détail requête par requête avec la durée +- **Log Analytics** — requêtes KQL pour filtrer les 5xx ou les 4xx +- **Azure API Management** — gateway qui centralise tous les appels API et leurs codes de retour + +Apprendre à lire ces codes maintenant, c'est poser les bases de la surveillance d'infrastructure que vous pratiquerez tout au long de cette formation. diff --git a/CONFLITS.md b/CONFLITS.md index 882409d..f365e10 100644 --- a/CONFLITS.md +++ b/CONFLITS.md @@ -1,274 +1,274 @@ -# Gestion des conflits Git — Étape 7 - -**Durée estimée :** 45 min -**Prérequis :** avoir terminé les étapes 1 à 6 (bugs corrigés, PR ouverte) - ---- - -## Pourquoi les conflits arrivent-ils ? - -En équipe, plusieurs développeurs travaillent en parallèle sur les mêmes fichiers. -Quand deux personnes modifient les mêmes lignes d'un fichier sur des branches différentes, -Git ne sait pas quelle version garder — il vous demande de trancher. C'est un **conflit de fusion**. - -``` -Branche fix/marie Branche fix/karim - │ │ - modifie config.json modifie config.json - (même lignes) (même lignes) - │ │ - └──────────┬───────────────┘ - │ - CONFLIT - Git ne peut pas - merger seul -``` - -Ce n'est pas une erreur — c'est un mécanisme de sécurité. Git refuse d'écraser -le travail de quelqu'un sans votre accord explicite. - ---- - -## Ce qui va se passer dans ce TP - -1. Tout le monde a corrigé les bugs et ouvert une PR sur GitHub -2. Le formateur merge **la PR d'une seule personne** vers `main` -3. `main` est maintenant différent de votre branche locale -4. Quand vous essayez de mettre votre branche à jour → **conflit sur `config.json`** -5. Vous résolvez le conflit, poussez à nouveau → votre PR peut être mergée - ---- - -## Étape 7.1 — Préparer le conflit : compléter config.json - -Avant que le formateur merge la première PR, chaque apprenant doit -**ajouter son prénom** dans le tableau `"apprenants"` de `config.json`. - -Ouvrez `config.json` et modifiez le tableau : - -```json -{ - "projet": "TP-Git-Collaboratif", - "promotion": "DevSecOps Azure — Simplon", - "apprenants": ["Votre Prénom"], - "api": { - "port": 5000, - "host": "localhost", - "route": "/api/logs", - "log_file": "server.log" - } -} -``` - -Commitez cette modification : - -```bash -git add config.json -git commit -m "chore: ajout prénom dans config.json + correction port" -git push origin fix/prenom-debug-python-node -``` - ---- - -## Étape 7.2 — Le formateur merge une PR (signal de départ) - -Le formateur annonce quelle PR il merge sur `main`. -À partir de ce moment, `main` contient les corrections de cette personne — -y compris son prénom dans `config.json`. - -Votre branche, elle, contient votre prénom. -Les deux versions sont incompatibles sur la même ligne → conflit garanti. - ---- - -## Étape 7.3 — Récupérer les changements de main - -```bash -# Récupérer l'état actuel du dépôt distant sans modifier vos fichiers -git fetch origin - -# Vérifier la différence entre votre branche et main -git log --oneline origin/main..HEAD -``` - ---- - -## Étape 7.4 — Rebaser votre branche sur main - -Le **rebase** réapplique vos commits par-dessus les derniers commits de `main`. -C'est la méthode préférée en DevOps car elle produit un historique linéaire et propre. - -```bash -git rebase origin/main -``` - -Git va s'arrêter et afficher un message comme celui-ci : - -``` -CONFLICT (content): Merge conflict in config.json -error: could not apply abc1234... chore: ajout prénom dans config.json -hint: Resolve all conflicts manually, mark them as resolved with -hint: "git add/rm ", then run "git rebase --continue". -``` - ---- - -## Étape 7.5 — Comprendre les marqueurs de conflit - -Ouvrez `config.json` dans votre éditeur. Vous verrez quelque chose comme : - -```json -{ - "projet": "TP-Git-Collaboratif", - "promotion": "DevSecOps Azure — Simplon", -<<<<<<< HEAD - "apprenants": ["Marie"], -======= - "apprenants": ["Karim"], ->>>>>>> abc1234 (chore: ajout prénom dans config.json + correction port) - "api": { - "port": 5000, -``` - -**Décryptage des marqueurs :** - -| Marqueur | Signification | -|----------|---------------| -| `<<<<<<< HEAD` | Début de VOTRE version (celle sur main après rebase) | -| `=======` | Séparateur entre les deux versions | -| `>>>>>>> abc1234` | Fin de VOTRE commit en cours d'application | - -> Les deux versions ont corrigé le port à 5000 — c'est cohérent. -> Le seul vrai conflit est sur les prénoms : chacun n'a ajouté que le sien. - ---- - -## Étape 7.6 — Résoudre le conflit - -La résolution correcte ici est de **garder les deux prénoms**. -Supprimez les marqueurs et fusionnez manuellement le contenu : - -```json -{ - "projet": "TP-Git-Collaboratif", - "promotion": "DevSecOps Azure — Simplon", - "apprenants": ["Marie", "Karim"], - "api": { - "port": 5000, - "host": "localhost", - "route": "/api/logs", - "log_file": "server.log" - } -} -``` - -> Règle d'or : un conflit résolu ne doit contenir aucun marqueur -> (`<<<<<<<`, `=======`, `>>>>>>>`). S'il en reste un, le fichier est invalide. - ---- - -## Étape 7.7 — Valider et continuer le rebase - -```bash -# Vérifier que config.json ne contient plus de marqueurs -cat config.json - -# Marquer le conflit comme résolu -git add config.json - -# Continuer le rebase -git rebase --continue -``` - -Git vous demandera éventuellement de valider le message de commit (éditeur qui s'ouvre) — vous pouvez le laisser tel quel et fermer. - ---- - -## Étape 7.8 — Pousser la branche mise à jour - -Après un rebase, l'historique de votre branche a changé. -Il faut forcer le push — en utilisant `--force-with-lease` qui est plus sûr -que `--force` car il vérifie que personne d'autre n'a pushé entre-temps. - -```bash -git push --force-with-lease origin fix/prenom-debug-python-node -``` - -Votre PR sur GitHub se mettra à jour automatiquement. - ---- - -## Rebase vs Merge — quelle différence ? - -Il existe deux façons d'intégrer les changements de `main` dans votre branche : - -**`git rebase origin/main`** (recommandé en équipe) -- Réapplique vos commits par-dessus `main` -- Historique linéaire, plus lisible -- Nécessite un `push --force-with-lease` - -**`git merge origin/main`** (plus simple à apprendre) -- Crée un commit de fusion supplémentaire -- Historique non-linéaire mais plus sûr pour les débutants -- Push normal possible - -``` -Avant rebase / merge : - -main : A --- B --- C (commit de Karim) - \ -votre branche : D (votre commit) - -Après REBASE : -main : A --- B --- C --- D' (votre commit réappliqué) - -Après MERGE : -main : A --- B --- C ------- M (commit de merge) - \ / -votre branche : D -``` - -Pour ce TP, les deux approches sont acceptées. -En entreprise, la convention dépend de l'équipe — demandez toujours avant de pousser. - ---- - -## Commandes de secours - -Si vous êtes bloqué et voulez tout annuler : - -```bash -# Annuler le rebase en cours et revenir à l'état avant -git rebase --abort - -# Voir l'état actuel des fichiers en conflit -git status - -# Voir les différences dans un fichier conflictuel -git diff config.json -``` - ---- - -## Résumé des commandes - -```bash -git fetch origin # Récupérer les changements distants -git rebase origin/main # Rebaser sur main (peut créer un conflit) -# → éditer les fichiers conflictuels -git add config.json # Marquer comme résolu -git rebase --continue # Continuer le rebase -git push --force-with-lease origin # Pousser la branche mise à jour -``` - ---- - -## Bonnes pratiques à retenir - -**Faites des petits commits fréquents** — moins de risque de conflits massifs. - -**Rebasez régulièrement** — ne laissez pas votre branche diverger trop longtemps de `main`. Plus vous attendez, plus la résolution sera complexe. - -**Communiquez** — si vous savez que vous allez modifier un fichier critique partagé, prévenez l'équipe pour éviter de travailler simultanément sur les mêmes lignes. - -**Ne forcez jamais sur `main`** — le `push --force` n'est acceptable que sur votre propre branche de travail. Sur `main`, c'est une faute grave en équipe. +# Gestion des conflits Git — Étape 7 + +**Durée estimée :** 45 min +**Prérequis :** avoir terminé les étapes 1 à 6 (bugs corrigés, PR ouverte) + +--- + +## Pourquoi les conflits arrivent-ils ? + +En équipe, plusieurs développeurs travaillent en parallèle sur les mêmes fichiers. +Quand deux personnes modifient les mêmes lignes d'un fichier sur des branches différentes, +Git ne sait pas quelle version garder — il vous demande de trancher. C'est un **conflit de fusion**. + +``` +Branche fix/marie Branche fix/karim + │ │ + modifie config.json modifie config.json + (même lignes) (même lignes) + │ │ + └──────────┬───────────────┘ + │ + CONFLIT + Git ne peut pas + merger seul +``` + +Ce n'est pas une erreur — c'est un mécanisme de sécurité. Git refuse d'écraser +le travail de quelqu'un sans votre accord explicite. + +--- + +## Ce qui va se passer dans ce TP + +1. Tout le monde a corrigé les bugs et ouvert une PR sur GitHub +2. Le formateur merge **la PR d'une seule personne** vers `main` +3. `main` est maintenant différent de votre branche locale +4. Quand vous essayez de mettre votre branche à jour → **conflit sur `config.json`** +5. Vous résolvez le conflit, poussez à nouveau → votre PR peut être mergée + +--- + +## Étape 7.1 — Préparer le conflit : compléter config.json + +Avant que le formateur merge la première PR, chaque apprenant doit +**ajouter son prénom** dans le tableau `"apprenants"` de `config.json`. + +Ouvrez `config.json` et modifiez le tableau : + +```json +{ + "projet": "TP-Git-Collaboratif", + "promotion": "DevSecOps Azure — Simplon", + "apprenants": ["Votre Prénom"], + "api": { + "port": 5000, + "host": "localhost", + "route": "/api/logs", + "log_file": "server.log" + } +} +``` + +Commitez cette modification : + +```bash +git add config.json +git commit -m "chore: ajout prénom dans config.json + correction port" +git push origin fix/prenom-debug-python-node +``` + +--- + +## Étape 7.2 — Le formateur merge une PR (signal de départ) + +Le formateur annonce quelle PR il merge sur `main`. +À partir de ce moment, `main` contient les corrections de cette personne — +y compris son prénom dans `config.json`. + +Votre branche, elle, contient votre prénom. +Les deux versions sont incompatibles sur la même ligne → conflit garanti. + +--- + +## Étape 7.3 — Récupérer les changements de main + +```bash +# Récupérer l'état actuel du dépôt distant sans modifier vos fichiers +git fetch origin + +# Vérifier la différence entre votre branche et main +git log --oneline origin/main..HEAD +``` + +--- + +## Étape 7.4 — Rebaser votre branche sur main + +Le **rebase** réapplique vos commits par-dessus les derniers commits de `main`. +C'est la méthode préférée en DevOps car elle produit un historique linéaire et propre. + +```bash +git rebase origin/main +``` + +Git va s'arrêter et afficher un message comme celui-ci : + +``` +CONFLICT (content): Merge conflict in config.json +error: could not apply abc1234... chore: ajout prénom dans config.json +hint: Resolve all conflicts manually, mark them as resolved with +hint: "git add/rm ", then run "git rebase --continue". +``` + +--- + +## Étape 7.5 — Comprendre les marqueurs de conflit + +Ouvrez `config.json` dans votre éditeur. Vous verrez quelque chose comme : + +```json +{ + "projet": "TP-Git-Collaboratif", + "promotion": "DevSecOps Azure — Simplon", +<<<<<<< HEAD + "apprenants": ["Marie"], +======= + "apprenants": ["Karim"], +>>>>>>> abc1234 (chore: ajout prénom dans config.json + correction port) + "api": { + "port": 5000, +``` + +**Décryptage des marqueurs :** + +| Marqueur | Signification | +|----------|---------------| +| `<<<<<<< HEAD` | Début de VOTRE version (celle sur main après rebase) | +| `=======` | Séparateur entre les deux versions | +| `>>>>>>> abc1234` | Fin de VOTRE commit en cours d'application | + +> Les deux versions ont corrigé le port à 5000 — c'est cohérent. +> Le seul vrai conflit est sur les prénoms : chacun n'a ajouté que le sien. + +--- + +## Étape 7.6 — Résoudre le conflit + +La résolution correcte ici est de **garder les deux prénoms**. +Supprimez les marqueurs et fusionnez manuellement le contenu : + +```json +{ + "projet": "TP-Git-Collaboratif", + "promotion": "DevSecOps Azure — Simplon", + "apprenants": ["Marie", "Karim"], + "api": { + "port": 5000, + "host": "localhost", + "route": "/api/logs", + "log_file": "server.log" + } +} +``` + +> Règle d'or : un conflit résolu ne doit contenir aucun marqueur +> (`<<<<<<<`, `=======`, `>>>>>>>`). S'il en reste un, le fichier est invalide. + +--- + +## Étape 7.7 — Valider et continuer le rebase + +```bash +# Vérifier que config.json ne contient plus de marqueurs +cat config.json + +# Marquer le conflit comme résolu +git add config.json + +# Continuer le rebase +git rebase --continue +``` + +Git vous demandera éventuellement de valider le message de commit (éditeur qui s'ouvre) — vous pouvez le laisser tel quel et fermer. + +--- + +## Étape 7.8 — Pousser la branche mise à jour + +Après un rebase, l'historique de votre branche a changé. +Il faut forcer le push — en utilisant `--force-with-lease` qui est plus sûr +que `--force` car il vérifie que personne d'autre n'a pushé entre-temps. + +```bash +git push --force-with-lease origin fix/prenom-debug-python-node +``` + +Votre PR sur GitHub se mettra à jour automatiquement. + +--- + +## Rebase vs Merge — quelle différence ? + +Il existe deux façons d'intégrer les changements de `main` dans votre branche : + +**`git rebase origin/main`** (recommandé en équipe) +- Réapplique vos commits par-dessus `main` +- Historique linéaire, plus lisible +- Nécessite un `push --force-with-lease` + +**`git merge origin/main`** (plus simple à apprendre) +- Crée un commit de fusion supplémentaire +- Historique non-linéaire mais plus sûr pour les débutants +- Push normal possible + +``` +Avant rebase / merge : + +main : A --- B --- C (commit de Karim) + \ +votre branche : D (votre commit) + +Après REBASE : +main : A --- B --- C --- D' (votre commit réappliqué) + +Après MERGE : +main : A --- B --- C ------- M (commit de merge) + \ / +votre branche : D +``` + +Pour ce TP, les deux approches sont acceptées. +En entreprise, la convention dépend de l'équipe — demandez toujours avant de pousser. + +--- + +## Commandes de secours + +Si vous êtes bloqué et voulez tout annuler : + +```bash +# Annuler le rebase en cours et revenir à l'état avant +git rebase --abort + +# Voir l'état actuel des fichiers en conflit +git status + +# Voir les différences dans un fichier conflictuel +git diff config.json +``` + +--- + +## Résumé des commandes + +```bash +git fetch origin # Récupérer les changements distants +git rebase origin/main # Rebaser sur main (peut créer un conflit) +# → éditer les fichiers conflictuels +git add config.json # Marquer comme résolu +git rebase --continue # Continuer le rebase +git push --force-with-lease origin # Pousser la branche mise à jour +``` + +--- + +## Bonnes pratiques à retenir + +**Faites des petits commits fréquents** — moins de risque de conflits massifs. + +**Rebasez régulièrement** — ne laissez pas votre branche diverger trop longtemps de `main`. Plus vous attendez, plus la résolution sera complexe. + +**Communiquez** — si vous savez que vous allez modifier un fichier critique partagé, prévenez l'équipe pour éviter de travailler simultanément sur les mêmes lignes. + +**Ne forcez jamais sur `main`** — le `push --force` n'est acceptable que sur votre propre branche de travail. Sur `main`, c'est une faute grave en équipe. diff --git a/README.md b/README.md index 4c2e171..47fb19f 100644 --- a/README.md +++ b/README.md @@ -1,560 +1,560 @@ -# TP Git Collaboratif — Débogage Full-Stack Python + Node.js - -**Durée estimée :** 3h30 -**Niveau :** Intermédiaire -**Effectif :** 12 apprenants - -> Les ressources et explications complémentaires sont disponibles dans le fichier **[RESSOURCES.md](./RESSOURCES.md)**. - ---- - -## Avant de commencer — Comprendre les outils utilisés - -Si vous êtes en reconversion et que certains termes vous sont nouveaux, pas de panique. Voici l'essentiel à savoir avant de démarrer. - -**Python** est un langage de programmation très utilisé en DevOps pour automatiser des tâches : analyser des fichiers, interagir avec des APIs, lancer des scripts de déploiement. Dans ce TP, un script Python est chargé de lire un fichier de logs et d'en extraire les informations importantes. - -**Flask** est un framework Python qui permet de créer très simplement une API web. Concrètement, Flask transforme votre script Python en un petit serveur web que l'on peut interroger via une URL, comme on interrogerait un site. Ici, Flask va exposer les résultats de l'analyse des logs à l'adresse `http://localhost:5000/api/logs`. - -**Node.js** est un environnement qui permet d'exécuter du JavaScript en dehors d'un navigateur, directement dans le terminal. Il est très utilisé pour créer des outils en ligne de commande, des serveurs web ou, comme dans ce TP, des scripts qui consomment des APIs. - -**npm** (Node Package Manager) est le gestionnaire de paquets de Node.js. Il permet d'installer des bibliothèques tierces listées dans un fichier `package.json`, exactement comme `pip` installe les dépendances Python listées dans `requirements.txt`. - -**Une API REST** est un service web qui reçoit des requêtes HTTP et retourne des données, généralement au format JSON. Dans ce TP, Python joue le rôle du serveur (il fournit les données) et Node.js joue le rôle du client (il demande les données et les affiche). - -**Les logs serveur** sont des fichiers texte que les applications génèrent automatiquement pour enregistrer ce qui se passe : démarrages, erreurs, avertissements, requêtes reçues… En DevOps, analyser ces logs est une tâche quotidienne pour surveiller l'état d'une infrastructure. - ---- - -## Contexte du TP - -Vous êtes développeur·se DevOps chez **NexaCloud**. - -Suite à une mise à jour en production, deux scripts critiques tombent en erreur : - -- `python-api/app.py` — une API Flask qui lit les logs du serveur Azure et retourne une analyse JSON -- `node-client/app.js` — un client Node.js qui interroge cette API et affiche un rapport dans le terminal - -Ces deux scripts **communiquent ensemble** : Node appelle Python via HTTP. -Aucun des deux ne fonctionnera correctement tant que tous les bugs ne sont pas corrigés. - -Votre responsable technique vous demande de : -1. Cloner le dépôt sur votre machine -2. Reproduire les erreurs en exécutant les scripts -3. Identifier et corriger les bugs (8 au total) -4. Pousser vos corrections via une Pull Request -5. Envoyer un email de rapport à votre responsable - ---- - -## Architecture du projet - -``` -[Fichier de logs Azure] - server.log - | - v - python-api/app.py <- API Flask (port 5000) - (lit les logs, compte <- Lance avec : python app.py - les ERROR / WARNING / INFO) - | - | HTTP GET /api/logs - v - node-client/app.js <- Client Node.js - (affiche le rapport <- Lance avec : node app.js - dans le terminal) -``` - ---- - -## Structure du dépôt - -``` -TP-Git-Collaboratif/ -├── README.md <- Ce fichier -├── config.json <- Configuration partagée Python + Node (contient un bug) -├── email-template.md <- Template pour votre email de rapport -├── CONFLITS.md <- Guide de résolution des conflits Git (étape 7) -├── CORRECTION_FORMATEUR.md <- Réservé au formateur -├── python-api/ -│ ├── app.py <- API Flask (contient des bugs) -│ ├── requirements.txt <- Dépendances Python (contient un bug) -│ └── server.log <- Fichier de logs Azure (ne pas modifier) -└── node-client/ - ├── app.js <- Client Node.js (contient des bugs) - └── package.json <- Dépendances Node (contient un bug) -``` - ---- - -## Prérequis - -Avant de commencer, vérifiez que vous avez installé : - -```bash -python --version # Python 3.8 ou supérieur -node --version # Node.js 18 ou supérieur -npm --version # npm 9 ou supérieur -git --version # Git 2.x -``` - ---- - -## Étapes du TP - -### Étape 1 — Setup Git (15 min) - -**1.1 — Cloner le dépôt** - -```bash -git clone -cd TP-Git-Collaboratif -``` - -**1.2 — Créer votre branche de travail** - -Respectez impérativement cette convention de nommage : - -```bash -git checkout -b fix/prenom-debug-python-node -# Exemple : git checkout -b fix/alderic-debug-python-node -``` - -**1.3 — Vérifier que votre branche est active** - -```bash -git branch -# La branche active est précédée d'une étoile (*) -``` - ---- - -### Étape 2 — Débogage Python (60 min) - -> Consigne : lancez les commandes, lisez attentivement les messages d'erreur, -> corrigez UN bug à la fois, relancez après chaque correction. - -**2.1 — Installer les dépendances Python** - -Placez-vous dans le dossier `python-api/` : - -```bash -cd python-api -pip install -r requirements.txt -``` - -Si une erreur apparaît ici, lisez le message. Ouvrez `requirements.txt` et cherchez ce qui est incorrect. - -**2.2 — Lancer l'API Flask** - -```bash -python app.py -``` - -Chaque message d'erreur vous indique : -- Le **type d'erreur** (SyntaxError, NameError, FileNotFoundError…) -- Le **fichier** concerné -- Le **numéro de ligne** précis - -Notez chaque erreur dans le template email au fur et à mesure. - -**2.3 — Vérifier que l'API répond correctement** - -Une fois tous les bugs Python corrigés, vous devriez voir : - -``` - * Running on http://127.0.0.1:5000 - * Debug mode: on -``` - -Testez l'API avec curl (dans un autre terminal) : - -```bash -curl http://localhost:5000/api/logs -``` - -Résultat attendu — un JSON de cette forme : - -```json -{ - "error_count": 5, - "errors": ["..."], - "info_count": 10, - "warning_count": 4, - "warnings": ["..."] -} -``` - ---- - -### Étape 3 — Débogage Node.js (50 min) - -> Laissez l'API Python en cours d'exécution dans votre premier terminal. -> Ouvrez un second terminal pour cette étape. - -**3.1 — Installer les dépendances Node** - -```bash -cd node-client -npm install -``` - -Si npm affiche une erreur ou un avertissement "404 Not Found", examinez `package.json`. -Le nom du paquet dans `dependencies` est-il correct ? - -**3.2 — Lancer le client Node** - -```bash -node app.js -``` - -Analysez chaque message d'erreur. Node.js vous indique le type d'erreur et la ligne. -Corrigez un bug à la fois, relancez après chaque correction. - -**3.3 — Résultat attendu quand tout fonctionne** - -``` -======================================== - RAPPORT D'ANALYSE DES LOGS AZURE -======================================== - Erreurs detectees : 5 - Avertissements : 4 - Messages info : 10 - ---- Detail des erreurs --- - > 2024-01-15 08:02:45 ERROR Failed to connect to Azure Storage: connection timeout - > 2024-01-15 08:05:33 ERROR Authentication failed for service account: deploy_svc - > 2024-01-15 08:07:42 ERROR Database query timeout after 30s on table: audit_logs - > 2024-01-15 08:09:00 ERROR Max retries exceeded - Azure Storage service unavailable - > 2024-01-15 08:12:45 ERROR Backup failed: insufficient permissions on /var/backup/azure -======================================== -``` - ---- - -### Étape 4 — Test de communication complète (20 min) - -Vérifiez que les deux projets fonctionnent ensemble : - -1. Terminal 1 : `cd python-api && python app.py` -2. Terminal 2 : `cd node-client && node app.js` -3. Le rapport complet doit s'afficher dans le terminal 2 - -Si Node affiche `Erreur de connexion à l'API Python`, vérifiez que : -- L'API Python est bien démarrée (terminal 1) -- Les ports correspondent entre `app.py` et `app.js` - ---- - -### Étape 5 — Rédiger l'email de rapport (20 min) - -Ouvrez le fichier `email-template.md` et complétez chaque section : -- Contexte de l'incident -- Tableau des bugs identifiés (fichier, ligne, type, description) -- Corrections apportées avec justification -- Tests de validation réalisés -- Lien vers votre Pull Request -- Recommandations pour éviter ces bugs à l'avenir - ---- - -### Étape 6 — Pull Request (15 min) - -**6.1 — Commiter vos corrections** - -```bash -cd .. # Revenir à la racine du projet -git add . -git status # Vérifiez les fichiers modifiés -git commit -m "fix: correction des 8 bugs Python et Node - analyse logs Azure" -``` - -**6.2 — Pousser votre branche** - -```bash -git push origin fix/prenom-debug-python-node -``` - -**6.3 — Ouvrir une Pull Request sur GitHub** - -1. Rendez-vous sur le dépôt GitHub -2. Cliquez sur "Compare & pull request" -3. Titre de la PR : `fix: débogage API Python + client Node - ` -4. Description : collez un résumé des bugs corrigés -5. Soumettez la PR - -**6.4 — Réviser la PR d'un·e camarade** - -Trouvez la PR d'un·e autre apprenant·e et ajoutez au minimum un commentaire constructif. - ---- - -### Étape 7 — Gestion des conflits Git (45 min) - -> Cette étape se fait **en groupe**, une fois que tout le monde a ouvert sa PR. -> Le formateur merge la PR d'une première personne sur `main`, puis chacun doit -> mettre sa branche à jour et résoudre le conflit qui en résulte. - -Le guide complet pas à pas est dans le fichier **[CONFLITS.md](./CONFLITS.md)**. - -En résumé, vous allez : -1. Ajouter votre prénom dans le tableau `"apprenants"` de `config.json` -2. Commiter et pousser cette modification -3. Attendre que le formateur merge une première PR sur `main` -4. Récupérer les changements et rebaser votre branche : `git rebase origin/main` -5. Résoudre le conflit dans `config.json` (garder tous les prénoms + le bon port) -6. Continuer le rebase et forcer le push : `git push --force-with-lease` - - ---- - -## Pour aller plus loin — Profils avancés - -> Cette section est destinée aux apprenants qui ont terminé les 6 étapes principales avant la fin du TP. -> Les trois défis suivants sont indépendants — vous pouvez les traiter dans l'ordre de votre choix. -> Ils correspondent directement aux modules Azure qui arrivent dans la suite de la formation. - ---- - -### Défi 1 — Conteneuriser les deux services avec Docker - -**Objectif :** packager l'API Python et le client Node dans des conteneurs Docker, puis les faire communiquer via Docker Compose. C'est la base du déploiement sur Azure Container Apps. - -**Branche à créer :** `feat/prenom-docker` - -**1.1 — Créer le Dockerfile pour l'API Python** - -Créez le fichier `python-api/Dockerfile` : - -```dockerfile -FROM python:3.11-slim - -WORKDIR /app - -COPY requirements.txt . -RUN pip install --no-cache-dir -r requirements.txt - -COPY . . - -EXPOSE 5000 - -CMD ["python", "app.py"] -``` - -**1.2 — Créer le Dockerfile pour le client Node** - -Créez le fichier `node-client/Dockerfile` : - -```dockerfile -FROM node:18-alpine - -WORKDIR /app - -COPY package.json . -RUN npm install - -COPY . . - -CMD ["node", "app.js"] -``` - -**1.3 — Créer le fichier docker-compose.yml** - -Créez `docker-compose.yml` à la racine du projet : - -```yaml -version: '3.8' - -services: - python-api: - build: ./python-api - ports: - - "5000:5000" - container_name: log-analyser-api - - node-client: - build: ./node-client - depends_on: - - python-api - container_name: log-analyser-client -``` - -> Attention : quand Node tourne dans un conteneur, `localhost` ne désigne plus votre machine mais le conteneur lui-même. Il faudra adapter l'URL dans `app.js` pour utiliser le nom du service Docker (`python-api`) à la place de `localhost`. C'est le principe de la communication inter-conteneurs. - -**1.4 — Lancer les deux services** - -```bash -docker-compose up --build -``` - -**Résultat attendu :** le rapport des logs s'affiche dans les logs du conteneur `node-client`. - ---- - -### Défi 2 — Ajouter un pipeline CI avec GitHub Actions - -**Objectif :** créer un workflow GitHub Actions qui vérifie automatiquement que le code s'installe et démarre sans erreur à chaque push ou Pull Request. - -> **CI ou CD ?** Ce pipeline couvre uniquement la partie **CI (Intégration Continue)** : il vérifie que le code est valide. Il tourne entièrement sur les serveurs de GitHub — aucun abonnement Azure n'est nécessaire. La partie **CD (Déploiement Continu)** vers Azure App Service ou Azure Container Apps sera ajoutée dans le module Azure DevOps de la formation, quand vous aurez accès à un abonnement Azure. -> -> Retenez la distinction : **CI = vérifier le code** (GitHub suffit) — **CD = déployer le code** (Azure intervient ici). - -**Branche à créer :** `feat/prenom-github-actions` - -**2.1 — Créer la structure du workflow** - -```bash -mkdir -p .github/workflows -``` - -Créez le fichier `.github/workflows/ci.yml` : - -```yaml -name: CI — Vérification API Python - -on: - push: - branches: [ main, 'fix/**', 'feat/**' ] - pull_request: - branches: [ main ] - -jobs: - test-python-api: - runs-on: ubuntu-latest - - steps: - - name: Récupérer le code - uses: actions/checkout@v4 - - - name: Installer Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: '3.11' - - - name: Installer les dépendances - run: | - cd python-api - pip install -r requirements.txt - - - name: Vérifier que Flask démarre sans erreur - run: | - cd python-api - timeout 5 python app.py || true - echo "Vérification terminée" - - test-node-client: - runs-on: ubuntu-latest - - steps: - - name: Récupérer le code - uses: actions/checkout@v4 - - - name: Installer Node.js 18 - uses: actions/setup-node@v4 - with: - node-version: '18' - - - name: Installer les dépendances npm - run: | - cd node-client - npm install - - - name: Vérifier la syntaxe JavaScript - run: | - cd node-client - node --check app.js - echo "Syntaxe JavaScript valide" -``` - -**2.2 — Pousser et observer** - -```bash -git add .github/ -git commit -m "ci: ajout pipeline GitHub Actions vérification Python et Node" -git push origin feat/prenom-github-actions -``` - -Rendez-vous dans l'onglet **Actions** de votre dépôt GitHub pour observer le pipeline s'exécuter en temps réel. - -**Questions de réflexion :** - -1. Que se passe-t-il si vous pushez volontairement un bug dans `app.py` (par exemple, remettez `flaskk` dans `requirements.txt`) ? Le pipeline détecte-t-il l'erreur ? -2. Comment configurer GitHub pour qu'une PR ne puisse pas être fusionnée si le pipeline échoue ? (Indice : Settings → Branches → Branch protection rules) -3. Quelle étape faudrait-il ajouter à ce pipeline pour déployer automatiquement l'API sur Azure App Service ? Que vous manque-t-il actuellement pour pouvoir le faire ? (Cette question sera répondue dans le module Azure DevOps de la formation.) - ---- - -### Défi 3 — Sécuriser la communication entre les deux services - -**Objectif :** ajouter une couche de sécurité entre Node et Flask — une clé API simple en header HTTP. Sans cette clé, l'API refuse de répondre. C'est l'introduction au principe d'authentification entre microservices, fondamental en DevSecOps. - -**Branche à créer :** `feat/prenom-securite-api` - -**3.1 — Côté Python : vérifier la clé API dans chaque requête** - -Modifiez `python-api/app.py` pour ajouter la vérification : - -```python -from flask import Flask, jsonify, request - -app = Flask(__name__) - -# Clé API attendue — en production, cette valeur viendrait -# d'une variable d'environnement Azure Key Vault, jamais en dur dans le code -API_KEY = "devsecops-simplon-2024" - -def verifier_cle_api(): - cle = request.headers.get("X-API-Key") - if cle != API_KEY: - return jsonify({"erreur": "Clé API invalide ou manquante"}), 401 - return None - -@app.route("/api/logs", methods=["GET"]) -def get_logs(): - erreur = verifier_cle_api() - if erreur: - return erreur - result = parse_logs("server.log") - return jsonify(result), 200 -``` - -**3.2 — Côté Node : envoyer la clé dans les headers** - -Modifiez `node-client/app.js` pour inclure la clé dans chaque requête : - -```javascript -const response = await axios.get(API_URL, { - headers: { - 'X-API-Key': 'devsecops-simplon-2024' - } -}); -``` - -**3.3 — Tester que la sécurité fonctionne** - -Testez d'abord sans la clé : - -```bash -curl http://localhost:5000/api/logs -# Attendu → 401 Unauthorized -``` - -Puis avec la bonne clé : - -```bash -curl http://localhost:5000/api/logs -H "X-API-Key: devsecops-simplon-2024" -# Attendu → 200 OK avec les données JSON -``` - -**3.4 — Réflexion sécurité (à noter dans votre commit)** - -Ajoutez un commentaire dans votre code qui répond à cette question : pourquoi ne faut-il **jamais** stocker une clé API directement dans le code source ? Quelle alternative Azure propose-t-elle pour stocker ces secrets de façon sécurisée ? - -> Indice : cherchez "Azure Key Vault" dans la documentation Azure. - - ---- - -## Ressources utiles - -Toutes les ressources (documentation, vidéos, guides) sont regroupées dans le fichier **[RESSOURCES.md](./RESSOURCES.md)**. +# TP Git Collaboratif — Débogage Full-Stack Python + Node.js + +**Durée estimée :** 3h30 +**Niveau :** Intermédiaire +**Effectif :** 12 apprenants + +> Les ressources et explications complémentaires sont disponibles dans le fichier **[RESSOURCES.md](./RESSOURCES.md)**. + +--- + +## Avant de commencer — Comprendre les outils utilisés + +Si vous êtes en reconversion et que certains termes vous sont nouveaux, pas de panique. Voici l'essentiel à savoir avant de démarrer. + +**Python** est un langage de programmation très utilisé en DevOps pour automatiser des tâches : analyser des fichiers, interagir avec des APIs, lancer des scripts de déploiement. Dans ce TP, un script Python est chargé de lire un fichier de logs et d'en extraire les informations importantes. + +**Flask** est un framework Python qui permet de créer très simplement une API web. Concrètement, Flask transforme votre script Python en un petit serveur web que l'on peut interroger via une URL, comme on interrogerait un site. Ici, Flask va exposer les résultats de l'analyse des logs à l'adresse `http://localhost:5000/api/logs`. + +**Node.js** est un environnement qui permet d'exécuter du JavaScript en dehors d'un navigateur, directement dans le terminal. Il est très utilisé pour créer des outils en ligne de commande, des serveurs web ou, comme dans ce TP, des scripts qui consomment des APIs. + +**npm** (Node Package Manager) est le gestionnaire de paquets de Node.js. Il permet d'installer des bibliothèques tierces listées dans un fichier `package.json`, exactement comme `pip` installe les dépendances Python listées dans `requirements.txt`. + +**Une API REST** est un service web qui reçoit des requêtes HTTP et retourne des données, généralement au format JSON. Dans ce TP, Python joue le rôle du serveur (il fournit les données) et Node.js joue le rôle du client (il demande les données et les affiche). + +**Les logs serveur** sont des fichiers texte que les applications génèrent automatiquement pour enregistrer ce qui se passe : démarrages, erreurs, avertissements, requêtes reçues… En DevOps, analyser ces logs est une tâche quotidienne pour surveiller l'état d'une infrastructure. + +--- + +## Contexte du TP + +Vous êtes développeur·se DevOps chez **NexaCloud**. + +Suite à une mise à jour en production, deux scripts critiques tombent en erreur : + +- `python-api/app.py` — une API Flask qui lit les logs du serveur Azure et retourne une analyse JSON +- `node-client/app.js` — un client Node.js qui interroge cette API et affiche un rapport dans le terminal + +Ces deux scripts **communiquent ensemble** : Node appelle Python via HTTP. +Aucun des deux ne fonctionnera correctement tant que tous les bugs ne sont pas corrigés. + +Votre responsable technique vous demande de : +1. Cloner le dépôt sur votre machine +2. Reproduire les erreurs en exécutant les scripts +3. Identifier et corriger les bugs (8 au total) +4. Pousser vos corrections via une Pull Request +5. Envoyer un email de rapport à votre responsable + +--- + +## Architecture du projet + +``` +[Fichier de logs Azure] + server.log + | + v + python-api/app.py <- API Flask (port 5000) + (lit les logs, compte <- Lance avec : python app.py + les ERROR / WARNING / INFO) + | + | HTTP GET /api/logs + v + node-client/app.js <- Client Node.js + (affiche le rapport <- Lance avec : node app.js + dans le terminal) +``` + +--- + +## Structure du dépôt + +``` +TP-Git-Collaboratif/ +├── README.md <- Ce fichier +├── config.json <- Configuration partagée Python + Node (contient un bug) +├── email-template.md <- Template pour votre email de rapport +├── CONFLITS.md <- Guide de résolution des conflits Git (étape 7) +├── CORRECTION_FORMATEUR.md <- Réservé au formateur +├── python-api/ +│ ├── app.py <- API Flask (contient des bugs) +│ ├── requirements.txt <- Dépendances Python (contient un bug) +│ └── server.log <- Fichier de logs Azure (ne pas modifier) +└── node-client/ + ├── app.js <- Client Node.js (contient des bugs) + └── package.json <- Dépendances Node (contient un bug) +``` + +--- + +## Prérequis + +Avant de commencer, vérifiez que vous avez installé : + +```bash +python --version # Python 3.8 ou supérieur +node --version # Node.js 18 ou supérieur +npm --version # npm 9 ou supérieur +git --version # Git 2.x +``` + +--- + +## Étapes du TP + +### Étape 1 — Setup Git (15 min) + +**1.1 — Cloner le dépôt** + +```bash +git clone +cd TP-Git-Collaboratif +``` + +**1.2 — Créer votre branche de travail** + +Respectez impérativement cette convention de nommage : + +```bash +git checkout -b fix/prenom-debug-python-node +# Exemple : git checkout -b fix/alderic-debug-python-node +``` + +**1.3 — Vérifier que votre branche est active** + +```bash +git branch +# La branche active est précédée d'une étoile (*) +``` + +--- + +### Étape 2 — Débogage Python (60 min) + +> Consigne : lancez les commandes, lisez attentivement les messages d'erreur, +> corrigez UN bug à la fois, relancez après chaque correction. + +**2.1 — Installer les dépendances Python** + +Placez-vous dans le dossier `python-api/` : + +```bash +cd python-api +pip install -r requirements.txt +``` + +Si une erreur apparaît ici, lisez le message. Ouvrez `requirements.txt` et cherchez ce qui est incorrect. + +**2.2 — Lancer l'API Flask** + +```bash +python app.py +``` + +Chaque message d'erreur vous indique : +- Le **type d'erreur** (SyntaxError, NameError, FileNotFoundError…) +- Le **fichier** concerné +- Le **numéro de ligne** précis + +Notez chaque erreur dans le template email au fur et à mesure. + +**2.3 — Vérifier que l'API répond correctement** + +Une fois tous les bugs Python corrigés, vous devriez voir : + +``` + * Running on http://127.0.0.1:5000 + * Debug mode: on +``` + +Testez l'API avec curl (dans un autre terminal) : + +```bash +curl http://localhost:5000/api/logs +``` + +Résultat attendu — un JSON de cette forme : + +```json +{ + "error_count": 5, + "errors": ["..."], + "info_count": 10, + "warning_count": 4, + "warnings": ["..."] +} +``` + +--- + +### Étape 3 — Débogage Node.js (50 min) + +> Laissez l'API Python en cours d'exécution dans votre premier terminal. +> Ouvrez un second terminal pour cette étape. + +**3.1 — Installer les dépendances Node** + +```bash +cd node-client +npm install +``` + +Si npm affiche une erreur ou un avertissement "404 Not Found", examinez `package.json`. +Le nom du paquet dans `dependencies` est-il correct ? + +**3.2 — Lancer le client Node** + +```bash +node app.js +``` + +Analysez chaque message d'erreur. Node.js vous indique le type d'erreur et la ligne. +Corrigez un bug à la fois, relancez après chaque correction. + +**3.3 — Résultat attendu quand tout fonctionne** + +``` +======================================== + RAPPORT D'ANALYSE DES LOGS AZURE +======================================== + Erreurs detectees : 5 + Avertissements : 4 + Messages info : 10 + +--- Detail des erreurs --- + > 2024-01-15 08:02:45 ERROR Failed to connect to Azure Storage: connection timeout + > 2024-01-15 08:05:33 ERROR Authentication failed for service account: deploy_svc + > 2024-01-15 08:07:42 ERROR Database query timeout after 30s on table: audit_logs + > 2024-01-15 08:09:00 ERROR Max retries exceeded - Azure Storage service unavailable + > 2024-01-15 08:12:45 ERROR Backup failed: insufficient permissions on /var/backup/azure +======================================== +``` + +--- + +### Étape 4 — Test de communication complète (20 min) + +Vérifiez que les deux projets fonctionnent ensemble : + +1. Terminal 1 : `cd python-api && python app.py` +2. Terminal 2 : `cd node-client && node app.js` +3. Le rapport complet doit s'afficher dans le terminal 2 + +Si Node affiche `Erreur de connexion à l'API Python`, vérifiez que : +- L'API Python est bien démarrée (terminal 1) +- Les ports correspondent entre `app.py` et `app.js` + +--- + +### Étape 5 — Rédiger l'email de rapport (20 min) + +Ouvrez le fichier `email-template.md` et complétez chaque section : +- Contexte de l'incident +- Tableau des bugs identifiés (fichier, ligne, type, description) +- Corrections apportées avec justification +- Tests de validation réalisés +- Lien vers votre Pull Request +- Recommandations pour éviter ces bugs à l'avenir + +--- + +### Étape 6 — Pull Request (15 min) + +**6.1 — Commiter vos corrections** + +```bash +cd .. # Revenir à la racine du projet +git add . +git status # Vérifiez les fichiers modifiés +git commit -m "fix: correction des 8 bugs Python et Node - analyse logs Azure" +``` + +**6.2 — Pousser votre branche** + +```bash +git push origin fix/prenom-debug-python-node +``` + +**6.3 — Ouvrir une Pull Request sur GitHub** + +1. Rendez-vous sur le dépôt GitHub +2. Cliquez sur "Compare & pull request" +3. Titre de la PR : `fix: débogage API Python + client Node - ` +4. Description : collez un résumé des bugs corrigés +5. Soumettez la PR + +**6.4 — Réviser la PR d'un·e camarade** + +Trouvez la PR d'un·e autre apprenant·e et ajoutez au minimum un commentaire constructif. + +--- + +### Étape 7 — Gestion des conflits Git (45 min) + +> Cette étape se fait **en groupe**, une fois que tout le monde a ouvert sa PR. +> Le formateur merge la PR d'une première personne sur `main`, puis chacun doit +> mettre sa branche à jour et résoudre le conflit qui en résulte. + +Le guide complet pas à pas est dans le fichier **[CONFLITS.md](./CONFLITS.md)**. + +En résumé, vous allez : +1. Ajouter votre prénom dans le tableau `"apprenants"` de `config.json` +2. Commiter et pousser cette modification +3. Attendre que le formateur merge une première PR sur `main` +4. Récupérer les changements et rebaser votre branche : `git rebase origin/main` +5. Résoudre le conflit dans `config.json` (garder tous les prénoms + le bon port) +6. Continuer le rebase et forcer le push : `git push --force-with-lease` + + +--- + +## Pour aller plus loin — Profils avancés + +> Cette section est destinée aux apprenants qui ont terminé les 6 étapes principales avant la fin du TP. +> Les trois défis suivants sont indépendants — vous pouvez les traiter dans l'ordre de votre choix. +> Ils correspondent directement aux modules Azure qui arrivent dans la suite de la formation. + +--- + +### Défi 1 — Conteneuriser les deux services avec Docker + +**Objectif :** packager l'API Python et le client Node dans des conteneurs Docker, puis les faire communiquer via Docker Compose. C'est la base du déploiement sur Azure Container Apps. + +**Branche à créer :** `feat/prenom-docker` + +**1.1 — Créer le Dockerfile pour l'API Python** + +Créez le fichier `python-api/Dockerfile` : + +```dockerfile +FROM python:3.11-slim + +WORKDIR /app + +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +COPY . . + +EXPOSE 5000 + +CMD ["python", "app.py"] +``` + +**1.2 — Créer le Dockerfile pour le client Node** + +Créez le fichier `node-client/Dockerfile` : + +```dockerfile +FROM node:18-alpine + +WORKDIR /app + +COPY package.json . +RUN npm install + +COPY . . + +CMD ["node", "app.js"] +``` + +**1.3 — Créer le fichier docker-compose.yml** + +Créez `docker-compose.yml` à la racine du projet : + +```yaml +version: '3.8' + +services: + python-api: + build: ./python-api + ports: + - "5000:5000" + container_name: log-analyser-api + + node-client: + build: ./node-client + depends_on: + - python-api + container_name: log-analyser-client +``` + +> Attention : quand Node tourne dans un conteneur, `localhost` ne désigne plus votre machine mais le conteneur lui-même. Il faudra adapter l'URL dans `app.js` pour utiliser le nom du service Docker (`python-api`) à la place de `localhost`. C'est le principe de la communication inter-conteneurs. + +**1.4 — Lancer les deux services** + +```bash +docker-compose up --build +``` + +**Résultat attendu :** le rapport des logs s'affiche dans les logs du conteneur `node-client`. + +--- + +### Défi 2 — Ajouter un pipeline CI avec GitHub Actions + +**Objectif :** créer un workflow GitHub Actions qui vérifie automatiquement que le code s'installe et démarre sans erreur à chaque push ou Pull Request. + +> **CI ou CD ?** Ce pipeline couvre uniquement la partie **CI (Intégration Continue)** : il vérifie que le code est valide. Il tourne entièrement sur les serveurs de GitHub — aucun abonnement Azure n'est nécessaire. La partie **CD (Déploiement Continu)** vers Azure App Service ou Azure Container Apps sera ajoutée dans le module Azure DevOps de la formation, quand vous aurez accès à un abonnement Azure. +> +> Retenez la distinction : **CI = vérifier le code** (GitHub suffit) — **CD = déployer le code** (Azure intervient ici). + +**Branche à créer :** `feat/prenom-github-actions` + +**2.1 — Créer la structure du workflow** + +```bash +mkdir -p .github/workflows +``` + +Créez le fichier `.github/workflows/ci.yml` : + +```yaml +name: CI — Vérification API Python + +on: + push: + branches: [ main, 'fix/**', 'feat/**' ] + pull_request: + branches: [ main ] + +jobs: + test-python-api: + runs-on: ubuntu-latest + + steps: + - name: Récupérer le code + uses: actions/checkout@v4 + + - name: Installer Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: '3.11' + + - name: Installer les dépendances + run: | + cd python-api + pip install -r requirements.txt + + - name: Vérifier que Flask démarre sans erreur + run: | + cd python-api + timeout 5 python app.py || true + echo "Vérification terminée" + + test-node-client: + runs-on: ubuntu-latest + + steps: + - name: Récupérer le code + uses: actions/checkout@v4 + + - name: Installer Node.js 18 + uses: actions/setup-node@v4 + with: + node-version: '18' + + - name: Installer les dépendances npm + run: | + cd node-client + npm install + + - name: Vérifier la syntaxe JavaScript + run: | + cd node-client + node --check app.js + echo "Syntaxe JavaScript valide" +``` + +**2.2 — Pousser et observer** + +```bash +git add .github/ +git commit -m "ci: ajout pipeline GitHub Actions vérification Python et Node" +git push origin feat/prenom-github-actions +``` + +Rendez-vous dans l'onglet **Actions** de votre dépôt GitHub pour observer le pipeline s'exécuter en temps réel. + +**Questions de réflexion :** + +1. Que se passe-t-il si vous pushez volontairement un bug dans `app.py` (par exemple, remettez `flaskk` dans `requirements.txt`) ? Le pipeline détecte-t-il l'erreur ? +2. Comment configurer GitHub pour qu'une PR ne puisse pas être fusionnée si le pipeline échoue ? (Indice : Settings → Branches → Branch protection rules) +3. Quelle étape faudrait-il ajouter à ce pipeline pour déployer automatiquement l'API sur Azure App Service ? Que vous manque-t-il actuellement pour pouvoir le faire ? (Cette question sera répondue dans le module Azure DevOps de la formation.) + +--- + +### Défi 3 — Sécuriser la communication entre les deux services + +**Objectif :** ajouter une couche de sécurité entre Node et Flask — une clé API simple en header HTTP. Sans cette clé, l'API refuse de répondre. C'est l'introduction au principe d'authentification entre microservices, fondamental en DevSecOps. + +**Branche à créer :** `feat/prenom-securite-api` + +**3.1 — Côté Python : vérifier la clé API dans chaque requête** + +Modifiez `python-api/app.py` pour ajouter la vérification : + +```python +from flask import Flask, jsonify, request + +app = Flask(__name__) + +# Clé API attendue — en production, cette valeur viendrait +# d'une variable d'environnement Azure Key Vault, jamais en dur dans le code +API_KEY = "devsecops-simplon-2024" + +def verifier_cle_api(): + cle = request.headers.get("X-API-Key") + if cle != API_KEY: + return jsonify({"erreur": "Clé API invalide ou manquante"}), 401 + return None + +@app.route("/api/logs", methods=["GET"]) +def get_logs(): + erreur = verifier_cle_api() + if erreur: + return erreur + result = parse_logs("server.log") + return jsonify(result), 200 +``` + +**3.2 — Côté Node : envoyer la clé dans les headers** + +Modifiez `node-client/app.js` pour inclure la clé dans chaque requête : + +```javascript +const response = await axios.get(API_URL, { + headers: { + 'X-API-Key': 'devsecops-simplon-2024' + } +}); +``` + +**3.3 — Tester que la sécurité fonctionne** + +Testez d'abord sans la clé : + +```bash +curl http://localhost:5000/api/logs +# Attendu → 401 Unauthorized +``` + +Puis avec la bonne clé : + +```bash +curl http://localhost:5000/api/logs -H "X-API-Key: devsecops-simplon-2024" +# Attendu → 200 OK avec les données JSON +``` + +**3.4 — Réflexion sécurité (à noter dans votre commit)** + +Ajoutez un commentaire dans votre code qui répond à cette question : pourquoi ne faut-il **jamais** stocker une clé API directement dans le code source ? Quelle alternative Azure propose-t-elle pour stocker ces secrets de façon sécurisée ? + +> Indice : cherchez "Azure Key Vault" dans la documentation Azure. + + +--- + +## Ressources utiles + +Toutes les ressources (documentation, vidéos, guides) sont regroupées dans le fichier **[RESSOURCES.md](./RESSOURCES.md)**. diff --git a/RESSOURCES.md b/RESSOURCES.md index da55c69..f820eda 100644 --- a/RESSOURCES.md +++ b/RESSOURCES.md @@ -1,156 +1,156 @@ -# Ressources utiles — TP Git Collaboratif - -Ce fichier regroupe toutes les ressources pour vous aider pendant et après le TP. -N'hésitez pas à y revenir à tout moment. - ---- - -## Python - -**Comprendre les messages d'erreur Python** -Quand Python plante, il affiche un "traceback" : lisez-le de bas en haut. La dernière ligne indique le type d'erreur et le message, les lignes au-dessus montrent le chemin qui y a mené. - -- [Types d'erreurs Python — documentation officielle (français)](https://docs.python.org/fr/3/tutorial/errors.html) -- [Comprendre un traceback Python — RealPython (anglais)](https://realpython.com/python-traceback/) - -Les erreurs les plus fréquentes que vous rencontrerez : - -- `SyntaxError` — le code n'est pas valide syntaxiquement (parenthèse manquante, deux-points oublié…) -- `NameError` — vous utilisez une variable ou une fonction qui n'a pas été définie -- `FileNotFoundError` — le fichier demandé n'existe pas à l'emplacement indiqué -- `ModuleNotFoundError` — un paquet importé n'est pas installé - ---- - -## Flask - -Flask est le framework Python utilisé pour créer l'API de ce TP. - -- [Documentation officielle Flask (anglais)](https://flask.palletsprojects.com/en/3.0.x/) -- [Tutoriel Flask pour débutants — OpenClassrooms (français)](https://openclassrooms.com/fr/courses/4425066-concevez-un-site-avec-flask) - -Commandes utiles : - -```bash -# Installer Flask -pip install flask - -# Lancer une application Flask -python app.py - -# Tester une route depuis le terminal -curl http://localhost:5000/api/logs -``` - ---- - -## Node.js et npm - -Node.js permet d'exécuter du JavaScript dans le terminal. npm est son gestionnaire de paquets. - -- [Documentation officielle Node.js (français)](https://nodejs.org/fr/docs) -- [Guide npm pour débutants (français)](https://docs.npmjs.com/about-npm) - -Commandes utiles : - -```bash -# Vérifier la version installée -node --version -npm --version - -# Installer les dépendances d'un projet (lit package.json) -npm install - -# Lancer un script Node -node app.js - -# Lancer via le script "start" défini dans package.json -npm start -``` - -Erreurs fréquentes sous Node.js : - -- `Cannot find module 'xxx'` — le paquet n'est pas installé ou son nom est incorrect dans `require()` -- `TypeError: Cannot read properties of undefined` — vous essayez d'accéder à une propriété d'une variable qui vaut `undefined` -- `ECONNREFUSED` — le client ne peut pas se connecter au serveur (serveur non démarré ou mauvais port) - ---- - -## Codes HTTP - -Quand Node.js appelle l'API Python, la réponse contient toujours un code HTTP (200, 404, 500…). -Le guide complet avec les explications et les cas concrets de ce TP est dans **[CODES-HTTP.md](./CODES-HTTP.md)**. - ---- - -## Axios - -Axios est la bibliothèque Node.js utilisée dans ce TP pour faire des requêtes HTTP vers l'API Python. - -- [Documentation Axios — réponse d'une requête](https://axios-http.com/docs/res_schema) - -Structure d'une réponse Axios : - -```javascript -const response = await axios.get('http://localhost:5000/api/logs'); -// Les données retournées par le serveur sont dans : -response.data // <- c'est ici que se trouve le JSON -response.status // <- code HTTP (200, 404, 500...) -response.headers // <- en-têtes HTTP -``` - ---- - -## Git et GitHub - -**Conventions de nommage des branches** - -Dans ce TP, la convention est : `fix/prenom-description` -Exemples : `fix/marie-debug-python`, `fix/kevin-correction-node` - -**Messages de commit — Conventional Commits** -Un bon message de commit suit ce format : `type: description courte` - -- `fix:` — correction d'un bug -- `feat:` — ajout d'une nouvelle fonctionnalité -- `docs:` — modification de documentation -- `chore:` — tâche de maintenance (mise à jour de dépendances…) - -Exemples : `fix: correction du port Flask dans app.py` ou `fix: remplacement axioss par axios` - -Ressources Git : -- [Conventional Commits (français)](https://www.conventionalcommits.org/fr/v1.0.0/) -- [Créer une Pull Request sur GitHub (français)](https://docs.github.com/fr/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request) -- [Comprendre Git — série complète par Grafikart (vidéo français)](https://www.youtube.com/playlist?list=PLjwdMgw5TTLXuY5i7RW0QqGdW0NZntqiP) - ---- - -## Vidéos recommandées (français) - -Ces vidéos ont été sélectionnées par votre formateur pour accompagner la formation. - -**Git & GitHub — bases** -- [Apprendre GIT en 1 heure (2025)](https://www.youtube.com/watch?v=_WBBiGiCOEA) -- [Formation Git complète — Grafikart](https://www.youtube.com/playlist?list=PLjwdMgw5TTLXuY5i7RW0QqGdW0NZntqiP) -- [Débuter avec Git et GitHub en 30 min](https://www.youtube.com/watch?v=hPfgekYUKgk) - -**Gitflow et DevOps** -- [Devenir DevOps — Git et Gitflow — Xavki](https://www.youtube.com/watch?v=ro3ouEyzFzY) -- [Pipeline DevOps — Gitflow en pratique — Xavki](https://www.youtube.com/watch?v=pXWU0iNubk0) -- [Comprendre Git Flow — Grafikart](https://www.youtube.com/watch?v=ZQAQ4HcskAY) - ---- - -## Glossaire rapide - -| Terme | Définition simple | -|-------|-------------------| -| API | Service web qui reçoit des requêtes et retourne des données (souvent en JSON) | -| JSON | Format de données texte, lisible par les humains et les machines | -| Framework | Boîte à outils qui facilite le développement (Flask, Express…) | -| Dépendance | Bibliothèque externe dont votre projet a besoin pour fonctionner | -| Port | Numéro qui identifie un service sur une machine (ex: port 5000 pour Flask) | -| Localhost | Désigne votre propre machine (équivalent à 127.0.0.1) | -| Log | Fichier texte enregistrant les événements d'une application | -| Pull Request | Proposition de fusion d'une branche vers une autre, avec revue de code | -| Traceback | Message d'erreur Python qui montre la chaîne d'appels ayant mené à l'erreur | +# Ressources utiles — TP Git Collaboratif + +Ce fichier regroupe toutes les ressources pour vous aider pendant et après le TP. +N'hésitez pas à y revenir à tout moment. + +--- + +## Python + +**Comprendre les messages d'erreur Python** +Quand Python plante, il affiche un "traceback" : lisez-le de bas en haut. La dernière ligne indique le type d'erreur et le message, les lignes au-dessus montrent le chemin qui y a mené. + +- [Types d'erreurs Python — documentation officielle (français)](https://docs.python.org/fr/3/tutorial/errors.html) +- [Comprendre un traceback Python — RealPython (anglais)](https://realpython.com/python-traceback/) + +Les erreurs les plus fréquentes que vous rencontrerez : + +- `SyntaxError` — le code n'est pas valide syntaxiquement (parenthèse manquante, deux-points oublié…) +- `NameError` — vous utilisez une variable ou une fonction qui n'a pas été définie +- `FileNotFoundError` — le fichier demandé n'existe pas à l'emplacement indiqué +- `ModuleNotFoundError` — un paquet importé n'est pas installé + +--- + +## Flask + +Flask est le framework Python utilisé pour créer l'API de ce TP. + +- [Documentation officielle Flask (anglais)](https://flask.palletsprojects.com/en/3.0.x/) +- [Tutoriel Flask pour débutants — OpenClassrooms (français)](https://openclassrooms.com/fr/courses/4425066-concevez-un-site-avec-flask) + +Commandes utiles : + +```bash +# Installer Flask +pip install flask + +# Lancer une application Flask +python app.py + +# Tester une route depuis le terminal +curl http://localhost:5000/api/logs +``` + +--- + +## Node.js et npm + +Node.js permet d'exécuter du JavaScript dans le terminal. npm est son gestionnaire de paquets. + +- [Documentation officielle Node.js (français)](https://nodejs.org/fr/docs) +- [Guide npm pour débutants (français)](https://docs.npmjs.com/about-npm) + +Commandes utiles : + +```bash +# Vérifier la version installée +node --version +npm --version + +# Installer les dépendances d'un projet (lit package.json) +npm install + +# Lancer un script Node +node app.js + +# Lancer via le script "start" défini dans package.json +npm start +``` + +Erreurs fréquentes sous Node.js : + +- `Cannot find module 'xxx'` — le paquet n'est pas installé ou son nom est incorrect dans `require()` +- `TypeError: Cannot read properties of undefined` — vous essayez d'accéder à une propriété d'une variable qui vaut `undefined` +- `ECONNREFUSED` — le client ne peut pas se connecter au serveur (serveur non démarré ou mauvais port) + +--- + +## Codes HTTP + +Quand Node.js appelle l'API Python, la réponse contient toujours un code HTTP (200, 404, 500…). +Le guide complet avec les explications et les cas concrets de ce TP est dans **[CODES-HTTP.md](./CODES-HTTP.md)**. + +--- + +## Axios + +Axios est la bibliothèque Node.js utilisée dans ce TP pour faire des requêtes HTTP vers l'API Python. + +- [Documentation Axios — réponse d'une requête](https://axios-http.com/docs/res_schema) + +Structure d'une réponse Axios : + +```javascript +const response = await axios.get('http://localhost:5000/api/logs'); +// Les données retournées par le serveur sont dans : +response.data // <- c'est ici que se trouve le JSON +response.status // <- code HTTP (200, 404, 500...) +response.headers // <- en-têtes HTTP +``` + +--- + +## Git et GitHub + +**Conventions de nommage des branches** + +Dans ce TP, la convention est : `fix/prenom-description` +Exemples : `fix/marie-debug-python`, `fix/kevin-correction-node` + +**Messages de commit — Conventional Commits** +Un bon message de commit suit ce format : `type: description courte` + +- `fix:` — correction d'un bug +- `feat:` — ajout d'une nouvelle fonctionnalité +- `docs:` — modification de documentation +- `chore:` — tâche de maintenance (mise à jour de dépendances…) + +Exemples : `fix: correction du port Flask dans app.py` ou `fix: remplacement axioss par axios` + +Ressources Git : +- [Conventional Commits (français)](https://www.conventionalcommits.org/fr/v1.0.0/) +- [Créer une Pull Request sur GitHub (français)](https://docs.github.com/fr/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request) +- [Comprendre Git — série complète par Grafikart (vidéo français)](https://www.youtube.com/playlist?list=PLjwdMgw5TTLXuY5i7RW0QqGdW0NZntqiP) + +--- + +## Vidéos recommandées (français) + +Ces vidéos ont été sélectionnées par votre formateur pour accompagner la formation. + +**Git & GitHub — bases** +- [Apprendre GIT en 1 heure (2025)](https://www.youtube.com/watch?v=_WBBiGiCOEA) +- [Formation Git complète — Grafikart](https://www.youtube.com/playlist?list=PLjwdMgw5TTLXuY5i7RW0QqGdW0NZntqiP) +- [Débuter avec Git et GitHub en 30 min](https://www.youtube.com/watch?v=hPfgekYUKgk) + +**Gitflow et DevOps** +- [Devenir DevOps — Git et Gitflow — Xavki](https://www.youtube.com/watch?v=ro3ouEyzFzY) +- [Pipeline DevOps — Gitflow en pratique — Xavki](https://www.youtube.com/watch?v=pXWU0iNubk0) +- [Comprendre Git Flow — Grafikart](https://www.youtube.com/watch?v=ZQAQ4HcskAY) + +--- + +## Glossaire rapide + +| Terme | Définition simple | +|-------|-------------------| +| API | Service web qui reçoit des requêtes et retourne des données (souvent en JSON) | +| JSON | Format de données texte, lisible par les humains et les machines | +| Framework | Boîte à outils qui facilite le développement (Flask, Express…) | +| Dépendance | Bibliothèque externe dont votre projet a besoin pour fonctionner | +| Port | Numéro qui identifie un service sur une machine (ex: port 5000 pour Flask) | +| Localhost | Désigne votre propre machine (équivalent à 127.0.0.1) | +| Log | Fichier texte enregistrant les événements d'une application | +| Pull Request | Proposition de fusion d'une branche vers une autre, avec revue de code | +| Traceback | Message d'erreur Python qui montre la chaîne d'appels ayant mené à l'erreur | diff --git a/config.json b/config.json index 46dd74d..c62f739 100644 --- a/config.json +++ b/config.json @@ -1,11 +1,11 @@ -{ - "projet": "TP-Git-Collaboratif", - "promotion": "DevSecOps Azure — Simplon", - "apprenants": [], - "api": { - "port": 5000, - "host": "localhost", - "route": "/api/logs", - "log_file": "server.log" - } -} +{ + "projet": "TP-Git-Collaboratif", + "promotion": "DevSecOps Azure — Simplon", + "apprenants": [], + "api": { + "port": 5000, + "host": "localhost", + "route": "/api/logs", + "log_file": "server.log" + } +} diff --git a/node-client/app.js b/node-client/app.js index 38af06e..47cf0a2 100644 --- a/node-client/app.js +++ b/node-client/app.js @@ -1,35 +1,35 @@ -// Client Node.js — Interroge l'API Python et affiche un rapport des logs Azure -// ----------------------------------------------------------------- - -const path = require('path'); - -// Configuration partagée chargée depuis config.json (à la racine du projet) -const config = require(path.join(__dirname, '..', 'config.json')); -const API_URL = `http://${config.api.host}:${config.api.port}${config.api.route}`; - -const axios = require('axios'); - -async function getLogs() { - try { - const response = await axios.get(API_URL); - - const data = response.data; - - console.log('\n========================================'); - console.log(' RAPPORT D\'ANALYSE DES LOGS AZURE '); - console.log('========================================'); - console.log(` Erreurs detectees : ${data.error_count}`); - console.log(` Avertissements : ${data.warning_count}`); - console.log(` Messages info : ${data.info_count}`); - console.log('\n--- Detail des erreurs ---'); - data.errors.forEach(err => console.log(` > ${err}`)); - console.log('\n--- Detail des avertissements ---'); - data.warnings.forEach(warn => console.log(` > ${warn}`)); - console.log('========================================\n'); - - } catch (error) { - console.error('Erreur de connexion a l\'API Python :', error.message); - } -} - -getLogs(); +// Client Node.js — Interroge l'API Python et affiche un rapport des logs Azure +// ----------------------------------------------------------------- + +const path = require('path'); + +// Configuration partagée chargée depuis config.json (à la racine du projet) +const config = require(path.join(__dirname, '..', 'config.json')); +const API_URL = `http://${config.api.host}:${config.api.port}${config.api.route}`; + +const axios = require('axios'); + +async function getLogs() { + try { + const response = await axios.get(API_URL); + + const data = response.data; + + console.log('\n========================================'); + console.log(' RAPPORT D\'ANALYSE DES LOGS AZURE '); + console.log('========================================'); + console.log(` Erreurs detectees : ${data.error_count}`); + console.log(` Avertissements : ${data.warning_count}`); + console.log(` Messages info : ${data.info_count}`); + console.log('\n--- Detail des erreurs ---'); + data.errors.forEach(err => console.log(` > ${err}`)); + console.log('\n--- Detail des avertissements ---'); + data.warnings.forEach(warn => console.log(` > ${warn}`)); + console.log('========================================\n'); + + } catch (error) { + console.error('Erreur de connexion a l\'API Python :', error.message); + } +} + +getLogs(); diff --git a/node-client/package-lock.json b/node-client/package-lock.json index cc86a3c..6fc99fe 100644 --- a/node-client/package-lock.json +++ b/node-client/package-lock.json @@ -1,346 +1,346 @@ -{ - "name": "log-analyser-client", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "log-analyser-client", - "version": "1.0.0", - "dependencies": { - "axios": "^1.6.0" - } - }, - "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "license": "MIT", - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "license": "MIT" - }, - "node_modules/axios": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.16.1.tgz", - "integrity": "sha512-caYkukvroVPO8KrzuJEb50Hm07KwfBZPEC3VeFHTsqWHvKTsy54hjJz9BS/cdaypROE2rH6xvm9mHX4fgWkr3A==", - "license": "MIT", - "dependencies": { - "follow-redirects": "^1.16.0", - "form-data": "^4.0.5", - "https-proxy-agent": "^5.0.1", - "proxy-from-env": "^2.1.0" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/follow-redirects": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", - "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "license": "MIT", - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/form-data": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", - "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "es-set-tostringtag": "^2.1.0", - "hasown": "^2.0.2", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "license": "MIT", - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", - "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "license": "MIT", - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/proxy-from-env": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", - "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", - "license": "MIT", - "engines": { - "node": ">=10" - } - } - } -} +{ + "name": "log-analyser-client", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "log-analyser-client", + "version": "1.0.0", + "dependencies": { + "axios": "^1.6.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/axios": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.16.1.tgz", + "integrity": "sha512-caYkukvroVPO8KrzuJEb50Hm07KwfBZPEC3VeFHTsqWHvKTsy54hjJz9BS/cdaypROE2rH6xvm9mHX4fgWkr3A==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.16.0", + "form-data": "^4.0.5", + "https-proxy-agent": "^5.0.1", + "proxy-from-env": "^2.1.0" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/follow-redirects": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", + "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", + "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/proxy-from-env": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", + "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + } + } +} diff --git a/node-client/package.json b/node-client/package.json index 77cf210..84280d9 100644 --- a/node-client/package.json +++ b/node-client/package.json @@ -1,12 +1,12 @@ -{ - "name": "log-analyser-client", - "version": "1.0.0", - "description": "Client Node.js qui interroge l'API Python d'analyse de logs Azure", - "main": "app.js", - "scripts": { - "start": "node app.js" - }, - "dependencies": { - "axios": "^1.6.0" - } -} +{ + "name": "log-analyser-client", + "version": "1.0.0", + "description": "Client Node.js qui interroge l'API Python d'analyse de logs Azure", + "main": "app.js", + "scripts": { + "start": "node app.js" + }, + "dependencies": { + "axios": "^1.6.0" + } +} diff --git a/python-api/app.py b/python-api/app.py index 7f34a1e..bc7c4b5 100644 --- a/python-api/app.py +++ b/python-api/app.py @@ -1,50 +1,50 @@ -from flask import Flask, jsonify -import json -import os - -app = Flask(__name__) - -# Chargement de la configuration partagée (config.json à la racine du projet) -config_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'config.json') -with open(config_path, 'r') as f: - config = json.load(f) - -# ------------------------------------------------------- -# Analyse un fichier de logs serveur et retourne -# le nombre d'errors, warnings et infos détectés. -# ------------------------------------------------------- - -def parse_logs(filepath): - errors = [] - warnings = [] - infos = [] - - with open(filepath, "r") as f: - for line in f: - line = line.strip() - if not line: - continue - if "ERROR" in line: - errors.append(line) - elif "WARNING" in line: - warnings.append(line) - elif "INFO" in line: - infos.append(line) - - return { - "error_count": len(errors), - "warning_count": len(warnings), - "info_count": len(infos), - "errors": errors, - "warnings": warnings - } - - -@app.route("/api/logs", methods=["GET"]) -def get_logs(): - result = parse_logs(config["api"]["log_file"]) - return jsonify(result), 200 - - -if __name__ == "__main__": - app.run(debug=True, port=config["api"]["port"]) +from flask import Flask, jsonify +import json +import os + +app = Flask(__name__) + +# Chargement de la configuration partagée (config.json à la racine du projet) +config_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'config.json') +with open(config_path, 'r') as f: + config = json.load(f) + +# ------------------------------------------------------- +# Analyse un fichier de logs serveur et retourne +# le nombre d'errors, warnings et infos détectés. +# ------------------------------------------------------- + +def parse_logs(filepath): + errors = [] + warnings = [] + infos = [] + + with open(filepath, "r") as f: + for line in f: + line = line.strip() + if not line: + continue + if "ERROR" in line: + errors.append(line) + elif "WARNING" in line: + warnings.append(line) + elif "INFO" in line: + infos.append(line) + + return { + "error_count": len(errors), + "warning_count": len(warnings), + "info_count": len(infos), + "errors": errors, + "warnings": warnings + } + + +@app.route("/api/logs", methods=["GET"]) +def get_logs(): + result = parse_logs(config["api"]["log_file"]) + return jsonify(result), 200 + + +if __name__ == "__main__": + app.run(debug=True, port=config["api"]["port"]) diff --git a/python-api/requirements.txt b/python-api/requirements.txt index 7b8e9a8..5bd19d3 100644 --- a/python-api/requirements.txt +++ b/python-api/requirements.txt @@ -1 +1 @@ -flask==3.0.0 +flask==3.0.0 diff --git a/python-api/server.log b/python-api/server.log index 3d4fb2e..5eac220 100644 --- a/python-api/server.log +++ b/python-api/server.log @@ -1,19 +1,19 @@ -2024-01-15 08:00:01 INFO Application started on port 8080 -2024-01-15 08:00:05 INFO Connected to Azure SQL Database successfully -2024-01-15 08:01:22 WARNING High memory usage detected: 78% -2024-01-15 08:02:45 ERROR Failed to connect to Azure Storage: connection timeout -2024-01-15 08:03:10 INFO Request processed: GET /api/health [200] -2024-01-15 08:05:33 ERROR Authentication failed for service account: deploy_svc -2024-01-15 08:06:15 WARNING CPU usage spike detected: 92% -2024-01-15 08:07:42 ERROR Database query timeout after 30s on table: audit_logs -2024-01-15 08:08:00 INFO Retry attempt 1/3 for Azure Storage connection -2024-01-15 08:08:30 INFO Retry attempt 2/3 for Azure Storage connection -2024-01-15 08:09:00 ERROR Max retries exceeded - Azure Storage service unavailable -2024-01-15 08:10:15 WARNING Disk space below threshold: 15% remaining on /dev/sda1 -2024-01-15 08:11:22 INFO Backup job started: daily-backup-2024-01-15 -2024-01-15 08:12:45 ERROR Backup failed: insufficient permissions on /var/backup/azure -2024-01-15 08:14:03 INFO Alert sent to monitoring team via Azure Monitor -2024-01-15 08:15:00 INFO Scheduled maintenance check completed -2024-01-15 08:16:30 WARNING SSL certificate expires in 14 days for api.azuretech.fr -2024-01-15 08:18:55 INFO Kubernetes pod restarted: api-deployment-7d9f8b-xkp2m -2024-01-15 08:20:00 INFO Health check passed: all 3 replicas running +2024-01-15 08:00:01 INFO Application started on port 8080 +2024-01-15 08:00:05 INFO Connected to Azure SQL Database successfully +2024-01-15 08:01:22 WARNING High memory usage detected: 78% +2024-01-15 08:02:45 ERROR Failed to connect to Azure Storage: connection timeout +2024-01-15 08:03:10 INFO Request processed: GET /api/health [200] +2024-01-15 08:05:33 ERROR Authentication failed for service account: deploy_svc +2024-01-15 08:06:15 WARNING CPU usage spike detected: 92% +2024-01-15 08:07:42 ERROR Database query timeout after 30s on table: audit_logs +2024-01-15 08:08:00 INFO Retry attempt 1/3 for Azure Storage connection +2024-01-15 08:08:30 INFO Retry attempt 2/3 for Azure Storage connection +2024-01-15 08:09:00 ERROR Max retries exceeded - Azure Storage service unavailable +2024-01-15 08:10:15 WARNING Disk space below threshold: 15% remaining on /dev/sda1 +2024-01-15 08:11:22 INFO Backup job started: daily-backup-2024-01-15 +2024-01-15 08:12:45 ERROR Backup failed: insufficient permissions on /var/backup/azure +2024-01-15 08:14:03 INFO Alert sent to monitoring team via Azure Monitor +2024-01-15 08:15:00 INFO Scheduled maintenance check completed +2024-01-15 08:16:30 WARNING SSL certificate expires in 14 days for api.azuretech.fr +2024-01-15 08:18:55 INFO Kubernetes pod restarted: api-deployment-7d9f8b-xkp2m +2024-01-15 08:20:00 INFO Health check passed: all 3 replicas running