#!/bin/bash # 验证双实例 watchdog 的判定逻辑(不真重启,靠 DRY_RUN + 桩函数) # # 为什么要专门测:双实例引入的分支不少(哪个死了/哪个低连接/公网通不通), # 直接覆盖生产脚本风险太大。 set -u SRC=/tmp/wd_v4.sh OUT=/tmp/wd_test_out # 先语法检查 echo "════════ 0. 语法检查 ════════" if bash -n "$SRC" 2>&1; then echo " ✓ 语法 OK" else echo " ✗ 语法有错" exit 1 fi # 造一个测试环境:把所有外部依赖替换成桩 setup_env() { rm -rf /tmp/wdtest mkdir -p /tmp/wdtest/relay sed -e 's#^RELAY_DIR=.*#RELAY_DIR=/tmp/wdtest/relay#' \ -e 's#^URL_FILE=.*#URL_FILE=/tmp/wdtest/relay/public_url.txt#' \ -e 's#^STATE_FILE=.*#STATE_FILE=/tmp/wdtest/relay/watchdog.state#' \ -e 's#^LAST_RESTART_FILE=.*#LAST_RESTART_FILE=/tmp/wdtest/relay/watchdog.last_restart#' \ -e 's#^TUNNEL_LOGS=.*#TUNNEL_LOGS=("/tmp/wdtest/relay/tunnel.log" "/tmp/wdtest/relay/tunnel-h2.log")#' \ "$SRC" > /tmp/wdtest/wd.sh echo "https://relay.example" > /tmp/wdtest/relay/public_url.txt touch /tmp/wdtest/relay/tunnel.log /tmp/wdtest/relay/tunnel-h2.log # logger 桩:别真写 syslog mkdir -p /tmp/wdtest/bin cat > /tmp/wdtest/bin/logger <<'EOF' #!/bin/bash exit 0 EOF cat > /tmp/wdtest/bin/curl <<'EOF' #!/bin/bash # 桩:METRICS_CURL 决定 metrics 返回什么,HEALTH_CODE 决定公网状态 for a in "$@"; do case "$a" in */metrics) printf '# TYPE cloudflared_tunnel_ha_connections gauge\ncloudflared_tunnel_ha_connections %s\n' "${STUB_CONNS_20241:-0}" exit 0 ;; *:20242/metrics) printf 'cloudflared_tunnel_ha_connections %s\n' "${STUB_CONNS_20242:-0}" exit 0 ;; esac done # 公网健康检查 if [ "${STUB_HEALTH:-200}" = "000" ]; then exit 7; fi echo "${STUB_HEALTH:-200}" EOF cat > /tmp/wdtest/bin/systemctl <<'EOF' #!/bin/bash if [ "$1" = "is-active" ]; then svc="${@: -1}" # STUB_DEAD_SVC 指定的服务返回非 active if [ "$svc" = "${STUB_DEAD_SVC:-}" ]; then exit 3; fi exit 0 fi if [ "$1" = "restart" ]; then echo "RESTART:$2" exit 0 fi exit 0 EOF chmod +x /tmp/wdtest/bin/* # 重置状态 rm -f /tmp/wdtest/relay/watchdog.state /tmp/wdtest/relay/watchdog.last_restart } # 跑一个场景 # $1 = 场景名 # $2 = CONNS 20241("X" 表示端口不通) # $3 = CONNS 20242 # $4 = 公网码 # $5 = 哪个服务 is-active 返回 false(可空) run_case() { local name="$1" c1="$2" c2="$3" health="$4" dead="${5:-}" setup_env # "X" 表示端口不通 -> 桩 curl 直接失败 local stub1="$c1" stub2="$c2" if [ "$c1" = "X" ]; then stub1=""; fi if [ "$c2" = "X" ]; then stub2=""; fi # 用一个特制 curl:端口不通时不输出 cat > /tmp/wdtest/bin/curl < /tmp/wdtest/out.txt 2>&1 # 只看这次运行产生的判断行 grep -E '已建齐|公网|重启|先不重启|DRY_RUN|继续等' /tmp/wdtest/out.txt | tail -4 | sed 's/^/ /' echo } echo "════════ 1. 双实例都健康 + 公网通 -> 不该有任何动作 ════════" run_case "都健康" 5 5 200 echo "════════ 2. 主实例没连接,副实例正常 + 公网通 ════════" echo " (期望:只重启主实例,因为副实例还在扛)" run_case "主实例 0 条" 0 5 200 echo "════════ 3. 主实例 metrics 端口不通(进程卡死)════════" echo " (期望:重启主实例)" run_case "主实例无响应" X 5 200 echo "════════ 4. 两个实例都 0 条 + 公网不通 ════════" echo " (期望:重启两个)" run_case "双实例全垮" 0 0 000 echo "════════ 5. 连接数正常但公网 502(连续第 1 次)════════" echo " (期望:只记一次,不重启)" run_case "公网 502 首次" 5 5 502 echo "════════ 6. 只有一个实例低连接 + 公网不通 ════════" echo " (期望:重启那个低连接的实例,不动另一个)" run_case "副实例低连接" 5 1 000