Files
sless/run_e2e_tests.sh
T

257 lines
10 KiB
Bash
Executable File

#!/usr/bin/env bash
# 2026-03-11
# run_e2e_tests.sh — E2E тест примеров: apply → modify → apply → destroy
#
# Запускает два примера: hello-node (nodejs20) и simple-python (python3.11)
# Каждый проходит полный цикл: init → apply → modify → apply → destroy
# В конце кластер должен быть полностью чистым.
#
# Использование:
# ./run_e2e_tests.sh
# ./run_e2e_tests.sh hello-node # только один пример
#
# Требования: terraform, curl в PATH
set -uo pipefail
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
EXAMPLES_DIR="$REPO_ROOT/examples"
LOGS_DIR="$REPO_ROOT/.e2e-logs"
mkdir -p "$LOGS_DIR"
# ── Примеры для запуска ──────────────────────────────────────────────────────
if [ $# -gt 0 ]; then
EXAMPLES=("$@")
else
EXAMPLES=("hello-node" "simple-python")
fi
# ── Цвета ────────────────────────────────────────────────────────────────────
GREEN='\033[0;32m'; RED='\033[0;31m'; YELLOW='\033[1;33m'; RESET='\033[0m'
ok() { echo -e "${GREEN}$*${RESET}"; }
fail() { echo -e "${RED}$*${RESET}"; }
info() { echo -e "${YELLOW}$*${RESET}"; }
# ── Результаты ───────────────────────────────────────────────────────────────
declare -A RESULTS=()
# ── Cleanup-trap: уничтожить всё что могло остаться ──────────────────────────
cleanup() {
for example in "${EXAMPLES[@]}"; do
local dir="$EXAMPLES_DIR/$example"
if [ -f "$dir/terraform.tfstate" ] && \
grep -q '"resources"' "$dir/terraform.tfstate" 2>/dev/null && \
python3 -c "import json,sys; d=json.load(open('$dir/terraform.tfstate')); sys.exit(0 if d.get('resources') else 1)" 2>/dev/null; then
info "cleanup trap: destroying $example"
(cd "$dir" && terraform destroy -auto-approve -input=false -no-color \
>> "$LOGS_DIR/${example}-destroy-emergency.log" 2>&1) || true
fi
restore_backup "$example"
done
}
trap cleanup EXIT
# ── Retry wrapper: 3 попытки, ретрай при TLS/EOF ошибках ─────────────────────
# Использование: tf_retry <logfile> <terraform args...>
tf_retry() {
local logfile="$1"; shift
local attempt=1
while [ "$attempt" -le 3 ]; do
if "$@" 2>&1 | tee "$logfile"; then
return 0
fi
if grep -Eiq \
'TLS handshake timeout|unexpected EOF|i/o timeout|context deadline' \
"$logfile" && [ "$attempt" -lt 3 ]; then
info "network error, retry $((attempt+1))/3..."
attempt=$((attempt + 1))
sleep 3
continue
fi
return 1
done
return 1
}
# ── Modify: сохранить бэкап и внести изменение ───────────────────────────────
# Изменения минимальны и легко проверяемы в plan output
backup_and_modify() {
local example="$1"
case "$example" in
hello-node)
cp "$EXAMPLES_DIR/$example/http.tf" \
"$EXAMPLES_DIR/$example/http.tf.e2e.bak"
# memory_mb 128 → 256, добавить env_vars
perl -0pi -e \
's/(memory_mb\s+=\s+)128/${1}256/' \
"$EXAMPLES_DIR/$example/http.tf"
perl -0pi -e \
's/(source_dir\s+=.*\n)/$1\n env_vars = \{ GREETING = "e2e-test" \}\n/' \
"$EXAMPLES_DIR/$example/http.tf"
;;
simple-python)
cp "$EXAMPLES_DIR/$example/time-display.tf" \
"$EXAMPLES_DIR/$example/time-display.tf.e2e.bak"
# memory_mb 64 → 96
perl -0pi -e \
's/(memory_mb\s+=\s+)64/${1}96/' \
"$EXAMPLES_DIR/$example/time-display.tf"
;;
simple-node)
cp "$EXAMPLES_DIR/$example/time-display.tf" \
"$EXAMPLES_DIR/$example/time-display.tf.e2e.bak"
perl -0pi -e \
's/(memory_mb\s+=\s+)64/${1}96/' \
"$EXAMPLES_DIR/$example/time-display.tf"
;;
esac
}
restore_backup() {
local example="$1"
for bak in "$EXAMPLES_DIR/$example"/*.e2e.bak; do
[ -f "$bak" ] || continue
mv "$bak" "${bak%.e2e.bak}"
done
}
# ── Шаг apply: проверяем что terraform вернул 0 и есть "Apply complete" ───────
assert_apply_ok() {
local logfile="$1"
if ! grep -q 'Apply complete' "$logfile"; then
return 1
fi
# Выводим краткий итог
grep -E 'Apply complete|added|changed|destroyed|Outputs:' "$logfile" | head -5
return 0
}
assert_destroy_ok() {
local logfile="$1"
grep -q 'Destroy complete' "$logfile"
}
# ── Запуск одного примера ─────────────────────────────────────────────────────
run_example() {
local example="$1"
local dir="$EXAMPLES_DIR/$example"
local log_base="$LOGS_DIR/$example"
local failed=0
echo
echo "════════════════════════════════════════"
echo " EXAMPLE: $example"
echo "════════════════════════════════════════"
# Чистим локальные артефакты от предыдущих запусков
rm -rf "$dir/.terraform" "$dir/.terraform.lock.hcl" \
"$dir/terraform.tfstate" "$dir/terraform.tfstate.backup" \
"$dir"/terraform.tfstate.*.backup
# 1. init ───────────────────────────────────────────────────────────────────
info "init"
if tf_retry "${log_base}-init.log" \
terraform -chdir="$dir" init -input=false -no-color; then
ok "init"
else
fail "init — см. ${log_base}-init.log"
RESULTS[$example]="FAIL (init)"
return 1
fi
# 2. apply ──────────────────────────────────────────────────────────────────
info "apply"
if tf_retry "${log_base}-apply.log" \
terraform -chdir="$dir" apply -auto-approve -input=false -no-color \
&& assert_apply_ok "${log_base}-apply.log"; then
ok "apply"
else
fail "apply — см. ${log_base}-apply.log"
RESULTS[$example]="FAIL (apply)"
return 1
fi
# 3. modify ─────────────────────────────────────────────────────────────────
info "modify (backup + patch)"
backup_and_modify "$example"
ok "files patched"
# 4. apply after modify ─────────────────────────────────────────────────────
info "apply (после modify)"
if tf_retry "${log_base}-apply-modify.log" \
terraform -chdir="$dir" apply -auto-approve -input=false -no-color \
&& assert_apply_ok "${log_base}-apply-modify.log"; then
ok "apply (modify)"
# Показываем что изменилось
grep -E 'changed|updated|Modifying' "${log_base}-apply-modify.log" | head -3 \
|| true
else
fail "apply (modify) — см. ${log_base}-apply-modify.log"
RESULTS[$example]="FAIL (apply-modify)"
failed=1
fi
# Восстанавливаем файлы до destroy
restore_backup "$example"
# 5. destroy ────────────────────────────────────────────────────────────────
info "destroy"
if tf_retry "${log_base}-destroy.log" \
terraform -chdir="$dir" destroy -auto-approve -input=false -no-color \
&& assert_destroy_ok "${log_base}-destroy.log"; then
ok "destroy"
grep 'Destroy complete' "${log_base}-destroy.log" || true
else
fail "destroy — см. ${log_base}-destroy.log"
RESULTS[$example]="FAIL (destroy)"
return 1
fi
# Чистим после успешного прогона
rm -rf "$dir/.terraform" "$dir/.terraform.lock.hcl" \
"$dir/terraform.tfstate" "$dir/terraform.tfstate.backup"
if [ "$failed" -eq 0 ]; then
RESULTS[$example]="PASS"
fi
}
# ── Main ──────────────────────────────────────────────────────────────────────
main() {
echo
echo "╔══════════════════════════════════════╗"
echo "║ sless E2E tests $(date '+%Y-%m-%d %H:%M') ║"
echo "╚══════════════════════════════════════╝"
echo "Примеры: ${EXAMPLES[*]}"
echo "Логи: $LOGS_DIR"
for example in "${EXAMPLES[@]}"; do
run_example "$example" || true
done
# ── Итог ────────────────────────────────────────────────────────────────────
echo
echo "════════════════ ИТОГ ════════════════"
local all_pass=1
for example in "${EXAMPLES[@]}"; do
local result="${RESULTS[$example]:-UNKNOWN}"
if [ "$result" = "PASS" ]; then
ok "$example: $result"
else
fail "$example: $result"
all_pass=0
fi
done
echo "══════════════════════════════════════"
if [ "$all_pass" -eq 1 ]; then
echo -e "${GREEN}Все тесты прошли успешно.${RESET}"
exit 0
else
echo -e "${RED}Есть ошибки. Логи: $LOGS_DIR${RESET}"
exit 1
fi
}
main