mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-06 10:59:59 +00:00
The check listed the torrcs without the limit through "grep -L ...; true". With no torrc under /opt/chutney/net/nodes, or with one that cannot be read, grep printed nothing, the trailing true turned its error into success, and the test reported that every node runs one worker. The check now counts the limit per file with "grep -cH" and reads the exit status. A status above 1 or an empty answer fails the test, and a file with a count of 0 is named as before. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
125 lines
4.8 KiB
Bash
Executable File
125 lines
4.8 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# shellcheck shell=bash
|
|
#
|
|
# svc-net-tor role test: probe every currently-served domain and assert it
|
|
# responds. Onion (.onion) domains are reached through the node's Tor SOCKS
|
|
# proxy; any clearnet domains are reached directly. A domain "responds" when the
|
|
# reverse proxy returns an HTTP status in 200-499 (5xx or no answer = failure).
|
|
#
|
|
# Domains are auto-discovered from the deployed OpenResty vhosts
|
|
# (<servers>/{http,https}/<domain>.conf); pass explicit domains as arguments to
|
|
# override.
|
|
#
|
|
# Env (all required; the role's test.env carries them):
|
|
# TOR_SOCKS SOCKS proxy for .onion (127.0.0.1:<svc-net-tor
|
|
# services.tor.ports.local.socks>)
|
|
# NGINX_SERVERS_DIR the deployed OpenResty servers dir from the nginx lookup
|
|
# RETRIES attempts per domain
|
|
# SLEEP_SECONDS wait between attempts
|
|
# TOR_DNSMASQ_CONF the dnsmasq drop-in path; its directory is scanned for
|
|
# upstreams when a probe fails
|
|
# TOR_CONTAINER address of the tor container
|
|
# TOR_FLAVOR public or chutney
|
|
|
|
set -uo pipefail
|
|
|
|
TOR_SOCKS="${TOR_SOCKS:?pass TOR_SOCKS as env (127.0.0.1:<svc-net-tor services.tor.ports.local.socks>)}"
|
|
NGINX_SERVERS_DIR="${NGINX_SERVERS_DIR:?pass NGINX_SERVERS_DIR as env (the deployed OpenResty vhost servers dir from the nginx lookup, e.g. /etc/nginx/conf.d/servers)}"
|
|
RETRIES="${RETRIES:?pass RETRIES as env (attempts per domain, from svc-net-tor test.env)}"
|
|
SLEEP_SECONDS="${SLEEP_SECONDS:?pass SLEEP_SECONDS as env (wait between attempts, from svc-net-tor test.env)}"
|
|
TOR_DNSMASQ_CONF="${TOR_DNSMASQ_CONF:?pass TOR_DNSMASQ_CONF as env (the dnsmasq drop-in path, from svc-net-tor vars)}"
|
|
TOR_CONTAINER="${TOR_CONTAINER:?pass TOR_CONTAINER as env (address of the tor container, from svc-net-tor test.env)}"
|
|
TOR_FLAVOR="${TOR_FLAVOR:?pass TOR_FLAVOR as env (public or chutney, from svc-net-tor test.env)}"
|
|
|
|
discover_domains() {
|
|
find "${NGINX_SERVERS_DIR}/http" "${NGINX_SERVERS_DIR}/https" \
|
|
-maxdepth 1 -type f -name '*.conf' 2>/dev/null |
|
|
sed -E 's#.*/##; s#\.conf$##' | sort -u
|
|
}
|
|
|
|
if [[ "$#" -gt 0 ]]; then
|
|
domains=("$@")
|
|
else
|
|
mapfile -t domains < <(discover_domains)
|
|
fi
|
|
|
|
if [[ "${#domains[@]}" -eq 0 ]]; then
|
|
echo "[FATAL] no domains found under ${NGINX_SERVERS_DIR} and none given as arguments" >&2
|
|
exit 2
|
|
fi
|
|
|
|
echo "[INFO] probing ${#domains[@]} domain(s); onion via socks5://${TOR_SOCKS}"
|
|
|
|
# Probe one domain with retries. Echoes the final HTTP code; returns 0 on success.
|
|
probe() {
|
|
local domain="$1" scheme curl_proxy=() attempt=1 code
|
|
if [[ "${domain}" == *.onion ]]; then
|
|
scheme="http"
|
|
curl_proxy=(--socks5-hostname "${TOR_SOCKS}")
|
|
else
|
|
scheme="https"
|
|
fi
|
|
while [[ "${attempt}" -le "${RETRIES}" ]]; do
|
|
code="$(curl "${curl_proxy[@]}" --silent --show-error --location \
|
|
--insecure --max-time 60 \
|
|
-o /dev/null -w '%{http_code}' \
|
|
"${scheme}://${domain}/")"
|
|
if [[ "${code}" =~ ^[234][0-9][0-9]$ ]]; then
|
|
echo "${code}"
|
|
return 0
|
|
fi
|
|
attempt=$((attempt + 1))
|
|
[[ "${attempt}" -le "${RETRIES}" ]] && sleep "${SLEEP_SECONDS}"
|
|
done
|
|
echo "${code:-000}"
|
|
return 1
|
|
}
|
|
|
|
diagnose() {
|
|
local domain="$1" upstream upstreams=()
|
|
echo " resolv: $(grep -v '^#' /etc/resolv.conf 2>/dev/null | tr '\n' ' ')"
|
|
grep -F "${domain}" /etc/hosts 2>/dev/null | sed 's/^/ hosts: /'
|
|
if ! command -v dig >/dev/null 2>&1; then
|
|
echo " dig: not installed"
|
|
return
|
|
fi
|
|
echo " dig: $(dig +short +time=2 +tries=1 "${domain}" A 2>&1 | tr '\n' ' ')"
|
|
mapfile -t upstreams < <(sed -nE 's/^server=([0-9.]+)$/\1/p' "$(dirname "${TOR_DNSMASQ_CONF}")"/*.conf 2>/dev/null)
|
|
for upstream in "${upstreams[@]}"; do
|
|
echo " dig@${upstream}: $(dig +short +time=2 +tries=1 "@${upstream}" "${domain}" A 2>&1 | tr '\n' ' ')"
|
|
done
|
|
}
|
|
|
|
failed=0
|
|
for domain in "${domains[@]}"; do
|
|
if code="$(probe "${domain}")"; then
|
|
printf '[OK] %-60s HTTP %s\n' "${domain}" "${code}"
|
|
else
|
|
addr="$(getent hosts "${domain}" 2>/dev/null | head -n1)"
|
|
printf '[FAIL] %-60s HTTP %s dns=%s\n' "${domain}" "${code}" "${addr%% *}"
|
|
diagnose "${domain}"
|
|
failed=$((failed + 1))
|
|
fi
|
|
done
|
|
|
|
echo "[INFO] ${#domains[@]} probed, ${failed} failed"
|
|
|
|
"$(dirname "${BASH_SOURCE[0]}")/onion_ports.py" || failed=$((failed + 1))
|
|
|
|
if [[ "${TOR_FLAVOR}" == "chutney" ]]; then
|
|
status=0
|
|
workers="$(container exec "${TOR_CONTAINER}" sh -c 'grep -cH "^NumCPUs 1$" /opt/chutney/net/nodes/*/torrc')" || status=$?
|
|
unbounded="$(sed -n 's/:0$//p' <<<"${workers}")"
|
|
if [[ "${status}" -gt 1 || -z "${workers}" ]]; then
|
|
echo "[FAIL] cannot read the chutney node torrcs in ${TOR_CONTAINER}"
|
|
failed=$((failed + 1))
|
|
elif [[ -n "${unbounded}" ]]; then
|
|
echo "[FAIL] chutney nodes without NumCPUs 1 start one worker per host core: ${unbounded//$'\n'/ }"
|
|
failed=$((failed + 1))
|
|
else
|
|
echo "[OK] every chutney node runs one worker"
|
|
fi
|
|
fi
|
|
|
|
[[ "${failed}" -eq 0 ]]
|