- Conversion config hardcodée en variables (CLUSTER_NODES) - Détection nœud distant data-driven (extensible à 3+ nœuds) - Tests unitaires BATS avec mocks complets (32 tests) - CI/CD Forgejo avec shellcheck et validation syntaxe - Mode test pour sourcing sans exécution du main
300 lines
9.1 KiB
Bash
300 lines
9.1 KiB
Bash
#!/usr/bin/env bats
|
|
#
|
|
# Tests unitaires pour les fonctions de health check
|
|
# Test des vérifications de santé des disques et pools ZFS
|
|
#
|
|
|
|
load test_helper
|
|
|
|
# Charger uniquement les fonctions du script (pas le code principal)
|
|
setup() {
|
|
# Setup environnement
|
|
setup_script_env
|
|
|
|
# Sourcer le script en mode test (le BATS_TEST_MODE évite l'exécution du main)
|
|
export BATS_TEST_MODE=true
|
|
source "${BATS_TEST_DIRNAME}/../zfs-nfs-replica.sh"
|
|
}
|
|
|
|
teardown() {
|
|
cleanup_script_env
|
|
}
|
|
|
|
# ============================================================================
|
|
# Tests: get_pool_disk_uuids()
|
|
# ============================================================================
|
|
|
|
@test "get_pool_disk_uuids: retourne des UUIDs pour un pool sain" {
|
|
run get_pool_disk_uuids "zpool1"
|
|
|
|
[ "$status" -eq 0 ]
|
|
[[ "$output" =~ "wwn-0x5000cca2dfe2e414" ]]
|
|
}
|
|
|
|
@test "get_pool_disk_uuids: retourne vide pour pool inexistant" {
|
|
# Mock zpool pour retourner une erreur
|
|
zpool() {
|
|
if [[ "$1" == "status" ]]; then
|
|
echo "cannot open 'fakerpool': no such pool" >&2
|
|
return 1
|
|
fi
|
|
}
|
|
export -f zpool
|
|
|
|
run get_pool_disk_uuids "fakerpool"
|
|
|
|
# La fonction doit gérer l'erreur gracieusement
|
|
[ "$status" -ne 0 ] || [ -z "$output" ]
|
|
}
|
|
|
|
# ============================================================================
|
|
# Tests: init_disk_tracking()
|
|
# ============================================================================
|
|
|
|
@test "init_disk_tracking: crée le fichier d'état avec UUIDs" {
|
|
run init_disk_tracking "zpool1"
|
|
|
|
[ "$status" -eq 0 ]
|
|
[ -f "${STATE_DIR}/disk-uuids-zpool1.txt" ]
|
|
|
|
# Vérifier le contenu
|
|
grep -q "initialized=true" "${STATE_DIR}/disk-uuids-zpool1.txt"
|
|
grep -q "pool=zpool1" "${STATE_DIR}/disk-uuids-zpool1.txt"
|
|
grep -q "wwn-0x" "${STATE_DIR}/disk-uuids-zpool1.txt"
|
|
}
|
|
|
|
@test "init_disk_tracking: ne réinitialise pas si déjà initialisé" {
|
|
# Créer un fichier déjà initialisé
|
|
create_disk_uuid_file "zpool1"
|
|
|
|
# Modifier le timestamp pour vérifier qu'il ne change pas
|
|
original_content=$(cat "${STATE_DIR}/disk-uuids-zpool1.txt")
|
|
|
|
run init_disk_tracking "zpool1"
|
|
|
|
[ "$status" -eq 0 ]
|
|
|
|
# Le fichier ne doit pas avoir changé
|
|
new_content=$(cat "${STATE_DIR}/disk-uuids-zpool1.txt")
|
|
[ "$original_content" == "$new_content" ]
|
|
}
|
|
|
|
# ============================================================================
|
|
# Tests: verify_disk_presence()
|
|
# ============================================================================
|
|
|
|
@test "verify_disk_presence: succès si tous les disques présents" {
|
|
create_disk_uuid_file "zpool1" "wwn-0x5000cca2dfe2e414"
|
|
export TEST_DISK_PRESENT=true
|
|
|
|
run verify_disk_presence "zpool1"
|
|
|
|
[ "$status" -eq 0 ]
|
|
}
|
|
|
|
@test "verify_disk_presence: échec si disque manquant" {
|
|
# Créer un fichier avec UUID fictif
|
|
create_disk_uuid_file "zpool1" "wwn-0xFAKE_MISSING_DISK"
|
|
export TEST_DISK_PRESENT=false
|
|
|
|
run verify_disk_presence "zpool1"
|
|
|
|
[ "$status" -eq 1 ]
|
|
[[ "$output" =~ "manquant" ]] || [[ "$output" =~ "MISSING" ]]
|
|
}
|
|
|
|
@test "verify_disk_presence: retourne erreur si fichier d'état absent" {
|
|
# Pas de fichier disk-uuids
|
|
rm -f "${STATE_DIR}/disk-uuids-zpool1.txt"
|
|
|
|
run verify_disk_presence "zpool1"
|
|
|
|
[ "$status" -eq 1 ]
|
|
}
|
|
|
|
# ============================================================================
|
|
# Tests: check_pool_health_status()
|
|
# ============================================================================
|
|
|
|
@test "check_pool_health_status: succès pour pool ONLINE avec espace libre" {
|
|
export TEST_POOL_STATE="ONLINE"
|
|
export TEST_POOL_CAPACITY=67
|
|
|
|
run check_pool_health_status "zpool1"
|
|
|
|
[ "$status" -eq 0 ]
|
|
}
|
|
|
|
@test "check_pool_health_status: échec pour pool DEGRADED" {
|
|
export TEST_POOL_STATE="DEGRADED"
|
|
export TEST_POOL_CAPACITY=67
|
|
|
|
run check_pool_health_status "zpool1"
|
|
|
|
[ "$status" -eq 1 ]
|
|
}
|
|
|
|
@test "check_pool_health_status: échec si espace disque critique (>95%)" {
|
|
export TEST_POOL_STATE="ONLINE"
|
|
export TEST_POOL_CAPACITY=96
|
|
|
|
run check_pool_health_status "zpool1"
|
|
|
|
[ "$status" -eq 1 ]
|
|
[[ "$output" =~ "espace libre" ]] || [[ "$output" =~ "capacity" ]]
|
|
}
|
|
|
|
@test "check_pool_health_status: succès avec exactement 95% (limite)" {
|
|
export TEST_POOL_STATE="ONLINE"
|
|
export TEST_POOL_CAPACITY=95
|
|
|
|
run check_pool_health_status "zpool1"
|
|
|
|
# 95% = 5% libre, c'est la limite, doit passer
|
|
[ "$status" -eq 0 ]
|
|
}
|
|
|
|
# ============================================================================
|
|
# Tests: triple_health_check()
|
|
# ============================================================================
|
|
|
|
@test "triple_health_check: succès si 3/3 tentatives réussissent" {
|
|
create_disk_uuid_file "zpool1"
|
|
export TEST_POOL_STATE="ONLINE"
|
|
export TEST_POOL_CAPACITY=67
|
|
export TEST_DISK_PRESENT=true
|
|
export CHECK_DELAY=0 # Pas de délai dans tests
|
|
|
|
run triple_health_check "zpool1"
|
|
|
|
[ "$status" -eq 0 ]
|
|
}
|
|
|
|
@test "triple_health_check: échec si les 3 tentatives échouent" {
|
|
create_disk_uuid_file "zpool1" "wwn-0xFAKE_MISSING"
|
|
export TEST_DISK_PRESENT=false
|
|
export CHECK_DELAY=0
|
|
|
|
run triple_health_check "zpool1"
|
|
|
|
[ "$status" -eq 1 ]
|
|
}
|
|
|
|
@test "triple_health_check: fait vraiment 3 tentatives (pas d'early return)" {
|
|
create_disk_uuid_file "zpool1"
|
|
export TEST_POOL_STATE="DEGRADED"
|
|
export TEST_DISK_PRESENT=true
|
|
export CHECK_DELAY=0
|
|
|
|
run triple_health_check "zpool1"
|
|
|
|
[ "$status" -eq 1 ]
|
|
|
|
# Vérifier qu'il y a bien 3 lignes d'erreur (3 tentatives)
|
|
attempt_count=$(echo "$output" | grep -c "Vérification santé #" || echo "0")
|
|
[ "$attempt_count" -eq 3 ]
|
|
}
|
|
|
|
# ============================================================================
|
|
# Tests: check_recent_critical_error()
|
|
# ============================================================================
|
|
|
|
@test "check_recent_critical_error: retourne 0 si erreur récente (<1h)" {
|
|
# Erreur il y a 30 minutes (1800 secondes)
|
|
local current_epoch=1735481400
|
|
local error_epoch=$((current_epoch - 1800))
|
|
|
|
export TEST_CURRENT_EPOCH=$current_epoch
|
|
create_critical_error_file "zpool1" "$error_epoch"
|
|
|
|
run check_recent_critical_error "zpool1"
|
|
|
|
[ "$status" -eq 0 ]
|
|
}
|
|
|
|
@test "check_recent_critical_error: retourne 1 si erreur ancienne (>1h)" {
|
|
# Erreur il y a 2 heures (7200 secondes)
|
|
local current_epoch=1735481400
|
|
local error_epoch=$((current_epoch - 7200))
|
|
|
|
export TEST_CURRENT_EPOCH=$current_epoch
|
|
create_critical_error_file "zpool1" "$error_epoch"
|
|
|
|
run check_recent_critical_error "zpool1"
|
|
|
|
[ "$status" -eq 1 ]
|
|
}
|
|
|
|
@test "check_recent_critical_error: retourne 1 si pas de fichier d'erreur" {
|
|
rm -f "${STATE_DIR}/critical-errors-zpool1.txt"
|
|
|
|
run check_recent_critical_error "zpool1"
|
|
|
|
[ "$status" -eq 1 ]
|
|
}
|
|
|
|
# ============================================================================
|
|
# Tests: record_critical_error()
|
|
# ============================================================================
|
|
|
|
@test "record_critical_error: crée fichier avec toutes les infos" {
|
|
run record_critical_error "zpool1" "Test failure reason" "lxc_migrated"
|
|
|
|
[ "$status" -eq 0 ]
|
|
[ -f "${STATE_DIR}/critical-errors-zpool1.txt" ]
|
|
|
|
grep -q "reason=Test failure reason" "${STATE_DIR}/critical-errors-zpool1.txt"
|
|
grep -q "action=lxc_migrated" "${STATE_DIR}/critical-errors-zpool1.txt"
|
|
grep -q "epoch=" "${STATE_DIR}/critical-errors-zpool1.txt"
|
|
}
|
|
|
|
@test "record_critical_error: écrase le fichier précédent" {
|
|
# Créer une première erreur
|
|
create_critical_error_file "zpool1" "1735400000"
|
|
|
|
# Enregistrer une nouvelle erreur
|
|
run record_critical_error "zpool1" "New error" "lxc_stopped"
|
|
|
|
[ "$status" -eq 0 ]
|
|
|
|
# Vérifier que c'est la nouvelle erreur
|
|
grep -q "reason=New error" "${STATE_DIR}/critical-errors-zpool1.txt"
|
|
grep -q "action=lxc_stopped" "${STATE_DIR}/critical-errors-zpool1.txt"
|
|
}
|
|
|
|
# ============================================================================
|
|
# Tests: handle_health_failure()
|
|
# ============================================================================
|
|
|
|
@test "handle_health_failure: migre le LXC si première erreur" {
|
|
# Pas d'erreur récente
|
|
rm -f "${STATE_DIR}/critical-errors-zpool1.txt"
|
|
|
|
export REMOTE_NODE_NAME="acemagician"
|
|
|
|
run handle_health_failure "zpool1" "Disk failure"
|
|
|
|
[ "$status" -eq 0 ]
|
|
[[ "$output" =~ "MIGRATION" ]] || [[ "$output" =~ "migrate" ]]
|
|
|
|
# Vérifier que l'erreur a été enregistrée
|
|
[ -f "${STATE_DIR}/critical-errors-zpool1.txt" ]
|
|
grep -q "action=lxc_migrated" "${STATE_DIR}/critical-errors-zpool1.txt"
|
|
}
|
|
|
|
@test "handle_health_failure: arrête le LXC si erreur récente (<1h)" {
|
|
# Erreur récente (30 min)
|
|
local current_epoch=1735481400
|
|
local error_epoch=$((current_epoch - 1800))
|
|
|
|
export TEST_CURRENT_EPOCH=$current_epoch
|
|
create_critical_error_file "zpool1" "$error_epoch"
|
|
|
|
run handle_health_failure "zpool1" "Another disk failure"
|
|
|
|
[ "$status" -eq 0 ]
|
|
[[ "$output" =~ "ARRÊT" ]] || [[ "$output" =~ "stop" ]] || [[ "$output" =~ "ping-pong" ]]
|
|
|
|
# Vérifier que l'erreur a été mise à jour
|
|
grep -q "action=lxc_stopped" "${STATE_DIR}/critical-errors-zpool1.txt"
|
|
}
|