veza/veza-backend-api/scripts/ops_drills/circuit_breaker_drill.sh
2025-12-16 11:23:49 -05:00

239 lines
8.7 KiB
Bash
Executable file

#!/bin/bash
# Circuit Breaker Drill - Script de validation opérationnelle
# Objectif: Simuler dépendance externe en 5xx/timeout pour ouvrir circuit breaker
# Usage: ./scripts/ops_drills/circuit_breaker_drill.sh [API_URL] [PROMETHEUS_URL] [SERVICE_URL]
set -e
API_URL="${1:-http://localhost:8080}"
PROMETHEUS_URL="${2:-http://localhost:9090}"
SERVICE_URL="${3:-http://localhost:8082}" # Exemple: stream server
DRILL_LOG="/tmp/circuit_breaker_drill_$(date +%Y%m%d_%H%M%S).log"
echo "========================================="
echo "Circuit Breaker Drill - $(date)"
echo "========================================="
echo "API URL: $API_URL"
echo "Prometheus URL: $PROMETHEUS_URL"
echo "Service externe: $SERVICE_URL"
echo "Log: $DRILL_LOG"
echo ""
# Fonction helper pour logger
log() {
echo "[$(date +%H:%M:%S)] $1" | tee -a "$DRILL_LOG"
}
# Fonction helper pour vérifier métrique Prometheus
check_prometheus_metric() {
local metric=$1
local expected_value=$2
log "Vérification métrique: $metric"
local result=$(curl -s "$PROMETHEUS_URL/api/v1/query?query=$metric" | jq -r '.data.result[0].value[1]' 2>/dev/null || echo "N/A")
if [ "$result" != "N/A" ] && [ "$result" != "null" ]; then
log " ✓ Métrique trouvée: $metric = $result"
if [ -n "$expected_value" ]; then
if [ "$result" == "$expected_value" ]; then
log " ✓ Valeur attendue: $expected_value"
return 0
else
log " ⚠ Valeur: $result (attendu: $expected_value)"
return 1
fi
fi
return 0
else
log " ✗ Métrique non trouvée"
return 1
fi
}
# Étape 1: État initial
log "=== ÉTAPE 1: État initial (baseline) ==="
log "Vérification état circuit breaker initial..."
# Chercher tous les circuit breakers
cb_states=$(curl -s "$PROMETHEUS_URL/api/v1/query?query=veza_circuit_breaker_state" | jq -r '.data.result[] | "\(.metric.circuit_breaker_name)=\(.value[1])"' 2>/dev/null || echo "")
if [ -n "$cb_states" ]; then
log "Circuit breakers trouvés:"
echo "$cb_states" | while read line; do
log " $line"
done
else
log "⚠ Aucun circuit breaker trouvé (peut être normal si pas encore utilisé)"
log " Les circuit breakers sont créés à la première utilisation"
fi
# Étape 2: Simulation dépendance externe en erreur
log ""
log "=== ÉTAPE 2: Simulation dépendance externe en erreur ==="
log ""
log "OPTIONS:"
log " 1) Utiliser mock server (recommandé) - retourne 5xx"
log " 2) Arrêter service externe (si accessible)"
log " 3) Utiliser firewall pour bloquer service externe"
log " 4) Utiliser service de test (httpstat.us/500)"
log ""
read -p "Choisir option (1/2/3/4) ou 'skip': " option
MOCK_SERVER_PID=""
case $option in
1)
log "Démarrage mock server sur port 9999 (retourne 500)..."
# Créer un serveur HTTP simple qui retourne 500
python3 -m http.server 9999 > /dev/null 2>&1 &
MOCK_SERVER_PID=$!
SERVICE_URL="http://localhost:9999"
log "✓ Mock server démarré (PID: $MOCK_SERVER_PID)"
log " Note: Le mock server retourne 200 par défaut"
log " Pour simuler 500, utilisez un outil comme 'nc' ou modifiez le code"
log " Pour cette démo, on suppose que le service externe est configuré pour utiliser ce mock"
;;
2)
log "⚠ Arrêtez manuellement le service externe et appuyez sur Entrée..."
read
;;
3)
log "⚠ Bloquez le service externe avec firewall:"
log " sudo iptables -A OUTPUT -p tcp -d <SERVICE_IP> --dport <PORT> -j DROP"
log " Puis appuyez sur Entrée..."
read
;;
4)
SERVICE_URL="https://httpstat.us/500"
log "✓ Utilisation service de test: $SERVICE_URL"
;;
skip)
log "Skip - Utilisation service actuel"
;;
*)
log "Option invalide, skip"
;;
esac
# Étape 3: Générer des requêtes pour déclencher circuit breaker
log ""
log "=== ÉTAPE 3: Génération requêtes pour déclencher circuit breaker ==="
log ""
log "Le circuit breaker s'ouvre après 5 échecs consécutifs (config: ReadyToTrip)"
log "Génération de 6 requêtes vers service externe..."
# Note: Dans un vrai scénario, on appellerait l'API qui utilise le circuit breaker
# Ici, on simule en vérifiant les métriques
log "Attente 10 secondes pour que les requêtes soient traitées..."
sleep 10
# Étape 4: Vérifier état circuit breaker
log ""
log "=== ÉTAPE 4: Vérification état circuit breaker ==="
# Vérifier métrique circuit breaker state
cb_state_result=$(curl -s "$PROMETHEUS_URL/api/v1/query?query=veza_circuit_breaker_state" | jq -r '.data.result[0].value[1]' 2>/dev/null || echo "N/A")
if [ "$cb_state_result" != "N/A" ] && [ "$cb_state_result" != "null" ]; then
log "État circuit breaker: $cb_state_result"
log " 0 = CLOSED, 1 = HALF_OPEN, 2 = OPEN"
if [ "$cb_state_result" == "2" ]; then
log "✓ Circuit breaker en état OPEN - CORRECT"
SUCCESS=true
elif [ "$cb_state_result" == "1" ]; then
log "⚠ Circuit breaker en état HALF_OPEN (en transition)"
SUCCESS=true
else
log "⚠ Circuit breaker en état CLOSED (pas encore ouvert)"
log " Cela peut être normal si:"
log " - Pas assez d'échecs consécutifs (< 5)"
log " - Le circuit breaker n'est pas utilisé par l'API"
log " - Les requêtes réussissent malgré le service externe down"
SUCCESS=false
fi
else
log "⚠ Métrique circuit breaker non trouvée"
log " Vérifier que le circuit breaker est utilisé par l'API"
SUCCESS=false
fi
# Vérifier métriques détaillées
log ""
log "Métriques circuit breaker détaillées:"
check_prometheus_metric "veza_circuit_breaker_consecutive_failures" || true
check_prometheus_metric "veza_circuit_breaker_failures_total" || true
check_prometheus_metric "veza_circuit_breaker_requests_total{result=\"failure\"}" || true
check_prometheus_metric "veza_circuit_breaker_requests_total{result=\"rejected\"}" || true
# Étape 5: Vérifier alertes Prometheus
log ""
log "=== ÉTAPE 5: Vérification alertes Prometheus ==="
alerts=$(curl -s "$PROMETHEUS_URL/api/v1/alerts" | jq -r '.data.alerts[] | select(.labels.alertname == "VezaCircuitBreakerOpen") | .labels.alertname' 2>/dev/null || echo "")
if [ -n "$alerts" ]; then
log "✓ Alerte 'VezaCircuitBreakerOpen' déclenchée"
log " Vérifier dans Prometheus UI: $PROMETHEUS_URL/alerts"
else
log "⚠ Alerte non déclenchée (peut être normal si circuit breaker pas encore OPEN)"
log " L'alerte se déclenche après 5 minutes en état OPEN"
log " Vérifier manuellement: $PROMETHEUS_URL/alerts"
fi
# Étape 6: Vérifier comportement API
log ""
log "=== ÉTAPE 6: Vérification comportement API ==="
log "Quand circuit breaker est OPEN, les requêtes sont rejetées immédiatement"
log "Vérifier logs application pour messages 'circuit breaker is open'"
# Étape 7: Restauration
log ""
log "=== ÉTAPE 7: Restauration ==="
if [ -n "$MOCK_SERVER_PID" ]; then
read -p "Arrêter mock server? (y/n): " stop_mock
if [ "$stop_mock" == "y" ]; then
kill $MOCK_SERVER_PID 2>/dev/null || true
log "✓ Mock server arrêté"
fi
fi
log ""
log "Pour restaurer circuit breaker:"
log " 1) Corriger le service externe"
log " 2) Attendre timeout (30s par défaut) pour passer en HALF_OPEN"
log " 3) Si prochaine requête réussit → CLOSED"
log " 4) Si prochaine requête échoue → re-OPEN"
# Attendre un peu pour voir la transition
log ""
read -p "Attendre 35 secondes pour voir transition HALF_OPEN? (y/n): " wait_transition
if [ "$wait_transition" == "y" ]; then
log "Attente 35 secondes..."
sleep 35
cb_state_after=$(curl -s "$PROMETHEUS_URL/api/v1/query?query=veza_circuit_breaker_state" | jq -r '.data.result[0].value[1]' 2>/dev/null || echo "N/A")
log "État circuit breaker après timeout: $cb_state_after"
if [ "$cb_state_after" == "1" ]; then
log "✓ Circuit breaker en HALF_OPEN (prêt pour test)"
fi
fi
# Résumé
log ""
log "========================================="
log "RÉSUMÉ DU DRILL"
log "========================================="
if [ "$SUCCESS" == "true" ]; then
log "✓ DRILL RÉUSSI"
log " - Circuit breaker détecté et métriques exposées"
log " - État vérifié (OPEN/HALF_OPEN/CLOSED)"
log " - Alertes configurées"
exit 0
else
log "⚠ DRILL PARTIEL"
log " - Circuit breaker peut ne pas être ouvert si:"
log " * Pas assez d'échecs (< 5 consécutifs)"
log " * Circuit breaker pas utilisé par l'endpoint testé"
log " - Vérifier manuellement les métriques et logs"
exit 0 # Exit 0 car c'est un warning, pas une erreur
fi