Files
HiveGrid-Wireframes/hivegrid-cloud/scripts/docker-worker-runtime-smoke.sh
T

191 lines
6.2 KiB
Bash

#!/usr/bin/env bash
set -euo pipefail
IMAGE="${1:-toolctrl-worker:smoke}"
CONTAINER_NAME="${DOCKER_WORKER_SMOKE_CONTAINER:-toolctrl-worker-runtime-smoke-$$}"
DB_CONTAINER_NAME="${DOCKER_WORKER_SMOKE_DB_CONTAINER:-toolctrl-worker-runtime-smoke-db-$$}"
REDIS_CONTAINER_NAME="${DOCKER_WORKER_SMOKE_REDIS_CONTAINER:-toolctrl-worker-runtime-smoke-redis-$$}"
NETWORK_NAME="${DOCKER_WORKER_SMOKE_NETWORK:-toolctrl-worker-runtime-smoke-$$}"
SMOKE_REVISION="${DOCKER_WORKER_SMOKE_REVISION:-}"
DB_PASSWORD="toolctrl"
DB_NAME="toolctrl"
DB_USER="toolctrl"
cleanup() {
docker rm -f "$CONTAINER_NAME" >/dev/null 2>&1 || true
docker rm -f "$DB_CONTAINER_NAME" >/dev/null 2>&1 || true
docker rm -f "$REDIS_CONTAINER_NAME" >/dev/null 2>&1 || true
docker network rm "$NETWORK_NAME" >/dev/null 2>&1 || true
}
resolve_smoke_revision() {
if [[ -n "$SMOKE_REVISION" ]]; then
printf "%s" "$SMOKE_REVISION"
return
fi
docker image inspect -f '{{ index .Config.Labels "org.opencontainers.image.revision" }}' "$IMAGE"
}
trap cleanup EXIT
cleanup
SMOKE_REVISION="$(resolve_smoke_revision)"
if [[ -z "$SMOKE_REVISION" || "$SMOKE_REVISION" == "<no value>" || "$SMOKE_REVISION" == "unknown" ]]; then
echo "Image ${IMAGE} does not expose a concrete org.opencontainers.image.revision; build with --build-arg APP_REVISION or set DOCKER_WORKER_SMOKE_REVISION explicitly" >&2
exit 1
fi
worker_env=(
-e NODE_ENV=production
-e APP_NAME=ToolCTRL
-e APP_BASE_DOMAIN=localhost
)
expect_worker_health() {
local output="$1"
local expected_status="$2"
local expected_enabled="$3"
local expected_group="${4:-}"
local expected_queue="${5:-}"
node -e '
const [raw, expectedStatus, expectedEnabled, expectedRevision, expectedGroup, expectedQueue] = process.argv.slice(1);
const report = JSON.parse(raw);
if (report.status !== expectedStatus) {
console.error(`Expected worker status ${expectedStatus}, got ${report.status}`);
process.exit(1);
}
if (String(report.workerEnabled) !== expectedEnabled) {
console.error(`Expected workerEnabled ${expectedEnabled}, got ${report.workerEnabled}`);
process.exit(1);
}
if (report.revision !== expectedRevision) {
console.error(`Expected revision ${expectedRevision}, got ${report.revision || "<empty>"}`);
process.exit(1);
}
if (expectedGroup && report.workerGroup !== expectedGroup) {
console.error(`Expected workerGroup ${expectedGroup}, got ${report.workerGroup || "<empty>"}`);
process.exit(1);
}
if (expectedQueue && !Array.isArray(report.queues)) {
console.error("Expected worker health queues to be an array");
process.exit(1);
}
if (expectedQueue && !report.queues.includes(expectedQueue)) {
console.error(`Expected worker queues to include ${expectedQueue}, got ${report.queues?.join(",") || "<empty>"}`);
process.exit(1);
}
if (expectedStatus === "ready" && (report.databaseStatus !== "ready" || report.redisStatus !== "ready")) {
console.error(`Expected ready DB/Redis, got database=${report.databaseStatus} redis=${report.redisStatus}`);
process.exit(1);
}
' "$output" "$expected_status" "$expected_enabled" "$SMOKE_REVISION" "$expected_group" "$expected_queue"
}
docker network create "$NETWORK_NAME" >/dev/null
health_output="$(
docker run --rm \
"${worker_env[@]}" \
-e WORKER_ENABLED=false \
"$IMAGE" node apps/worker/dist/health.js
)"
expect_worker_health "$health_output" disabled false
docker run -d --name "$DB_CONTAINER_NAME" --network "$NETWORK_NAME" --network-alias db \
-e POSTGRES_USER="$DB_USER" \
-e POSTGRES_PASSWORD="$DB_PASSWORD" \
-e POSTGRES_DB="$DB_NAME" \
postgres:16-alpine >/dev/null
docker run -d --name "$REDIS_CONTAINER_NAME" --network "$NETWORK_NAME" --network-alias redis redis:7-alpine >/dev/null
CONTAINER_DATABASE_URL="postgresql://${DB_USER}:${DB_PASSWORD}@db:5432/${DB_NAME}?schema=public"
CONTAINER_REDIS_URL="redis://redis:6379/0"
for attempt in $(seq 1 30); do
if docker exec "$DB_CONTAINER_NAME" pg_isready -U "$DB_USER" -d "$DB_NAME" >/dev/null 2>&1; then
break
fi
if [[ "$attempt" == "30" ]]; then
echo "Postgres smoke database did not become ready" >&2
docker logs "$DB_CONTAINER_NAME" >&2 || true
exit 1
fi
sleep 1
done
for attempt in $(seq 1 30); do
if docker exec "$REDIS_CONTAINER_NAME" redis-cli ping >/dev/null 2>&1; then
break
fi
if [[ "$attempt" == "30" ]]; then
echo "Redis smoke broker did not become ready" >&2
docker logs "$REDIS_CONTAINER_NAME" >&2 || true
exit 1
fi
sleep 1
done
docker run --rm --network "$NETWORK_NAME" \
-e NODE_ENV=production \
-e DATABASE_URL="$CONTAINER_DATABASE_URL" \
--entrypoint /bin/sh \
"$IMAGE" -lc 'cd /myapp && pnpm exec prisma migrate deploy --config prisma.config.ts' >/dev/null
enabled_health_output="$(
docker run --rm --network "$NETWORK_NAME" \
"${worker_env[@]}" \
-e WORKER_ENABLED=true \
-e WORKER_QUEUE_GROUP=general \
-e DATABASE_URL="$CONTAINER_DATABASE_URL" \
-e BULLMQ_REDIS_URL="$CONTAINER_REDIS_URL" \
"$IMAGE" node apps/worker/dist/health.js
)"
expect_worker_health "$enabled_health_output" ready true general toolctrl.notifications
mail_health_output="$(
docker run --rm --network "$NETWORK_NAME" \
"${worker_env[@]}" \
-e WORKER_ENABLED=true \
-e WORKER_QUEUE_GROUP=mail \
-e DATABASE_URL="$CONTAINER_DATABASE_URL" \
-e BULLMQ_REDIS_URL="$CONTAINER_REDIS_URL" \
"$IMAGE" node apps/worker/dist/health.js
)"
expect_worker_health "$mail_health_output" ready true mail toolctrl.email
docker run -d --name "$CONTAINER_NAME" --network "$NETWORK_NAME" \
"${worker_env[@]}" \
-e WORKER_ENABLED=true \
-e WORKER_QUEUE_GROUP=general \
-e DATABASE_URL="$CONTAINER_DATABASE_URL" \
-e BULLMQ_REDIS_URL="$CONTAINER_REDIS_URL" \
"$IMAGE" >/tmp/toolctrl-worker-runtime-smoke.log
for attempt in $(seq 1 10); do
if [[ "$(docker inspect -f '{{.State.Running}}' "$CONTAINER_NAME" 2>/dev/null || true)" == "true" ]]; then
break
fi
if [[ "$attempt" == "10" ]]; then
echo "Worker container did not stay running" >&2
docker logs "$CONTAINER_NAME" >&2 || true
exit 1
fi
sleep 1
done
running_health_output="$(
docker exec "$CONTAINER_NAME" node apps/worker/dist/health.js
)"
expect_worker_health "$running_health_output" ready true general toolctrl.notifications
echo "Docker worker runtime smoke passed for ${IMAGE}"