who_need_help/scripts/load-stack-up.sh

154 lines
4.8 KiB
Bash
Executable File

#!/bin/sh
set -eu
ROOT=$(CDPATH='' cd -- "$(dirname -- "$0")/.." && pwd)
ENV_FILE=${WNH_LOAD_ENV_FILE:-"$ROOT/output/runtime/load.env"}
LOAD_TOOLS_IMAGE=${WNH_LOAD_TOOLS_IMAGE:-who-need-help:load-tools}
PYTHON_RUNTIME_IMAGE=${WNH_PYTHON_RUNTIME_IMAGE:-who-need-help:python-runtime}
REPLICAS=${1:-}
"$ROOT/scripts/ensure-local-load-env.sh"
set -a
# shellcheck source=/dev/null
. "$ENV_FILE"
set +a
: "${LOAD_PROJECT:?LOAD_PROJECT is missing from the generated load runtime environment}"
: "${LOAD_HOST:?LOAD_HOST is missing from the generated load runtime environment}"
: "${LOAD_WEB_REPLICAS:?LOAD_WEB_REPLICAS is missing from the generated load runtime environment}"
: "${LOAD_WORKER_REPLICAS:?LOAD_WORKER_REPLICAS is missing from the generated load runtime environment}"
: "${LOAD_RESILIENCE_RECOVERY_TIMEOUT_SECONDS:?LOAD_RESILIENCE_RECOVERY_TIMEOUT_SECONDS is missing from the generated load runtime environment}"
: "${LOAD_RESILIENCE_PROBE_INTERVAL_SECONDS:?LOAD_RESILIENCE_PROBE_INTERVAL_SECONDS is missing from the generated load runtime environment}"
if [ "$LOAD_PROJECT" = who_need_help ]; then
echo "The load profile must not use the staging Compose project." >&2
exit 1
fi
if [ -n "$REPLICAS" ]; then
LOAD_WEB_REPLICAS=$REPLICAS
fi
case "$LOAD_WEB_REPLICAS:$LOAD_WORKER_REPLICAS" in
*[!0-9:]* | 0:* | *:0 | :* | *:)
echo "Web and worker replica counts must be positive integers." >&2
exit 1
;;
esac
cd "$ROOT"
compose() {
if [ "${WNH_LOAD_CPU_REPLAY:-false}" = true ]; then
docker compose \
--env-file "$ENV_FILE" \
-p "$LOAD_PROJECT" \
-f compose.yaml \
-f compose.load.yaml \
-f compose.cpu-replay.yaml \
"$@"
else
docker compose \
--env-file "$ENV_FILE" \
-p "$LOAD_PROJECT" \
-f compose.yaml \
-f compose.load.yaml \
"$@"
fi
}
cleanup_failed_start() {
status=$?
trap - EXIT HUP INT TERM
if [ "$status" -ne 0 ]; then
# A cancelled Docker API request can finish creating a network just after
# the first `compose down`. Repeat the exact project-scoped teardown until
# the project has no containers or networks, or the bounded retry expires.
attempt=0
while [ "$attempt" -lt 5 ]; do
compose down --remove-orphans >/dev/null 2>&1 || true
remaining_containers=$(docker ps -aq \
--filter "label=com.docker.compose.project=$LOAD_PROJECT")
remaining_networks=$(docker network ls -q \
--filter "label=com.docker.compose.project=$LOAD_PROJECT")
if [ -z "$remaining_containers" ] && [ -z "$remaining_networks" ]; then
break
fi
attempt=$((attempt + 1))
sleep 1
done
if image_id=$(docker image inspect --format '{{.Id}}' "$LOAD_TOOLS_IMAGE" 2>/dev/null); then
if [ -z "$(docker ps -aq --filter "ancestor=$image_id")" ]; then
docker image rm "$LOAD_TOOLS_IMAGE" >/dev/null 2>&1 || true
fi
fi
if image_id=$(docker image inspect --format '{{.Id}}' "$PYTHON_RUNTIME_IMAGE" 2>/dev/null); then
if [ -z "$(docker ps -aq --filter "ancestor=$image_id")" ]; then
docker image rm "$PYTHON_RUNTIME_IMAGE" >/dev/null 2>&1 || true
fi
fi
if [ -n "$(docker ps -aq --filter "label=com.docker.compose.project=$LOAD_PROJECT")" ] || \
[ -n "$(docker network ls -q --filter "label=com.docker.compose.project=$LOAD_PROJECT")" ]; then
echo "Load-profile startup cleanup left project resources behind." >&2
status=1
fi
fi
exit "$status"
}
trap cleanup_failed_start EXIT HUP INT TERM
docker build --target load_tools --tag "$LOAD_TOOLS_IMAGE" .
docker build \
--target python_runtime \
--tag "$PYTHON_RUNTIME_IMAGE" \
--file ops/external-boundaries/Dockerfile \
ops/external-boundaries
compose up -d --build --wait \
--scale "web=$LOAD_WEB_REPLICAS" \
--scale "worker=$LOAD_WORKER_REPLICAS"
COMPOSE_PROJECT_NAME=$LOAD_PROJECT "$ROOT/scripts/verify-realtime-cluster.sh" compose
COMPOSE_PROJECT_NAME=$LOAD_PROJECT "$ROOT/scripts/verify-beam-runtime.sh" compose
deadline=$(( $(date +%s) + LOAD_RESILIENCE_RECOVERY_TIMEOUT_SECONDS ))
status=000
while [ "$(date +%s)" -le "$deadline" ]; do
if ! status=$(
curl --silent \
--header "Host: $LOAD_HOST" \
--output /dev/null \
--write-out '%{http_code}' \
"http://localhost:$HTTP_PORT/healthz/ready"
); then
status=000
fi
if [ "$status" = 200 ]; then
break
fi
sleep "$LOAD_RESILIENCE_PROBE_INTERVAL_SECONDS"
done
if [ "$status" != 200 ]; then
echo "Load-profile readiness returned HTTP $status instead of 200." >&2
exit 1
fi
curl --fail --silent --show-error \
--header "Host: $LOAD_HOST" \
"http://localhost:$HTTP_PORT/healthz/ready"
printf '\nLoad profile is ready with %s web and %s worker replicas.\n' \
"$LOAD_WEB_REPLICAS" "$LOAD_WORKER_REPLICAS"
trap - EXIT HUP INT TERM