From 9e4d95917cd8b6df8dc6e80841edf53a0f01ca66 Mon Sep 17 00:00:00 2001 From: Nathan Tesseyre Date: Thu, 21 May 2026 09:35:00 +0200 Subject: [PATCH 01/20] 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 962f538ccea8ac0a33bf1268e3e3362063fafd2e Mon Sep 17 00:00:00 2001 From: Nathan Tesseyre Date: Thu, 21 May 2026 09:41:57 +0200 Subject: [PATCH 02/20] 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 a9ef9217a0b8e259ca8d3fe46405b526636c99a8 Mon Sep 17 00:00:00 2001 From: Nathan Tesseyre Date: Thu, 21 May 2026 09:43:49 +0200 Subject: [PATCH 03/20] 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 a344db5eb8264ad724e3f99fd4f66b02eced05f8 Mon Sep 17 00:00:00 2001 From: Nathan Tesseyre Date: Thu, 21 May 2026 09:45:45 +0200 Subject: [PATCH 04/20] 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 002e7dc87d9c6bafe440e100949b9b9eff911394 Mon Sep 17 00:00:00 2001 From: Nathan Tesseyre Date: Thu, 21 May 2026 09:48:04 +0200 Subject: [PATCH 05/20] 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 e5f14a7aef2a43342610ce3bd98dc33feb03641e Mon Sep 17 00:00:00 2001 From: Nathan Tesseyre Date: Thu, 21 May 2026 09:48:47 +0200 Subject: [PATCH 06/20] 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 f587d9ba28bf03157641f8bf6a5c03f9e3f91e84 Mon Sep 17 00:00:00 2001 From: Nathan Tesseyre Date: Thu, 21 May 2026 09:53:12 +0200 Subject: [PATCH 07/20] 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 1ad2514cab123950c46b702b58a81ef890ef2fc3 Mon Sep 17 00:00:00 2001 From: Nathan Tesseyre Date: Thu, 21 May 2026 09:53:47 +0200 Subject: [PATCH 08/20] 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 eca1a7c13cd7f6a463fedfc8af37fa0f2d78f1bf Mon Sep 17 00:00:00 2001 From: Nathan Tesseyre Date: Thu, 21 May 2026 09:57:50 +0200 Subject: [PATCH 09/20] 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 96dc6c2516eea824fc49a6cd5787ce0d071a3c8c Mon Sep 17 00:00:00 2001 From: Nathan Tesseyre Date: Thu, 21 May 2026 09:59:33 +0200 Subject: [PATCH 10/20] 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 4d1fa7f3a52a24e7e0627f535c095ac905b3195e Mon Sep 17 00:00:00 2001 From: Nathan Tesseyre Date: Thu, 21 May 2026 10:01:49 +0200 Subject: [PATCH 11/20] 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 fa4791f377350e5a29251aefa7ba5fe6a3d5f62b Mon Sep 17 00:00:00 2001 From: Nathan Tesseyre Date: Thu, 21 May 2026 10:05:51 +0200 Subject: [PATCH 12/20] 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 4fcdc3fd9885f0d8b962daf4bc5b2c5a41b1c67f Mon Sep 17 00:00:00 2001 From: Nathan Tesseyre Date: Thu, 21 May 2026 10:08:55 +0200 Subject: [PATCH 13/20] 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 c8a69eb1a5f6b99f055b6e74f4503f3885961924 Mon Sep 17 00:00:00 2001 From: Nathan Tesseyre Date: Thu, 21 May 2026 10:11:26 +0200 Subject: [PATCH 14/20] 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 a4b9a2d4b1213838ef2257f893e18b071a5292bf Mon Sep 17 00:00:00 2001 From: Nathan Tesseyre Date: Thu, 21 May 2026 10:14:10 +0200 Subject: [PATCH 15/20] 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 d866a691003136032d53672cd85a69577947d128 Mon Sep 17 00:00:00 2001 From: Nathan Tesseyre Date: Thu, 21 May 2026 11:24:43 +0200 Subject: [PATCH 16/20] feat: add api key on each axios request on node --- node-client/app.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/node-client/app.js b/node-client/app.js index 38af06e..84ff4a9 100644 --- a/node-client/app.js +++ b/node-client/app.js @@ -11,7 +11,11 @@ const axios = require('axios'); async function getLogs() { try { - const response = await axios.get(API_URL); + const response = await axios.get(API_URL, { + headers: { + 'X-API-Key': 'devsecops-simplon-2024' + } + }); const data = response.data; From 73cdf18f2afc127737dae3862b2f995c0eac4a66 Mon Sep 17 00:00:00 2001 From: Nathan Tesseyre Date: Thu, 21 May 2026 11:31:28 +0200 Subject: [PATCH 17/20] feat: add API key authentication to python-api --- python-api/app.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/python-api/app.py b/python-api/app.py index 9ed4cdf..37c3f43 100644 --- a/python-api/app.py +++ b/python-api/app.py @@ -39,9 +39,21 @@ def parse_logs(filepath): "warnings": warnings } +# 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 From 8442c74301061d2f0ffd6a4d53f486c8e4c59ca1 Mon Sep 17 00:00:00 2001 From: Nathan Tesseyre Date: Thu, 21 May 2026 11:35:36 +0200 Subject: [PATCH 18/20] fix: add request import on 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 37c3f43..556c0fa 100644 --- a/python-api/app.py +++ b/python-api/app.py @@ -1,4 +1,4 @@ -from flask import Flask, jsonify +from flask import Flask, jsonify, request import json import os From 81f37a643a4241f5f1d29d676bfff7449f03b8eb Mon Sep 17 00:00:00 2001 From: Nathan Tesseyre Date: Thu, 21 May 2026 11:46:20 +0200 Subject: [PATCH 19/20] refactor: centralize API key in config.json --- config.json | 3 ++- node-client/app.js | 2 +- python-api/app.py | 6 +----- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/config.json b/config.json index 46dd74d..d7acd7c 100644 --- a/config.json +++ b/config.json @@ -6,6 +6,7 @@ "port": 5000, "host": "localhost", "route": "/api/logs", - "log_file": "server.log" + "log_file": "server.log", + "key": "devsecops-simplon-2024" } } diff --git a/node-client/app.js b/node-client/app.js index 84ff4a9..a5078fe 100644 --- a/node-client/app.js +++ b/node-client/app.js @@ -13,7 +13,7 @@ async function getLogs() { try { const response = await axios.get(API_URL, { headers: { - 'X-API-Key': 'devsecops-simplon-2024' + 'X-API-Key': `${config.api.key}` } }); diff --git a/python-api/app.py b/python-api/app.py index 556c0fa..fd758e9 100644 --- a/python-api/app.py +++ b/python-api/app.py @@ -39,13 +39,9 @@ def parse_logs(filepath): "warnings": warnings } -# 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: + if cle != config["api"]["key"]: return jsonify({"erreur": "Clé API invalide ou manquante"}), 401 return None From 94862d757e5d378b859496372ff4c5afcb7e7eb1 Mon Sep 17 00:00:00 2001 From: Nathan Tesseyre Date: Thu, 21 May 2026 12:23:42 +0200 Subject: [PATCH 20/20] chore: use config.json to load log_file in app.py instead of hardcoded value --- .github/workflows/ci.yml | 36 +- .gitignore | 8 +- CODES-HTTP.md | 378 +++++------ CONFLITS.md | 548 ++++++++-------- README.md | 1120 ++++++++++++++++----------------- RESSOURCES.md | 312 ++++----- config.json | 24 +- node-client/app.js | 78 +-- node-client/package-lock.json | 692 ++++++++++---------- node-client/package.json | 24 +- python-api/app.py | 116 ++-- python-api/requirements.txt | 2 +- python-api/server.log | 38 +- 13 files changed, 1691 insertions(+), 1685 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 61a1d71..8127191 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 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 d7acd7c..877306c 100644 --- a/config.json +++ b/config.json @@ -1,12 +1,12 @@ -{ - "projet": "TP-Git-Collaboratif", - "promotion": "DevSecOps Azure — Simplon", - "apprenants": [], - "api": { - "port": 5000, - "host": "localhost", - "route": "/api/logs", - "log_file": "server.log", - "key": "devsecops-simplon-2024" - } -} +{ + "projet": "TP-Git-Collaboratif", + "promotion": "DevSecOps Azure — Simplon", + "apprenants": [], + "api": { + "port": 5000, + "host": "localhost", + "route": "/api/logs", + "log_file": "server.log", + "key": "devsecops-simplon-2024" + } +} diff --git a/node-client/app.js b/node-client/app.js index a5078fe..2f27e96 100644 --- a/node-client/app.js +++ b/node-client/app.js @@ -1,39 +1,39 @@ -// 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, { - headers: { - 'X-API-Key': `${config.api.key}` - } - }); - - 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, { + headers: { + 'X-API-Key': `${config.api.key}` + } + }); + + 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 fd758e9..52a519c 100644 --- a/python-api/app.py +++ b/python-api/app.py @@ -1,58 +1,58 @@ -from flask import Flask, jsonify, request -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 - } - -def verifier_cle_api(): - cle = request.headers.get("X-API-Key") - if cle != config["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 - - -if __name__ == "__main__": - app.run(debug=True, port=config["api"]["port"]) +from flask import Flask, jsonify, request +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 + } + +def verifier_cle_api(): + cle = request.headers.get("X-API-Key") + if cle != config["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(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