mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2026-09-03 05:10:16 +03:00
# Description of Changes - The multi-node compose stack + behave suite (11 features) - The nightly multinode-e2e job in build-enterprise.yml cuke features are cluster_health - both nodes boot healthy and join the Valkey backplane load_balancing - traffic spreads across nodes; no spurious 401 when bounced cross_node_auth - a token from one node validates on all nodes (shared DB keys) shared_state - teams/sources/org visible from every node policy_management - create/rename/delete a policy on any node, reflected everywhere source_management - source CRUD cross-node; referenced source can't be deleted anywhere connections - S3 connection resolves (secret masked) and deletes cluster-wide processor_ledger - files processed exactly once even when both nodes trigger together policy_run_coordination - a run on one node is visible from every node rate_limiting - rate-limit counters shared via Valkey, not per node failover - LB keeps serving when a node dies; recovered node accepts existing tokens can now start a full node system with export PREMIUM_KEY=<your licence key> ./start-multinode-test.sh starts a 40 person org DB install with multi node and database (--no-seed to have without DB on startup) 4 teams 1 s3 connection 1 policy --- ## Checklist ### General - [ ] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [ ] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md) (if applicable) - [ ] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md) (if applicable) - [ ] I have performed a self-review of my own code - [ ] My changes generate no new warnings ### Documentation - [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### Translations (if applicable) - [ ] I ran [`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md) ### UI Changes (if applicable) - [ ] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [ ] I have run `task check` to verify linters, typechecks, and tests pass - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#7-testing) for more details.
82 lines
4.3 KiB
Bash
82 lines
4.3 KiB
Bash
#!/usr/bin/env bash
|
|
# Multi-node smoke tests against a running stack (start-multinode-test.sh): load-balancer spread, cross-node JWT validation (signing keys persist in the shared DB), and processor state visible from every node.
|
|
# Auth: extracts the Bearer JWT from the login body via sed (no jq needed host-side) and hits nodes directly with docker exec.
|
|
# Non-destructive - safe to re-run against the stack at http://localhost:8080.
|
|
set -uo pipefail
|
|
cd "$(dirname "$0")"
|
|
|
|
LB="http://localhost:8080"
|
|
ADMIN_USER="admin"; ADMIN_PASS="stirling"
|
|
NODES="multinode-stirling-1 multinode-stirling-2"
|
|
# An authed, admin-visible endpoint that returns 200 with a valid token, 401 without.
|
|
PROBE="/api/v1/sources"
|
|
pass=0; fail=0
|
|
ok() { echo " PASS - $*"; pass=$((pass+1)); }
|
|
bad() { echo " FAIL - $*"; fail=$((fail+1)); }
|
|
|
|
login() { # -> prints the bearer token
|
|
curl -s -X POST "$LB/api/v1/auth/login" -H 'Content-Type: application/json' \
|
|
-d "{\"username\":\"$ADMIN_USER\",\"password\":\"$ADMIN_PASS\"}" \
|
|
| sed -n 's/.*"access_token":"\([^"]*\)".*/\1/p'
|
|
}
|
|
|
|
echo "== 1. Load balancer spreads requests across nodes =="
|
|
served=$(for i in $(seq 1 12); do
|
|
curl -s -D - -o /dev/null "$LB/api/v1/info/status" | tr -d '\r' | awk -F': ' '/^X-Served-By/{print $2}'
|
|
done | sort -u)
|
|
distinct=$(printf '%s\n' "$served" | grep -c .)
|
|
echo " upstreams seen: $(printf '%s' "$served" | paste -sd, -)"
|
|
[ "$distinct" -ge 2 ] && ok "LB round-robined across $distinct nodes" \
|
|
|| bad "only $distinct node(s) served (expected >=2; is X-Served-By enabled?)"
|
|
|
|
echo "== 2. A JWT from the LB is accepted by BOTH nodes directly (shared signing key) =="
|
|
jwt=$(login)
|
|
if [ -z "$jwt" ]; then
|
|
bad "admin login via LB failed - cannot test cross-node JWT"
|
|
else
|
|
ok "logged in via LB, got a JWT (${#jwt} chars)"
|
|
for n in $NODES; do
|
|
hc=$(docker exec "$n" curl -s -o /dev/null -w '%{http_code}' \
|
|
-H "Authorization: Bearer $jwt" "http://localhost:8080$PROBE" 2>/dev/null)
|
|
[ "$hc" = "200" ] && ok "$n accepted the foreign-minted JWT (HTTP 200)" \
|
|
|| bad "$n rejected the JWT (HTTP $hc) - keys not shared across nodes"
|
|
done
|
|
fi
|
|
|
|
echo "== 3. Processor state is shared: each node sees the same sources =="
|
|
count_of() { # $1=node -> number of sources that node reports
|
|
docker exec "$1" curl -s -H "Authorization: Bearer $jwt" "http://localhost:8080$PROBE" 2>/dev/null \
|
|
| grep -o '"id"' | grep -c .
|
|
}
|
|
a=$(count_of multinode-stirling-1); b=$(count_of multinode-stirling-2)
|
|
echo " stirling-1 sources: $a stirling-2 sources: $b"
|
|
if [ "$a" -gt 0 ] && [ "$a" = "$b" ]; then
|
|
ok "both nodes report the same $a sources (shared DB)"
|
|
else
|
|
bad "source counts differ or zero across nodes ($a vs $b)"
|
|
fi
|
|
|
|
echo "== 4. Seeded org is in the shared DB =="
|
|
users=$(docker exec multinode-postgres psql -U stirling -d stirling -tAc "select count(*) from users" 2>/dev/null | tr -d '[:space:]')
|
|
teams=$(docker exec multinode-postgres psql -U stirling -d stirling -tAc "select count(*) from teams" 2>/dev/null | tr -d '[:space:]')
|
|
conns=$(docker exec multinode-postgres psql -U stirling -d stirling -tAc "select count(*) from integration_configs" 2>/dev/null | tr -d '[:space:]')
|
|
echo " users=$users teams=$teams integration_configs=$conns"
|
|
[ "${users:-0}" -ge 40 ] && ok "$users users present" || bad "only ${users:-0} users (did the seed run?)"
|
|
[ "${conns:-0}" -ge 1 ] && ok "$conns S3/integration connection(s) present" || bad "no integration connections"
|
|
|
|
echo "== 5. Cross-node encrypted-secret read (shared credential key) =="
|
|
# The seed's S3 secret was encrypted by whichever node handled it; fetching it via the LB (either node) and getting a masked, non-error view proves the credential key is shared, not per-node.
|
|
lc=$(curl -s -o /tmp/mn_conns.json -w '%{http_code}' -H "Authorization: Bearer $jwt" "$LB/api/v1/integrations")
|
|
if [ "$lc" = "200" ] && grep -q '"integrationType"' /tmp/mn_conns.json; then
|
|
ok "integration list decrypts through the LB (HTTP 200) - credential key is shared"
|
|
else
|
|
bad "integration list failed (HTTP $lc) - credential key may not be shared across nodes"
|
|
fi
|
|
|
|
echo
|
|
echo "============================================================"
|
|
echo " Multi-node validation: $pass passed, $fail failed."
|
|
echo " Stack left running: $LB (admin / stirling)"
|
|
echo "============================================================"
|
|
[ "$fail" -eq 0 ]
|