From 382cd50a1bbffc0d96adb24e4411194885e53749 Mon Sep 17 00:00:00 2001 From: ShinyHero666 <160204855+ShinyHero666@users.noreply.github.com> Date: Wed, 5 Aug 2026 12:13:08 +0800 Subject: [PATCH] fix(smoke): support separate actuator target Signed-off-by: ShinyHero666 <160204855+ShinyHero666@users.noreply.github.com> --- README.md | 12 ++++ scripts/smoke-test.sh | 63 +++++++++++++++++++-- scripts/tests/smoke-test-admin-mode-test.sh | 51 +++++++++++++++-- 3 files changed, 115 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 34555dda7..bf37d13c6 100644 --- a/README.md +++ b/README.md @@ -400,6 +400,18 @@ Run it against a local backend: ./scripts/smoke-test.sh http://localhost:8080 ``` +Local Compose and staging runs can keep using one backend URL. For an ingress +deployment where the public URL exposes application APIs but keeps Actuator on +the backend service, set a separate Actuator target: + +```bash +ACTUATOR_BASE_URL=http://skillhub-server:8080 \ + ./scripts/smoke-test.sh https://skillhub.example.com +``` + +The health check requires an Actuator JSON response, so an HTML SPA fallback is +reported as a routing or target error instead of a successful health response. + Admin label-management smoke checks run only when current admin credentials are supplied explicitly: diff --git a/scripts/smoke-test.sh b/scripts/smoke-test.sh index a523aafeb..78852f1b0 100755 --- a/scripts/smoke-test.sh +++ b/scripts/smoke-test.sh @@ -2,16 +2,18 @@ set -euo pipefail BASE_URL="${1:-http://localhost:8080}" +ACTUATOR_BASE_URL="${ACTUATOR_BASE_URL:-$BASE_URL}" PASS=0 FAIL=0 -COOKIE_JAR="$(mktemp)" +TMP_DIR="$(mktemp -d)" +COOKIE_JAR="$TMP_DIR/cookies" USERNAME="smoketest_$(date +%s)" EMAIL="${USERNAME}@example.com" PASSWORD="Smoke@2026" NEW_PASSWORD="Smoke@2027" cleanup() { - rm -f "$COOKIE_JAR" + rm -rf "$TMP_DIR" } trap cleanup EXIT @@ -31,6 +33,56 @@ check() { fi } +check_health() { + local desc="$1" + local url="$2" + local body_file="$TMP_DIR/health-body" + local result + local status + local content_type + result="$(curl --retry 3 --retry-delay 1 --max-time 10 -sS -o "$body_file" \ + -w "%{http_code}|%{content_type}" "$url" || true)" + status="${result%%|*}" + content_type="${result#*|}" + + if [[ "$content_type" == text/html* ]]; then + echo "FAIL: $desc (routing/target error: received $content_type from $url)" + FAIL=$((FAIL + 1)) + elif [[ "$status" == "200" \ + && ( "$content_type" == application/json* || "$content_type" == application/*+json* ) \ + && -f "$body_file" \ + && "$(grep -Ec '"status"[[:space:]]*:' "$body_file" || true)" -gt 0 ]]; then + echo "PASS: $desc (HTTP $status, $content_type)" + PASS=$((PASS + 1)) + else + echo "FAIL: $desc (expected HTTP 200 actuator JSON, got HTTP $status, ${content_type:-no content type})" + FAIL=$((FAIL + 1)) + fi +} + +check_protected_actuator() { + local desc="$1" + local url="$2" + local result + local status + local content_type + result="$(curl --retry 3 --retry-delay 1 --max-time 10 -sS -o /dev/null \ + -w "%{http_code}|%{content_type}" "$url" || true)" + status="${result%%|*}" + content_type="${result#*|}" + + if [[ "$content_type" == text/html* ]]; then + echo "FAIL: $desc (routing/target error: received $content_type from $url)" + FAIL=$((FAIL + 1)) + elif [[ "$status" == "401" ]]; then + echo "PASS: $desc (HTTP $status)" + PASS=$((PASS + 1)) + else + echo "FAIL: $desc (expected 401, got $status)" + FAIL=$((FAIL + 1)) + fi +} + finish() { echo echo "Results: $PASS passed, $FAIL failed" @@ -38,11 +90,12 @@ finish() { } echo "=== SkillHub Smoke Test ===" -echo "Target: $BASE_URL" +echo "API target: $BASE_URL" +echo "Actuator target: $ACTUATOR_BASE_URL" echo -check "Health endpoint" "$BASE_URL/actuator/health" "200" -check "Prometheus metrics requires auth" "$BASE_URL/actuator/prometheus" "401" +check_health "Health endpoint" "$ACTUATOR_BASE_URL/actuator/health" +check_protected_actuator "Prometheus metrics requires auth" "$ACTUATOR_BASE_URL/actuator/prometheus" check "Namespaces API requires auth" "$BASE_URL/api/v1/namespaces" "401" check "Auth required" "$BASE_URL/api/v1/auth/me" "401" diff --git a/scripts/tests/smoke-test-admin-mode-test.sh b/scripts/tests/smoke-test-admin-mode-test.sh index aa0c6d9ed..672b1a586 100755 --- a/scripts/tests/smoke-test-admin-mode-test.sh +++ b/scripts/tests/smoke-test-admin-mode-test.sh @@ -26,6 +26,7 @@ data="" cookie_in="" cookie_out="" write_code=false +write_format="" output_file="" while (($#)); do case "$1" in @@ -47,6 +48,7 @@ while (($#)); do ;; -w) write_code=true + write_format="$2" shift 2 ;; -o) @@ -76,8 +78,16 @@ fi printf '%s\n' "$method $url $data" >>"${SMOKE_CURL_LOG:?SMOKE_CURL_LOG is required}" status=200 +content_type="application/json" +body='{}' case "$url" in - */actuator/health) status=200 ;; + https://public.example/actuator/health|https://public.example/actuator/prometheus) + content_type="text/html" + body='SkillHub' + ;; + */actuator/health) + body='{"status":"UP"}' + ;; */actuator/prometheus) status=401 ;; */api/v1/namespaces) if [[ -n "$cookie_in" && -f "$cookie_in.session" ]]; then status=200; else status=401; fi @@ -108,24 +118,35 @@ case "$url" in esac if [[ "$output_file" != "/dev/null" && -n "$output_file" ]]; then - printf '{}\n' >"$output_file" + printf '%s\n' "$body" >"$output_file" fi if [[ "$write_code" == true ]]; then - printf '%s' "$status" + if [[ "$write_format" == *content_type* ]]; then + printf '%s|%s' "$status" "$content_type" + else + printf '%s' "$status" + fi fi EOF chmod +x "$TMP_DIR/bin/curl" -run_smoke() { +run_smoke_at() { local name="$1" - shift + local base_url="$2" + shift 2 local log="$TMP_DIR/$name.curl.log" local out="$TMP_DIR/$name.out" local status=0 - env PATH="$TMP_DIR/bin:$PATH" SMOKE_CURL_LOG="$log" "$@" "$SMOKE_SCRIPT" http://skillhub.test >"$out" 2>&1 || status=$? + env PATH="$TMP_DIR/bin:$PATH" SMOKE_CURL_LOG="$log" "$@" "$SMOKE_SCRIPT" "$base_url" >"$out" 2>&1 || status=$? printf '%s\n' "$status" } +run_smoke() { + local name="$1" + shift + run_smoke_at "$name" http://skillhub.test "$@" +} + status="$(run_smoke skip-admin env)" [[ "$status" == "0" ]] || fail "default smoke without admin credentials should pass" grep -Fq "SKIP: Admin label management" "$TMP_DIR/skip-admin.out" \ @@ -153,4 +174,22 @@ if grep -Fq 'ChangeMe!2026' "$TMP_DIR/explicit-admin.curl.log"; then fail "admin login must not fall back to the bootstrap default password" fi +status="$(run_smoke_at split-targets https://public.example env \ + ACTUATOR_BASE_URL=http://actuator.internal:8080 SMOKE_ADMIN_CHECKS=false)" +[[ "$status" == "0" ]] || fail "split public and actuator targets should pass" +grep -Fq "GET http://actuator.internal:8080/actuator/health" "$TMP_DIR/split-targets.curl.log" \ + || fail "health check should use ACTUATOR_BASE_URL" +grep -Fq "GET http://actuator.internal:8080/actuator/prometheus" "$TMP_DIR/split-targets.curl.log" \ + || fail "Prometheus check should use ACTUATOR_BASE_URL" +if grep -Fq "https://public.example/actuator/" "$TMP_DIR/split-targets.curl.log"; then + fail "actuator checks must not use the public API target when ACTUATOR_BASE_URL is set" +fi +grep -Fq "GET https://public.example/api/v1/auth/me" "$TMP_DIR/split-targets.curl.log" \ + || fail "application API checks should continue using BASE_URL" + +status="$(run_smoke_at html-fallback https://public.example env SMOKE_ADMIN_CHECKS=false)" +[[ "$status" != "0" ]] || fail "HTML SPA fallback must not pass as actuator health" +grep -Fq "routing/target error: received text/html" "$TMP_DIR/html-fallback.out" \ + || fail "HTML fallback should produce an actionable routing/target error" + echo "smoke-test-admin-mode-test passed"