239 lines
8.7 KiB
Bash
Executable file
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
|