Files
uce/scripts/test_cli_worker_isolation.sh
T

154 lines
4.9 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")/.."
if [[ "${1:-}" != "--inside" ]]; then
exec timeout --signal=TERM --kill-after=5s 120s unshare --mount --fork --kill-child=TERM "$0" --inside
fi
root="/tmp/uce-cli-worker-isolation-$$"
site="$root/site"
work="$root/work"
settings="$root/settings.cfg"
log="$root/service.log"
cli_socket="$root/run/cli.sock"
fastcgi_socket="$root/run/fastcgi.sock"
server_pid=""
cleanup() {
status=$?
if [[ -n "$server_pid" ]] && kill -0 "$server_pid" 2>/dev/null; then
kill -TERM "$server_pid" 2>/dev/null || true
deadline=$((SECONDS + 10))
while kill -0 "$server_pid" 2>/dev/null && (( SECONDS < deadline )); do sleep 0.05; done
if kill -0 "$server_pid" 2>/dev/null; then kill -KILL "$server_pid" 2>/dev/null || true; fi
wait "$server_pid" 2>/dev/null || true
fi
if (( status != 0 )) && [[ -r "$log" ]]; then cat "$log" >&2; fi
rm -rf "$root"
return "$status"
}
trap cleanup EXIT
mkdir -p "$site" "$work" "$root/run" "$root/session" "$root/upload"
cp /etc/uce/settings.cfg "$settings"
cat >>"$settings" <<CFG
BIN_DIRECTORY=$work
PRECOMPILE_FILES_IN=$site
SITE_DIRECTORY=$site
FCGI_SOCKET_PATH=$fastcgi_socket
FCGI_PORT=
CLI_SOCKET_PATH=$cli_socket
CLI_WORKER_COUNT=2
CLI_WORKER_MAX_REQUESTS=3
WS_BROKER_SOCKET_PATH=$root/run/ws.sock
HTTP_PORT=
HTTP_DOCUMENT_ROOT=$site
SESSION_PATH=$root/session
TMP_UPLOAD_PATH=$root/upload
WASM_CORE_PATH=$(pwd)/bin/wasm/core.wasm
WORKER_COUNT=2
PROACTIVE_COMPILE_ENABLED=0
CFG
mount --bind "$settings" /etc/uce/settings.cfg
cat >"$site/isolation.uce" <<'UCE'
RENDER(Request& context)
{
print(request_perf()["worker_pid"].to_string());
}
CLI(Request& context)
{
print(request_perf()["worker_pid"].to_string());
}
UCE
cat >"$site/nested.uce" <<UCE
CLI(Request& context)
{
print(shell_exec("cd $(pwd) && timeout 10s scripts/uce-cli --socket $cli_socket /isolation.uce"));
}
UCE
bin/uce_fastcgi.linux.bin >"$log" 2>&1 &
server_pid=$!
deadline=$((SECONDS + 20))
while [[ ! -S "$cli_socket" ]] && (( SECONDS < deadline )); do sleep 0.05; done
[[ -S "$cli_socket" ]] || { echo "private UCE CLI socket was not ready" >&2; exit 1; }
timeout --signal=TERM --kill-after=1s 30s scripts/uce-cli --socket "$cli_socket" /isolation.uce >/dev/null
timeout --signal=TERM --kill-after=1s 30s scripts/uce-cli --socket "$cli_socket" /nested.uce | grep -Eq '^[0-9]+([.]0+)?$'
fastcgi_request() {
SCRIPT_FILENAME="$site/isolation.uce" SCRIPT_NAME=/isolation.uce REQUEST_URI=/isolation.uce REQUEST_METHOD=GET DOCUMENT_ROOT="$site" \
timeout --signal=TERM --kill-after=1s 10s cgi-fcgi -bind -connect "$fastcgi_socket" | tr -d '\r' | awk 'NF{last=$0} END{print last}'
}
fastcgi_request >/dev/null
cli_request() {
local output
local deadline=$((SECONDS + 20))
while (( SECONDS < deadline )); do
if output=$(timeout --signal=TERM --kill-after=1s 10s scripts/uce-cli --socket "$cli_socket" /isolation.uce 2>/dev/null); then
printf '%s\n' "$output"
return 0
fi
sleep 0.05
done
return 1
}
cli_pids=()
http_pids=()
# Concurrent completions must count individually and retire both initial CLI
# workers despite neither connection pool necessarily becoming idle first.
concurrent_jobs=()
for n in $(seq 1 12); do
cli_request >"$root/concurrent-$n" & concurrent_jobs+=("$!")
done
for job in "${concurrent_jobs[@]}"; do wait "$job"; done
mapfile -t concurrent_cli < <(cat "$root"/concurrent-* | sort -u)
cli_pids+=("${concurrent_cli[@]}")
for _ in $(seq 1 12); do
cli_pids+=("$(cli_request)")
http_pids+=("$(fastcgi_request)")
done
mapfile -t unique_cli < <(printf '%s\n' "${cli_pids[@]}" | sort -u)
mapfile -t unique_http < <(printf '%s\n' "${http_pids[@]}" | sort -u)
[[ ${#unique_cli[@]} -ge 4 ]] || { echo "CLI workers did not recycle after three requests: ${unique_cli[*]}" >&2; exit 1; }
[[ ${#unique_http[@]} -ge 1 ]] || { echo "HTTP requests reached no public worker" >&2; exit 1; }
for cli_pid in "${unique_cli[@]}"; do
if printf '%s\n' "${unique_http[@]}" | grep -Fxq "$cli_pid"; then
echo "CLI worker also served public HTTP: $cli_pid" >&2
exit 1
fi
done
[[ $(grep -c 'wasm FastCGI worker ready' "$log") -eq 2 ]]
[[ $(grep -c 'wasm CLI worker ready' "$log") -ge 4 ]]
echo "CLI worker isolation and recycling passed: CLI ${unique_cli[*]}, public ${unique_http[*]}"
# A CLI-only runtime must not allocate a listener-less public Wasmtime worker.
kill -TERM "$server_pid"
wait "$server_pid"
server_pid=""
rm -f "$cli_socket" "$fastcgi_socket"
cat >>"$settings" <<CFG
FCGI_SOCKET_PATH=
CLI_WORKER_COUNT=1
CLI_WORKER_MAX_REQUESTS=0
CFG
: >"$log"
bin/uce_fastcgi.linux.bin >"$log" 2>&1 &
server_pid=$!
deadline=$((SECONDS + 20))
while [[ ! -S "$cli_socket" ]] && (( SECONDS < deadline )); do sleep 0.05; done
[[ -S "$cli_socket" ]] || { echo "CLI-only socket was not ready" >&2; exit 1; }
cli_request >/dev/null
[[ $(grep -c 'wasm CLI worker ready' "$log") -eq 1 ]]
[[ $(grep -c 'wasm FastCGI worker ready' "$log") -eq 0 ]]
echo "CLI-only worker allocation passed"