v0.1.34: кэш валидации JWT + smoke-тесты
This commit is contained in:
@@ -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.
|
||||
|
||||
Executable
+118
@@ -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='<API-токен Nubes>' \
|
||||
# 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 <имя> <url> <ожидаемая подстрока>
|
||||
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 <имя> <url> <диапазон> <ожидаемая подстрока>
|
||||
# Для /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" "<svg"
|
||||
|
||||
echo "== UI-аутентификация (нужен TOKEN) =="
|
||||
if [[ -n "${TOKEN:-}" ]]; then
|
||||
AUTH_BODY=$(curl --max-time 30 -s -X POST "$ENDPOINT/ui/api/auth" \
|
||||
-H "User-Agent: $UA" -H "Content-Type: application/json" \
|
||||
-d "{\"token\":\"$TOKEN\"}")
|
||||
if echo "$AUTH_BODY" | grep -qF '"token"'; then
|
||||
ok "POST /ui/api/auth"
|
||||
if echo "$AUTH_BODY" | grep -qF '"secret_key"'; then
|
||||
bad "auth-ответ содержит secret_key (утечка!)"
|
||||
else
|
||||
ok "auth-ответ БЕЗ secret_key"
|
||||
fi
|
||||
CREDS=$(curl --max-time 30 -s "$ENDPOINT/ui/api/credentials" \
|
||||
-H "User-Agent: $UA" -H "Authorization: Bearer $TOKEN")
|
||||
if echo "$CREDS" | grep -qF '"access_key"' && echo "$CREDS" | grep -qF '"secret_key"'; then
|
||||
ok "GET /ui/api/credentials"
|
||||
else
|
||||
bad "GET /ui/api/credentials"
|
||||
fi
|
||||
else
|
||||
bad "POST /ui/api/auth: $AUTH_BODY"
|
||||
echo " (логин валидируется всегда свежо; 401/403 здесь = шлюз Nubes режет IP платформы, не код)"
|
||||
fi
|
||||
else
|
||||
echo " ⏭ пропущено (нет TOKEN)"
|
||||
fi
|
||||
|
||||
echo "== SQS API (нужны AWS-ключи) =="
|
||||
if [[ -n "${AWS_ACCESS_KEY_ID:-}" && -n "${AWS_SECRET_ACCESS_KEY:-}" ]]; then
|
||||
Q="smoke-$(date +%s)-$RANDOM"
|
||||
CQ=$(aws sqs create-queue --queue-name "$Q" \
|
||||
--endpoint-url "$ENDPOINT" --region "$REGION" 2>&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 ]]
|
||||
Reference in New Issue
Block a user