255 lines
7.8 KiB
Bash
Executable File
255 lines
7.8 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
# shellcheck source=scripts/local-debug/env.sh
|
|
source "$ROOT_DIR/scripts/local-debug/env.sh"
|
|
|
|
mkdir -p "$LOCAL_DEBUG_LOG_DIR" "$LOCAL_DEBUG_PID_DIR" "$PLATFORM_DATA_DIR" "$PLATFORM_LOG_DIR" "$RUN_WORKSPACE_ROOT" "$RUN_SPOOL_ROOT" "$RUN_BUILD_BUCKET_ROOT"
|
|
mkdir -p "$GOCACHE"
|
|
local_debug_prepare_run_lifecycle_templates
|
|
|
|
managed_pid_running() {
|
|
local pid_file="$1"
|
|
if [[ ! -f "$pid_file" ]]; then
|
|
return 1
|
|
fi
|
|
local pid
|
|
pid="$(cat "$pid_file")"
|
|
[[ -n "$pid" ]] && kill -0 "$pid" 2>/dev/null
|
|
}
|
|
|
|
pid_is_descendant_of() {
|
|
local pid="$1"
|
|
local ancestor="$2"
|
|
local parent
|
|
|
|
while [[ "$pid" =~ ^[0-9]+$ && "$pid" -gt 1 ]]; do
|
|
if [[ "$pid" == "$ancestor" ]]; then
|
|
return 0
|
|
fi
|
|
parent="$(ps -o ppid= -p "$pid" 2>/dev/null | tr -d '[:space:]')"
|
|
[[ -n "$parent" && "$parent" != "$pid" ]] || return 1
|
|
pid="$parent"
|
|
done
|
|
|
|
return 1
|
|
}
|
|
|
|
port_listener_pid() {
|
|
local port="$1"
|
|
lsof -tiTCP:"$port" -sTCP:LISTEN 2>/dev/null | head -n 1 || true
|
|
}
|
|
|
|
listener_owned_by_local_debug() {
|
|
local pid="$1"
|
|
local details
|
|
details="$(lsof -nP -p "$pid" 2>/dev/null || true)"
|
|
[[ "$details" == *"$ROOT_DIR"* || "$details" == *"$LOCAL_DEBUG_ROOT"* ]]
|
|
}
|
|
|
|
wait_for_pid_exit() {
|
|
local pid="$1"
|
|
for _ in $(seq 1 20); do
|
|
if ! kill -0 "$pid" 2>/dev/null; then
|
|
return 0
|
|
fi
|
|
sleep 0.25
|
|
done
|
|
return 1
|
|
}
|
|
|
|
ensure_port_available() {
|
|
local name="$1"
|
|
local port="$2"
|
|
local pid_file="$LOCAL_DEBUG_PID_DIR/$name.pid"
|
|
local listener_pid
|
|
listener_pid="$(port_listener_pid "$port")"
|
|
if [[ -z "$listener_pid" ]]; then
|
|
return 0
|
|
fi
|
|
if managed_pid_running "$pid_file"; then
|
|
local managed_pid
|
|
managed_pid="$(cat "$pid_file")"
|
|
if pid_is_descendant_of "$listener_pid" "$managed_pid"; then
|
|
return 0
|
|
fi
|
|
fi
|
|
if listener_owned_by_local_debug "$listener_pid"; then
|
|
printf 'stopping stale %s listener pid %s on port %s\n' "$name" "$listener_pid" "$port"
|
|
kill "$listener_pid" 2>/dev/null || true
|
|
if ! wait_for_pid_exit "$listener_pid"; then
|
|
printf 'stale %s listener pid %s did not stop after SIGTERM; sending SIGKILL\n' "$name" "$listener_pid"
|
|
kill -9 "$listener_pid" 2>/dev/null || true
|
|
wait_for_pid_exit "$listener_pid" || true
|
|
fi
|
|
rm -f "$pid_file"
|
|
return 0
|
|
fi
|
|
printf '%s port %s is already used by pid %s; choose another LOCAL_DEBUG_*_PORT or stop the existing listener\n' "$name" "$port" "$listener_pid" >&2
|
|
return 1
|
|
}
|
|
|
|
start_service() {
|
|
local name="$1"
|
|
local service_dir="$2"
|
|
local pid_file="$LOCAL_DEBUG_PID_DIR/$name.pid"
|
|
shift 2
|
|
|
|
if [[ -f "$pid_file" ]]; then
|
|
local existing_pid
|
|
existing_pid="$(cat "$pid_file")"
|
|
if kill -0 "$existing_pid" 2>/dev/null; then
|
|
printf '%s already running with pid %s\n' "$name" "$existing_pid"
|
|
return 0
|
|
fi
|
|
rm -f "$pid_file"
|
|
fi
|
|
|
|
printf 'starting %s\n' "$name"
|
|
(
|
|
cd "$service_dir"
|
|
exec nohup "$@" >"$LOCAL_DEBUG_LOG_DIR/$name.log" 2>&1
|
|
) >/dev/null 2>&1 &
|
|
printf '%s' "$!" >"$pid_file"
|
|
printf '%s pid %s log %s\n' "$name" "$(cat "$pid_file")" "$LOCAL_DEBUG_LOG_DIR/$name.log"
|
|
}
|
|
|
|
wait_for_url() {
|
|
local name="$1"
|
|
local url="$2"
|
|
local attempts="${3:-30}"
|
|
|
|
printf 'waiting for %s at %s\n' "$name" "$url"
|
|
for _ in $(seq 1 "$attempts"); do
|
|
if curl -fsS "$url" >/dev/null 2>&1; then
|
|
printf '%s is ready\n' "$name"
|
|
return 0
|
|
fi
|
|
sleep 1
|
|
done
|
|
|
|
printf '%s did not become ready at %s\n' "$name" "$url" >&2
|
|
return 1
|
|
}
|
|
|
|
wait_for_run_registration() {
|
|
local attempts="${1:-45}"
|
|
local run_pid_file="$LOCAL_DEBUG_PID_DIR/run.pid"
|
|
printf 'waiting for run endpoint %s registration\n' "$RUN_ENDPOINT_ID"
|
|
for _ in $(seq 1 "$attempts"); do
|
|
if ! managed_pid_running "$run_pid_file"; then
|
|
printf 'run exited before endpoint registration; see %s\n' "$LOCAL_DEBUG_LOG_DIR/run.log" >&2
|
|
return 1
|
|
fi
|
|
if run_endpoint_has_recent_heartbeat; then
|
|
printf 'run endpoint %s is registered\n' "$RUN_ENDPOINT_ID"
|
|
return 0
|
|
fi
|
|
sleep 1
|
|
done
|
|
printf 'run endpoint %s did not register before timeout\n' "$RUN_ENDPOINT_ID" >&2
|
|
return 1
|
|
}
|
|
|
|
run_endpoint_has_recent_heartbeat() {
|
|
[[ -f "$PLATFORM_METADATA_PATH" ]] || return 1
|
|
node -e '
|
|
const fs = require("fs");
|
|
const metadata = JSON.parse(fs.readFileSync(process.argv[1], "utf8"));
|
|
const endpointID = process.argv[2];
|
|
function findEndpoint(value) {
|
|
if (!value || typeof value !== "object") return null;
|
|
if ((value.ID || value.id) === endpointID && (value.LastHeartbeatAt || value.lastHeartbeatAt)) return value;
|
|
if (Array.isArray(value)) {
|
|
for (const item of value) {
|
|
const found = findEndpoint(item);
|
|
if (found) return found;
|
|
}
|
|
return null;
|
|
}
|
|
for (const item of Object.values(value)) {
|
|
const found = findEndpoint(item);
|
|
if (found) return found;
|
|
}
|
|
return null;
|
|
}
|
|
const endpoint = findEndpoint(metadata);
|
|
const heartbeat = endpoint && (endpoint.LastHeartbeatAt || endpoint.lastHeartbeatAt);
|
|
const ageMilliseconds = heartbeat ? Date.now() - Date.parse(heartbeat) : Number.POSITIVE_INFINITY;
|
|
process.exit(Number.isFinite(ageMilliseconds) && ageMilliseconds >= 0 && ageMilliseconds < 30000 ? 0 : 1);
|
|
' "$PLATFORM_METADATA_PATH" "$RUN_ENDPOINT_ID"
|
|
}
|
|
|
|
printf 'local debug root: %s\n' "$LOCAL_DEBUG_ROOT"
|
|
printf 'platform: %s\n' "$(local_debug_platform_url)"
|
|
printf 'platform_web: %s\n' "$(local_debug_web_url)"
|
|
printf 'run source: %s\n' "$RUN_SOURCE_DIR"
|
|
printf 'run build bucket: %s\n' "$RUN_BUILD_BUCKET_ROOT"
|
|
printf 'run build source snapshot: %s\n' "$RUN_BUILD_SOURCE_ROOT"
|
|
printf 'run bootstrap binary: %s\n' "$RUN_BOOTSTRAP_BIN"
|
|
printf 'platform log: %s\n' "$LOCAL_DEBUG_LOG_DIR/platform.log"
|
|
printf 'run log: %s\n' "$LOCAL_DEBUG_LOG_DIR/run.log"
|
|
printf 'platform_web log: %s\n' "$LOCAL_DEBUG_LOG_DIR/platform_web.log"
|
|
|
|
ensure_port_available platform "$LOCAL_DEBUG_PLATFORM_PORT"
|
|
ensure_port_available platform_web "$LOCAL_DEBUG_WEB_PORT"
|
|
|
|
start_service platform "$ROOT_DIR/platform" env \
|
|
GOCACHE="$GOCACHE" \
|
|
PLATFORM_ADDR="$PLATFORM_ADDR" \
|
|
PLATFORM_STORAGE_BACKEND="$PLATFORM_STORAGE_BACKEND" \
|
|
PLATFORM_DATA_DIR="$PLATFORM_DATA_DIR" \
|
|
PLATFORM_METADATA_PATH="$PLATFORM_METADATA_PATH" \
|
|
PLATFORM_LOG_BODY_BACKEND="$PLATFORM_LOG_BODY_BACKEND" \
|
|
PLATFORM_LOG_DIR="$PLATFORM_LOG_DIR" \
|
|
PLATFORM_BOOTSTRAP_ADMIN_EMAIL="$PLATFORM_BOOTSTRAP_ADMIN_EMAIL" \
|
|
PLATFORM_BOOTSTRAP_ADMIN_PASSWORD="$PLATFORM_BOOTSTRAP_ADMIN_PASSWORD" \
|
|
PLATFORM_SECRET_ENVELOPE_KEY="$PLATFORM_SECRET_ENVELOPE_KEY" \
|
|
go run ./cmd/platform
|
|
|
|
wait_for_url platform "$(local_debug_platform_url)/healthz"
|
|
|
|
start_service platform_web "$ROOT_DIR" env \
|
|
PLATFORM_API_PROXY="$PLATFORM_API_PROXY" \
|
|
VITE_PLATFORM_API_BASE_URL="$VITE_PLATFORM_API_BASE_URL" \
|
|
VITE_ENABLE_LOCAL_AUTH_FALLBACK="$VITE_ENABLE_LOCAL_AUTH_FALLBACK" \
|
|
npm --prefix platform_web run dev -- --host "0.0.0.0" --port "$LOCAL_DEBUG_WEB_PORT"
|
|
|
|
wait_for_url platform_web "$(local_debug_web_url)"
|
|
|
|
local_debug_build_bootstrap_run
|
|
start_service run "$(dirname "$RUN_BOOTSTRAP_BIN")" env \
|
|
GOCACHE="$GOCACHE" \
|
|
RUN_MODE="$RUN_MODE" \
|
|
RUN_PLATFORM_URL="$RUN_PLATFORM_URL" \
|
|
RUN_ENDPOINT_ID="$RUN_ENDPOINT_ID" \
|
|
RUN_DISPLAY_NAME="$RUN_DISPLAY_NAME" \
|
|
RUN_VERSION="$RUN_VERSION" \
|
|
RUN_REGISTRATION_TOKEN="$RUN_REGISTRATION_TOKEN" \
|
|
RUN_WORKSPACE_ROOT="$RUN_WORKSPACE_ROOT" \
|
|
RUN_BUILD_SOURCE_ROOT="$RUN_BUILD_SOURCE_ROOT" \
|
|
RUN_SPOOL_ROOT="$RUN_SPOOL_ROOT" \
|
|
RUN_MAX_JOBS="$RUN_MAX_JOBS" \
|
|
RUN_HEARTBEAT_INTERVAL_MS="$RUN_HEARTBEAT_INTERVAL_MS" \
|
|
RUN_POLL_INTERVAL_MS="$RUN_POLL_INTERVAL_MS" \
|
|
RUN_RETRY_BACKOFF_MS="$RUN_RETRY_BACKOFF_MS" \
|
|
"$RUN_BOOTSTRAP_BIN"
|
|
|
|
wait_for_run_registration
|
|
|
|
cat <<EOF
|
|
|
|
Local debug stack requested.
|
|
|
|
Login with:
|
|
account: operator.local@example.test
|
|
password: operator-local
|
|
|
|
Run smoke verification:
|
|
scripts/dev-smoke.sh
|
|
|
|
Open local console:
|
|
$(local_debug_web_url)
|
|
EOF
|