diff --git a/HISTORY/2026-08-14-session-log.md b/HISTORY/2026-08-14-session-log.md index 45eb538..1536323 100644 --- a/HISTORY/2026-08-14-session-log.md +++ b/HISTORY/2026-08-14-session-log.md @@ -355,3 +355,30 @@ Smoke-тест: `aws sqs list-queues --endpoint-url https://sqs.containerk8s.dev **Решение (принято)**: всё выше + grace-продление 2 мин при сетевой ошибке для ранее валидного токена. TTL 5 мин; отозванный токен умирает максимум через 5 мин; изоляция тенантов не затрагивается. + +--- + +## v0.1.34 — кэш валидации JWT (14.08.2026) + +**Проблема**: jwtMiddleware валидировал токен через Nubes-шлюз на КАЖДЫЙ запрос консоли; +шлюз нестабильно отвечает с IP платформы (то 200, то 401) → консоль периодически падала. + +**Реализовано** (по плану Sonnet): +1. `app/auth/cache.go`: кэш успешной валидации в памяти: `map[hex(sha256(token))]{validUntil}` + + `sync.RWMutex`, TTL 5 мин, grace +2 мин при сетевой ошибке для ранее валидного токена, + фоновая очистка (ticker 5 мин, sync.Once). +2. `app/auth/jwt.go`: sentinel `ErrTokenRejected` (`%w`) для 401/403 — отличие от сетевых ошибок. +3. `app/admin/admin.go`: `jwtAuth` — всегда свежая проверка + `WarmTokenCache` после успеха; + `jwtMiddleware` — `PingNubesAPICached`; `NewHandler` — старт очистки. + +**Проверено локально (мок-шлюз)**: логин с живым моком → 200; мок убит → логин 403 (свежо, правильно), +CREDS → 200 по кэшу (шлюз не нужен), неизвестный токен → 403. Vet/тесты OK. + +**Smoke на проде**: 11 PASS, 1 FAIL — только логин (шлюз режет IP платформы; по дизайну логин всегда свежо). +SQS-цикл create→send→receive→delete→delete-queue — PASS. + +**Особенность платформы**: GET /ui через ingress не закрывает соединение (~150с, curl exit 28), +внутри пода мгновенно. В smoke /ui проверяется range-запросом (206, 0.2с). +Браузеру не мешает (HTML приходит сразу). + +**Деплой**: v0.1.34 digest `sha256:17f9bbdb…`, под Ready, /health → v0.1.34. diff --git a/tests/smoke.sh b/tests/smoke.sh new file mode 100755 index 0000000..5638ae6 --- /dev/null +++ b/tests/smoke.sh @@ -0,0 +1,118 @@ +#!/usr/bin/env bash +# tests/smoke.sh — предварительные smoke-тесты работоспособности shared-sqs. +# Быстрая проверка: HTTP-эндпоинты, UI-аутентификация, полный цикл SQS API. +# Позже будет заменён/дополнен полным набором тестов. +# +# Использование: +# ENDPOINT=https://sqs.containerk8s.dev.nubes.ru \ +# TOKEN='' \ +# AWS_ACCESS_KEY_ID=... AWS_SECRET_ACCESS_KEY=... \ +# ./tests/smoke.sh +# +# TOKEN и AWS-ключи необязательны — соответствующие секции будут пропущены. +set -u + +ENDPOINT="${ENDPOINT:-https://sqs.containerk8s.dev.nubes.ru}" +REGION="${REGION:-us-east-1}" +UA="Mozilla/5.0" + +PASS=0 +FAIL=0 + +ok() { PASS=$((PASS+1)); echo " ✅ $1"; } +bad() { FAIL=$((FAIL+1)); echo " ❌ $1"; } + +# check <имя> <ожидаемая подстрока> +check() { + local name="$1" url="$2" expect="$3" body + body=$(curl --max-time 15 -s -H "User-Agent: $UA" "$url") || { bad "$name: ошибка curl"; return; } + if echo "$body" | grep -qF "$expect"; then ok "$name"; else bad "$name (ожидали '$expect')"; fi +} + +# check_range <имя> <диапазон> <ожидаемая подстрока> +# Для /ui: платформенный ingress не закрывает соединение на этом пути, +# поэтому полное чтение тела висит (~150с). Range-запрос завершается сразу. +check_range() { + local name="$1" url="$2" range="$3" expect="$4" body + body=$(curl --max-time 15 -s -r "$range" -H "User-Agent: $UA" "$url") || { bad "$name: ошибка curl"; return; } + if echo "$body" | grep -qF "$expect"; then ok "$name"; else bad "$name (ожидали '$expect')"; fi +} + +echo "== HTTP-эндпоинты ==" +check "GET /health" "$ENDPOINT/health" '"status":"ok"' +check "GET / (главная)" "$ENDPOINT/" "Порядок действий" +check "GET /examples" "$ENDPOINT/examples" "Все команды SQS API" +check "GET /admin" "$ENDPOINT/admin" "SQS Admin" +check_range "GET /ui" "$ENDPOINT/ui" "0-500" "SQS Console" +check "GET /static/logo.svg" "$ENDPOINT/static/logo.svg" "&1) \ + && ok "CreateQueue" || { bad "CreateQueue: $CQ"; CQ=""; } + + QUEUE_URL=$(echo "$CQ" | sed -n 's/.*"QueueUrl": *"\([^"]*\)".*/\1/p') + if [[ -z "$QUEUE_URL" ]]; then + bad "не удалось извлечь QueueUrl" + else + aws sqs send-message --queue-url "$QUEUE_URL" --message-body "smoke-test" \ + --endpoint-url "$ENDPOINT" --region "$REGION" >/dev/null 2>&1 \ + && ok "SendMessage" || bad "SendMessage" + + # ReceiveMessage может вернуть пусто при первом вызове — пробуем до 3 раз + RM=""; RH="" + for i in 1 2 3; do + RM=$(aws sqs receive-message --queue-url "$QUEUE_URL" \ + --endpoint-url "$ENDPOINT" --region "$REGION" 2>&1) + echo "$RM" | grep -qF '"Body": "smoke-test"' && { ok "ReceiveMessage"; break; } + sleep 1 + done + echo "$RM" | grep -qF '"Body": "smoke-test"' || bad "ReceiveMessage: $RM" + + RH=$(echo "$RM" | sed -n 's/.*"ReceiptHandle": *"\([^"]*\)".*/\1/p' | head -1) + if [[ -n "$RH" ]]; then + aws sqs delete-message --queue-url "$QUEUE_URL" --receipt-handle "$RH" \ + --endpoint-url "$ENDPOINT" --region "$REGION" >/dev/null 2>&1 \ + && ok "DeleteMessage" || bad "DeleteMessage" + fi + + aws sqs delete-queue --queue-url "$QUEUE_URL" \ + --endpoint-url "$ENDPOINT" --region "$REGION" >/dev/null 2>&1 \ + && ok "DeleteQueue" || bad "DeleteQueue" + fi +else + echo " ⏭ пропущено (нет AWS-ключей)" +fi + +echo +echo "Итог: PASS=$PASS FAIL=$FAIL" +[[ $FAIL -eq 0 ]]