Demo images / Build & push demo images (push) Failing after 2m16s
Keeps the dev and test tooling an outside contributor would want, and takes our node addresses out of it. Scripts that silently defaulted to one of our nodes now require an explicit host and exit 2 without one: smoke-test.sh, trust-archipelago-cert.sh, dev-container-test.sh (which also derives its RPC and health URLs from the SSH target instead of a second hardcoded copy), and image-recipe/dev-branding.sh. A default that points at a machine the user does not own is worse than no default: it fails confusingly, or reaches a stranger's device. Usage examples, mock data and test fixtures move to the RFC 5737 documentation range (192.0.2.0/24). CGNAT test values stay inside 100.64.0.0/10 so the range-check semantics they exercise still hold, and 192.168.1.0/.1/.254 are left alone — those are gateway logic and UI placeholders, not our addresses. Playwright and the perf spec defaulted their baseURL to one of our nodes; they now default to localhost:8100, the local dev server. Removed neode-ui APP_URLS entirely. It is dead code — exported, never imported — and it pinned fedimint's *prod* launch URL to 192.168.1.228:8175. Had anything consumed it, every user's node would have tried to reach an address that on their LAN is either nothing or someone else's machine. Deleting beats sanitizing dead config. Verified: frontend 868/868 vitest across 108 files; archipelago-container 75/75; mesh tests 9/9; audit-secrets 5/5. Zero node addresses and zero node names remain in tracked files. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
162 lines
5.2 KiB
Bash
Executable File
162 lines
5.2 KiB
Bash
Executable File
#!/bin/bash
|
|
#
|
|
# App surface smoke test.
|
|
#
|
|
# Verifies that installed containers have their published host ports listening
|
|
# and that known nginx app proxy paths return a non-5xx response. This catches
|
|
# the common "container is running but UI disappeared" failure mode.
|
|
#
|
|
# Usage:
|
|
# scripts/app-surface-smoke-test.sh --target archipelago@192.0.2.10 --ssh-key /path/key
|
|
|
|
set -euo pipefail
|
|
|
|
TARGET=""
|
|
SSH_KEY="${ARCHIPELAGO_SSH_KEY:-}"
|
|
SSH_EXTRA=()
|
|
|
|
while [ "$#" -gt 0 ]; do
|
|
case "$1" in
|
|
--target) TARGET="${2:-}"; shift 2 ;;
|
|
--ssh-key) SSH_KEY="${2:-}"; shift 2 ;;
|
|
--ssh-option) SSH_EXTRA+=("-o" "${2:-}"); shift 2 ;;
|
|
-h|--help) sed -n '1,12p' "$0"; exit 0 ;;
|
|
*) echo "unknown argument: $1" >&2; exit 2 ;;
|
|
esac
|
|
done
|
|
|
|
[ -n "$TARGET" ] || { echo "--target is required" >&2; exit 2; }
|
|
|
|
SSH_OPTS=(-F /dev/null -o BatchMode=yes -o PreferredAuthentications=publickey -o PasswordAuthentication=no)
|
|
[ -n "$SSH_KEY" ] && SSH_OPTS+=(-i "$SSH_KEY")
|
|
SSH_OPTS+=("${SSH_EXTRA[@]}")
|
|
|
|
ssh_run() {
|
|
ssh "${SSH_OPTS[@]}" "$TARGET" "$@"
|
|
}
|
|
|
|
ssh_run 'bash -s' <<'REMOTE'
|
|
set -u
|
|
|
|
pass=0
|
|
fail=0
|
|
|
|
ok() { echo " PASS $*"; pass=$((pass + 1)); }
|
|
bad() { echo " FAIL $*"; fail=$((fail + 1)); }
|
|
|
|
container_exists() {
|
|
podman ps -a --format '{{.Names}}' 2>/dev/null | grep -qx "$1"
|
|
}
|
|
|
|
port_listening() {
|
|
ss -ltn 2>/dev/null | awk '{print $4}' | grep -Eq "(^|:)$1$"
|
|
}
|
|
|
|
http_code() {
|
|
local url="$1" code
|
|
for _ in 1 2 3; do
|
|
code=$(curl -ksS -o /dev/null -w '%{http_code}' --max-time 12 "$url" 2>/dev/null || true)
|
|
[ -n "$code" ] || code=000
|
|
[ "$code" != "000" ] && { echo "$code"; return; }
|
|
sleep 2
|
|
done
|
|
echo "$code"
|
|
}
|
|
|
|
http_post_code() {
|
|
local url="$1" code
|
|
for _ in 1 2 3; do
|
|
code=$(curl -ksS -o /dev/null -w '%{http_code}' --max-time 25 \
|
|
-H 'Content-Type: application/json' \
|
|
-d '{"jsonrpc":"2.0","id":1,"method":"getblockchaininfo","params":[]}' \
|
|
"$url" 2>/dev/null || true)
|
|
[ -n "$code" ] || code=000
|
|
[ "$code" != "000" ] && { echo "$code"; return; }
|
|
sleep 2
|
|
done
|
|
echo "$code"
|
|
}
|
|
|
|
assert_http() {
|
|
local label="$1" url="$2" code
|
|
code=$(http_code "$url")
|
|
case "$code" in
|
|
200|204|301|302|307|308|401|403) ok "$label HTTP $code" ;;
|
|
*) bad "$label HTTP $code ($url)" ;;
|
|
esac
|
|
}
|
|
|
|
assert_http_post() {
|
|
local label="$1" url="$2" code
|
|
code=$(http_post_code "$url")
|
|
case "$code" in
|
|
200|204|401|403) ok "$label HTTP POST $code" ;;
|
|
*) bad "$label HTTP POST $code ($url)" ;;
|
|
esac
|
|
}
|
|
|
|
assert_container_ports() {
|
|
local name="$1" ports port missing=0
|
|
container_exists "$name" || return 0
|
|
ports=$(podman inspect "$name" --format '{{range $p,$bindings := .NetworkSettings.Ports}}{{if $bindings}}{{range $bindings}}{{.HostPort}}{{"\n"}}{{end}}{{end}}{{end}}' 2>/dev/null | sort -u)
|
|
[ -n "$ports" ] || return 0
|
|
while IFS= read -r port; do
|
|
[ -n "$port" ] || continue
|
|
if port_listening "$port"; then
|
|
ok "$name port $port listening"
|
|
else
|
|
bad "$name port $port missing listener"
|
|
missing=1
|
|
fi
|
|
done <<< "$ports"
|
|
return "$missing"
|
|
}
|
|
|
|
assert_env_contains() {
|
|
local name="$1" key="$2" needle="$3" val
|
|
container_exists "$name" || return 0
|
|
val=$(podman inspect "$name" --format '{{range .Config.Env}}{{println .}}{{end}}' 2>/dev/null | sed -n "s/^${key}=//p" | head -n 1)
|
|
if [ -n "$val" ] && printf '%s' "$val" | grep -qF "$needle"; then
|
|
ok "$name env $key"
|
|
else
|
|
bad "$name env $key missing $needle"
|
|
fi
|
|
}
|
|
|
|
echo "[surface] host=$(hostname) ip=$(hostname -I 2>/dev/null | awk '{print $1}')"
|
|
|
|
for c in $(podman ps -a --format '{{.Names}}' 2>/dev/null | sort); do
|
|
assert_container_ports "$c" || true
|
|
done
|
|
|
|
container_exists archy-bitcoin-ui && {
|
|
assert_http "bitcoin-ui" "http://127.0.0.1/app/bitcoin-ui/"
|
|
assert_http "bitcoin status" "http://127.0.0.1/app/bitcoin-ui/bitcoin-status"
|
|
assert_http_post "bitcoin rpc proxy" "http://127.0.0.1/app/bitcoin-ui/bitcoin-rpc/"
|
|
}
|
|
|
|
container_exists archy-electrs-ui && {
|
|
assert_http "electrumx ui" "http://127.0.0.1/app/electrumx/"
|
|
assert_http "electrumx status" "http://127.0.0.1/app/electrumx/electrs-status"
|
|
assert_http "electrs legacy status" "http://127.0.0.1/app/electrs/electrs-status"
|
|
}
|
|
|
|
container_exists mempool && assert_http "mempool ui" "http://127.0.0.1/app/mempool/"
|
|
container_exists indeedhub && assert_http "indeedhub ui" "http://127.0.0.1:7778/"
|
|
container_exists uptime-kuma && assert_http "uptime-kuma" "http://127.0.0.1/app/uptime-kuma/"
|
|
container_exists filebrowser && assert_http "filebrowser" "http://127.0.0.1/app/filebrowser/"
|
|
container_exists searxng && assert_http "searxng" "http://127.0.0.1/app/searxng/"
|
|
container_exists grafana && assert_http "grafana" "http://127.0.0.1/app/grafana/"
|
|
container_exists portainer && assert_http "portainer" "http://127.0.0.1/app/portainer/"
|
|
container_exists vaultwarden && assert_http "vaultwarden" "http://127.0.0.1/app/vaultwarden/"
|
|
container_exists nextcloud && assert_http "nextcloud" "http://127.0.0.1/app/nextcloud/"
|
|
container_exists archy-nbxplorer && assert_env_contains "archy-nbxplorer" "NBXPLORER_POSTGRES" "Database=nbxplorer"
|
|
container_exists btcpay-server && {
|
|
assert_env_contains "btcpay-server" "BTCPAY_POSTGRES" "Database=btcpay"
|
|
assert_http "btcpay" "http://127.0.0.1/app/btcpay/"
|
|
}
|
|
|
|
echo "[surface] summary: pass=$pass fail=$fail"
|
|
[ "$fail" -eq 0 ]
|
|
REMOTE
|