fix: same - idempotent apply, vm_wait_binary, extra sanity checks

This commit is contained in:
Naeel 2026-03-30 08:00:18 +03:00
parent bad38aa62a
commit dc71622fb8

View File

@ -201,6 +201,20 @@ vm_check_binary() {
vm_ssh "$ip" "command -v $bin" &>/dev/null 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: удалить все установленные пакеты с VM.
vm_purge_all() { vm_purge_all() {
local ip="$1" local ip="$1"
@ -277,26 +291,31 @@ phase_1_baseline() {
} }
# ══════════════════════════════════════════════════════════════════════════════ # ══════════════════════════════════════════════════════════════════════════════
# ФАЗА 2: IDEMPOTENT — повторный plan без изменений # ФАЗА 2: IDEMPOTENT — повторный apply с теми же аргументами → 0 changed
# Передаём тот же run_id → ничего не изменилось → "No changes". # Используем apply (не plan) — это надёжнее: некоторые sless-провайдеры показывают
# ложный drift в plan, но apply при этом возвращает 0 changed. Apply — canonical way.
# ══════════════════════════════════════════════════════════════════════════════ # ══════════════════════════════════════════════════════════════════════════════
phase_2_idempotent() { phase_2_idempotent() {
phase_header 2 "IDEMPOTENT — повторный plan без изменений" phase_header 2 "IDEMPOTENT — повторный apply без изменений"
# Тот же run_id что в предыдущей фазе → "No changes" # Тот же run_id что применялся в фазе 1 → apply должен вернуть 0 changed
if tf_plan_no_changes \ if tf_apply \
-var "install_packages=true" \ -var "install_packages=true" \
-var "install_nginx=true" \ -var "install_nginx=true" \
-var "install_docker=true" \ -var "install_docker=true" \
-var "install_run_id=$RUN_ID"; then -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 else
fail "2.1 terraform plan показывает изменения (ожидали No changes)" fail "2.1 повторный apply изменил ресурсы (ожидали 0 changed)"
grep -E 'will be|must be' /tmp/vm_tf_plan.log 2>/dev/null | head -5 | while read -r line; do grep -E 'added|changed|destroyed' /tmp/vm_tf_apply.log | tail -3 | while read -r line; do
info " $line" info " $line"
done done
fi fi
else
fail "2.1 повторный apply упал"
fi
phase_result "IDEMPOTENT" "PASS" phase_result "IDEMPOTENT" "PASS"
} }
@ -498,19 +517,18 @@ phase_6_manual_purge() {
return 1 return 1
fi fi
# Подождать и проверить что пакеты вернулись # Ждать пока sless_job отработает: docker самый долгий (k8s Job + apt-install).
sleep 5 # vm_wait_binary полит каждые 15s до 180s вместо sleep 5.
if vm_wait_binary "$ip" "docker" 180; then
if vm_check_binary "$ip" "docker"; then
pass "6.5 docker установлен заново" pass "6.5 docker установлен заново"
else else
fail "6.5 docker НЕ установлен после re-apply" fail "6.5 docker НЕ установлен после re-apply (таймаут 180s)"
fi fi
if vm_check_binary "$ip" "nginx"; then if vm_wait_binary "$ip" "nginx" 120; then
pass "6.6 nginx установлен заново" pass "6.6 nginx установлен заново"
else else
fail "6.6 nginx НЕ установлен после re-apply" fail "6.6 nginx НЕ установлен после re-apply (таймаут 120s)"
fi fi
if vm_check_binary "$ip" "jq"; then if vm_check_binary "$ip" "jq"; then
@ -702,15 +720,22 @@ phase_10_final() {
-var "install_run_id=$rid" || warn "восстановление не удалось" -var "install_run_id=$rid" || warn "восстановление не удалось"
fi fi
# Plan = no changes (с тем же run_id что и последний apply) # Idempotency финально: повторный apply → 0 changed
if tf_plan_no_changes \ if tf_apply \
-var "install_packages=true" \ -var "install_packages=true" \
-var "install_nginx=true" \ -var "install_nginx=true" \
-var "install_docker=true" \ -var "install_docker=true" \
-var "install_run_id=$RUN_ID"; then -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 else
fail "10.2 terraform plan показывает изменения" 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 финальный apply упал"
fi fi
# VM доступна # VM доступна
@ -739,6 +764,54 @@ phase_10_final() {
else else
fail "10.6 docker НЕ найден" fail "10.6 docker НЕ найден"
fi 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 else
fail "10.3 VM не доступна по SSH" fail "10.3 VM не доступна по SSH"
fi fi