diff --git a/VM/vm_stress_test.sh b/VM/vm_stress_test.sh index b217d61..a4b7a2b 100755 --- a/VM/vm_stress_test.sh +++ b/VM/vm_stress_test.sh @@ -201,6 +201,20 @@ vm_check_binary() { vm_ssh "$ip" "command -v $bin" &>/dev/null } +# vm_wait_binary: ждать пока бинарник появится на VM (sless_job работает асинхронно). +# Нужен потому что sless_job запускает kubernetes Job, который сначала собирает образ, +# затем стартует pod, и только потом SSH-устанавливает пакеты на VM — это занимает 1-3 мин. +vm_wait_binary() { + local ip="$1" bin="$2" timeout_sec="${3:-180}" + local deadline=$((SECONDS + timeout_sec)) + info "жду появления '$bin' на VM (до ${timeout_sec}s)..." + while [[ $SECONDS -lt $deadline ]]; do + if vm_check_binary "$ip" "$bin"; then return 0; fi + sleep 15 + done + return 1 +} + # vm_purge_all: удалить все установленные пакеты с VM. vm_purge_all() { local ip="$1" @@ -277,25 +291,30 @@ phase_1_baseline() { } # ══════════════════════════════════════════════════════════════════════════════ -# ФАЗА 2: IDEMPOTENT — повторный plan без изменений -# Передаём тот же run_id → ничего не изменилось → "No changes". +# ФАЗА 2: IDEMPOTENT — повторный apply с теми же аргументами → 0 changed +# Используем apply (не plan) — это надёжнее: некоторые sless-провайдеры показывают +# ложный drift в plan, но apply при этом возвращает 0 changed. Apply — canonical way. # ══════════════════════════════════════════════════════════════════════════════ phase_2_idempotent() { - phase_header 2 "IDEMPOTENT — повторный plan без изменений" + phase_header 2 "IDEMPOTENT — повторный apply без изменений" - # Тот же run_id что в предыдущей фазе → "No changes" - if tf_plan_no_changes \ + # Тот же run_id что применялся в фазе 1 → apply должен вернуть 0 changed + if tf_apply \ -var "install_packages=true" \ -var "install_nginx=true" \ -var "install_docker=true" \ -var "install_run_id=$RUN_ID"; then - pass "2.1 terraform plan → No changes" + if grep -q '0 added, 0 changed, 0 destroyed' /tmp/vm_tf_apply.log; then + pass "2.1 повторный apply → 0 added, 0 changed, 0 destroyed" + else + fail "2.1 повторный apply изменил ресурсы (ожидали 0 changed)" + grep -E 'added|changed|destroyed' /tmp/vm_tf_apply.log | tail -3 | while read -r line; do + info " $line" + done + fi else - fail "2.1 terraform plan показывает изменения (ожидали No changes)" - grep -E 'will be|must be' /tmp/vm_tf_plan.log 2>/dev/null | head -5 | while read -r line; do - info " $line" - done + fail "2.1 повторный apply упал" fi phase_result "IDEMPOTENT" "PASS" @@ -498,19 +517,18 @@ phase_6_manual_purge() { return 1 fi - # Подождать и проверить что пакеты вернулись - sleep 5 - - if vm_check_binary "$ip" "docker"; then + # Ждать пока sless_job отработает: docker самый долгий (k8s Job + apt-install). + # vm_wait_binary полит каждые 15s до 180s вместо sleep 5. + if vm_wait_binary "$ip" "docker" 180; then pass "6.5 docker установлен заново" else - fail "6.5 docker НЕ установлен после re-apply" + fail "6.5 docker НЕ установлен после re-apply (таймаут 180s)" fi - if vm_check_binary "$ip" "nginx"; then + if vm_wait_binary "$ip" "nginx" 120; then pass "6.6 nginx установлен заново" else - fail "6.6 nginx НЕ установлен после re-apply" + fail "6.6 nginx НЕ установлен после re-apply (таймаут 120s)" fi if vm_check_binary "$ip" "jq"; then @@ -702,15 +720,22 @@ phase_10_final() { -var "install_run_id=$rid" || warn "восстановление не удалось" fi - # Plan = no changes (с тем же run_id что и последний apply) - if tf_plan_no_changes \ + # Idempotency финально: повторный apply → 0 changed + if tf_apply \ -var "install_packages=true" \ -var "install_nginx=true" \ -var "install_docker=true" \ -var "install_run_id=$RUN_ID"; then - pass "10.2 terraform plan → No changes" + if grep -q '0 added, 0 changed, 0 destroyed' /tmp/vm_tf_apply.log; then + pass "10.2 финальный apply → 0 added, 0 changed, 0 destroyed" + else + fail "10.2 финальный apply изменил ресурсы (ожидали 0 changed)" + grep -E 'added|changed|destroyed' /tmp/vm_tf_apply.log | tail -3 | while read -r line; do + info " $line" + done + fi else - fail "10.2 terraform plan показывает изменения" + fail "10.2 финальный apply упал" fi # VM доступна @@ -739,6 +764,54 @@ phase_10_final() { else fail "10.6 docker НЕ найден" fi + + # nginx service активен (не просто бинарник, а именно демон) + if vm_ssh "$ip" "systemctl is-active nginx 2>/dev/null" 2>/dev/null | grep -q '^active$'; then + pass "10.7 nginx service активен (systemctl)" + else + fail "10.7 nginx service не активен" + fi + + # docker daemon активен + if vm_ssh "$ip" "systemctl is-active docker 2>/dev/null" 2>/dev/null | grep -q '^active$'; then + pass "10.8 docker daemon активен (systemctl)" + else + fail "10.8 docker daemon не активен" + fi + + # HTTP probe: nginx отвечает на localhost:80 + local http_code + http_code=$(vm_ssh "$ip" "curl -sS -o /dev/null -w '%{http_code}' http://localhost 2>/dev/null" 2>/dev/null) + if [[ "$http_code" == "200" ]]; then + pass "10.9 nginx отвечает HTTP 200" + else + fail "10.9 nginx не отвечает HTTP 200 (code='$http_code')" + fi + + # python3 доступен + local py_ver + py_ver=$(vm_ssh "$ip" "python3 --version 2>&1" 2>/dev/null) + if echo "$py_ver" | grep -q 'Python 3'; then + pass "10.10 python3 доступен ($py_ver)" + else + fail "10.10 python3 недоступен" + fi + + # docker smoke: запустить контейнер и проверить вывод + if vm_ssh "$ip" "docker run --rm hello-world 2>&1 | grep -q 'Hello from Docker'" 2>/dev/null; then + pass "10.11 docker run hello-world → успешно" + else + fail "10.11 docker run hello-world → не прошёл" + fi + + # disk space: убедиться что на VM есть место (>500MB free) + local free_mb + free_mb=$(vm_ssh "$ip" "df -m / 2>/dev/null | awk 'NR==2{print \$4}'" 2>/dev/null) + if [[ -n "$free_mb" && "$free_mb" -gt 500 ]]; then + pass "10.12 свободное место на / = ${free_mb}MB (>500MB)" + else + fail "10.12 мало места на / = ${free_mb}MB (ожидали >500MB)" + fi else fail "10.3 VM не доступна по SSH" fi