154 lines
4.9 KiB
Bash
Executable File
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"
|