burrow/Scripts/run-tailnet-connectivity-smoke.sh
2026-04-03 17:49:11 -07:00

186 lines
5.7 KiB
Bash
Executable file

#!/usr/bin/env bash
set -euo pipefail
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
bundle_id="${BURROW_UI_TEST_APP_BUNDLE_ID:-com.hackclub.burrow}"
smoke_root="${BURROW_TAILNET_SMOKE_ROOT:-/tmp/burrow-tailnet-connectivity}"
socket_path="${smoke_root}/burrow.sock"
db_path="${smoke_root}/burrow.db"
daemon_log="${BURROW_TAILNET_SMOKE_DAEMON_LOG:-${smoke_root}/daemon.log}"
payload_path="${smoke_root}/tailnet.json"
authority="${BURROW_TAILNET_SMOKE_AUTHORITY:-https://ts.burrow.net}"
account_name="${BURROW_TAILNET_SMOKE_ACCOUNT:-ui-test}"
identity_name="${BURROW_TAILNET_SMOKE_IDENTITY:-apple}"
hostname="${BURROW_TAILNET_SMOKE_HOSTNAME:-burrow-apple}"
message="${BURROW_TAILNET_SMOKE_MESSAGE:-burrow-tailnet-smoke}"
timeout_ms="${BURROW_TAILNET_SMOKE_TIMEOUT_MS:-8000}"
remote_ip="${BURROW_TAILNET_SMOKE_REMOTE_IP:-}"
remote_port="${BURROW_TAILNET_SMOKE_REMOTE_PORT:-18081}"
remote_hostname="${BURROW_TAILNET_SMOKE_REMOTE_HOSTNAME:-burrow-echo}"
remote_authkey="${BURROW_TAILNET_SMOKE_REMOTE_AUTHKEY:-}"
helper_bin="${BURROW_TAILNET_SMOKE_HELPER_BIN:-${smoke_root}/tailscale-login-bridge}"
remote_state_root="${BURROW_TAILNET_SMOKE_REMOTE_STATE_ROOT:-${smoke_root}/remote-state}"
remote_stdout="${smoke_root}/remote-helper.stdout"
remote_stderr="${BURROW_TAILNET_SMOKE_REMOTE_LOG:-${smoke_root}/remote-helper.log}"
if [[ -n "${TS_AUTHKEY:-}" ]]; then
default_tailnet_state_root="${smoke_root}/local-state"
else
default_tailnet_state_root="/tmp/${bundle_id}/SimulatorTailnetState"
fi
tailnet_state_root="${BURROW_TAILNET_STATE_ROOT:-${default_tailnet_state_root}}"
need_login=0
if [[ -z "${TS_AUTHKEY:-}" ]] && { [[ ! -d "$tailnet_state_root" ]] || [[ -z "$(find "$tailnet_state_root" -mindepth 1 -maxdepth 2 -print -quit 2>/dev/null)" ]]; }; then
need_login=1
fi
if [[ "$need_login" -eq 1 ]]; then
echo "Tailnet state root is empty; running iOS login bootstrap first..."
"${repo_root}/Scripts/run-ios-tailnet-ui-tests.sh"
fi
rm -rf "$smoke_root"
mkdir -p "$smoke_root"
cleanup() {
rm -f "$payload_path"
if [[ -n "${daemon_pid:-}" ]]; then
kill "$daemon_pid" >/dev/null 2>&1 || true
wait "$daemon_pid" >/dev/null 2>&1 || true
fi
if [[ -n "${remote_pid:-}" ]]; then
kill "$remote_pid" >/dev/null 2>&1 || true
wait "$remote_pid" >/dev/null 2>&1 || true
fi
}
trap cleanup EXIT
wait_for_helper_listen() {
python3 - <<'PY' "$1"
import json
import pathlib
import sys
import time
path = pathlib.Path(sys.argv[1])
deadline = time.time() + 20
while time.time() < deadline:
if path.exists():
with path.open("r", encoding="utf-8") as handle:
line = handle.readline().strip()
if line:
hello = json.loads(line)
print(hello["listen_addr"])
raise SystemExit(0)
time.sleep(0.1)
raise SystemExit("timed out waiting for helper startup line")
PY
}
wait_for_helper_ip() {
python3 - <<'PY' "$1"
import json
import sys
import time
import urllib.request
url = sys.argv[1]
deadline = time.time() + 30
while time.time() < deadline:
with urllib.request.urlopen(url, timeout=5) as response:
status = json.load(response)
if status.get("running") and status.get("tailscale_ips"):
print(status["tailscale_ips"][0])
raise SystemExit(0)
time.sleep(0.25)
raise SystemExit("timed out waiting for helper to become ready")
PY
}
python3 - <<'PY' "$payload_path" "$authority" "$account_name" "$identity_name" "$hostname"
import json
import pathlib
import sys
path = pathlib.Path(sys.argv[1])
payload = {
"authority": sys.argv[2],
"account": sys.argv[3],
"identity": sys.argv[4],
"hostname": sys.argv[5],
}
path.write_text(json.dumps(payload, indent=2) + "\n", encoding="utf-8")
PY
cargo build -p burrow --bin burrow
(
cd "${repo_root}/Tools/tailscale-login-bridge"
GOWORK=off go build -o "$helper_bin" .
)
if [[ -z "$remote_ip" ]]; then
if [[ -z "$remote_authkey" ]] && { [[ ! -d "$remote_state_root" ]] || [[ -z "$(find "$remote_state_root" -mindepth 1 -maxdepth 1 -print -quit 2>/dev/null)" ]]; }; then
echo "error: set BURROW_TAILNET_SMOKE_REMOTE_IP, BURROW_TAILNET_SMOKE_REMOTE_AUTHKEY, or BURROW_TAILNET_SMOKE_REMOTE_STATE_ROOT to an existing logged-in helper state" >&2
exit 1
fi
if [[ -n "$remote_authkey" ]]; then
rm -rf "$remote_state_root"
mkdir -p "$remote_state_root"
fi
(
cd "$repo_root"
if [[ -n "$remote_authkey" ]]; then
export TS_AUTHKEY="$remote_authkey"
fi
"$helper_bin" \
--listen 127.0.0.1:0 \
--state-dir "$remote_state_root" \
--hostname "$remote_hostname" \
--control-url "$authority" \
--udp-echo-port "$remote_port" \
>"$remote_stdout" 2>"$remote_stderr"
) &
remote_pid=$!
remote_listen_addr="$(wait_for_helper_listen "$remote_stdout")"
remote_ip="$(wait_for_helper_ip "http://${remote_listen_addr}/status")"
fi
(
cd "$smoke_root"
RUST_LOG="${BURROW_TAILNET_SMOKE_RUST_LOG:-info,burrow=debug}" \
BURROW_SOCKET_PATH="$socket_path" \
BURROW_TAILSCALE_STATE_ROOT="$tailnet_state_root" \
"${repo_root}/target/debug/burrow" daemon >"$daemon_log" 2>&1
) &
daemon_pid=$!
for _ in $(seq 1 50); do
[[ -S "$socket_path" ]] && break
sleep 0.2
done
if [[ ! -S "$socket_path" ]]; then
echo "error: Burrow daemon did not create ${socket_path}" >&2
[[ -f "$daemon_log" ]] && cat "$daemon_log" >&2
exit 1
fi
run_burrow() {
BURROW_SOCKET_PATH="$socket_path" \
BURROW_TAILSCALE_STATE_ROOT="$tailnet_state_root" \
"${repo_root}/target/debug/burrow" "$@"
}
run_burrow network-add 1 1 "$payload_path"
run_burrow start
run_burrow tunnel-config
run_burrow tailnet-udp-echo "${remote_ip}:${remote_port}" --message "$message" --timeout-ms "$timeout_ms"
echo
echo "Tailnet connectivity smoke passed."
echo "State root: $tailnet_state_root"
echo "Remote: ${remote_ip}:${remote_port}"