Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions docs/runbooks/scenario-runner.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Duckgres Scenario Runner

## Scope

The scenario runner executes end-to-end managed-warehouse flows against a configured dev environment. The first smoke scenario provisions a warehouse, waits for readiness, runs `SELECT 1` over PGWire with managed-hostname SNI, then deprovisions and verifies cleanup.

## Required Environment

Set these before running a real scenario:

```bash
export DUCKGRES_SCENARIO_API_BASE="<control-plane-api-base-url>"
export DUCKGRES_SCENARIO_INTERNAL_SECRET="<internal-secret>"
export DUCKGRES_SCENARIO_PG_HOST="<pgwire-direct-address-for-libpq-hostaddr>"
export DUCKGRES_SCENARIO_SNI_SUFFIX="<managed-hostname-suffix>"
```

`DUCKGRES_SCENARIO_PG_HOST` is used as libpq `hostaddr`; the runner separately sets `host=<org><suffix>` so TLS SNI carries the managed warehouse identity.

Optional:

```bash
export DUCKGRES_SCENARIO_OUTPUT_BASE="artifacts/scenario"
export DUCKGRES_SCENARIO_RUN_ID="scenario-smoke-manual"
export DUCKGRES_SCENARIO_PG_PORT="5432"
export DUCKGRES_SCENARIO_PG_CONNECT_TIMEOUT="10"
export DUCKGRES_SCENARIO_MAX_RUNTIME="30m"
export DUCKGRES_SCENARIO_GO_TEST_TIMEOUT="60m"
```

Do not commit concrete dev endpoints, secrets, org IDs, or private bucket names.

## Run

Validate configuration without running:

```bash
./scripts/scenario_run.sh --check-env
```

Run the dev smoke:

```bash
just scenario-smoke
```

Run a specific scenario file:

```bash
just scenario scenario=tests/scenario/scenarios/provision_smoke.yaml
```

Artifacts are written under `artifacts/scenario/<run_id>/`.

## Leaked Dev Warehouse Recovery

The smoke scenario has an `always_run` deprovision step, but an interrupted process can still leave dev resources behind. To clean up:

1. Identify the scenario org ID from the scenario file and artifact directory.
2. Call the control-plane deprovision endpoint with the internal secret.
3. Poll `/warehouse/status` until the state is `deleted` or the warehouse returns `404`.
4. If deletion does not complete, inspect the dev control-plane logs and the managed warehouse deprovision runbook.

Use placeholders in shared notes and PRs; keep concrete dev values local.
18 changes: 17 additions & 1 deletion justfile
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,7 @@ format:
[group('test')]
test:
just test-unit
just test-scenario
just test-integration
just test-controlplane
just test-configstore-integration
Expand All @@ -262,6 +263,11 @@ test:
test-unit:
go test -v -p 1 . ./configresolve/... ./duckdbservice/... ./server/... ./transpiler/... ./internal/... ./tests/manifests/...

# Run scenario runner unit tests
[group('test')]
test-scenario:
go test -v -count=1 ./tests/scenario/...

# Run cache-proxy tests
[group('test')]
test-cache-proxy:
Expand Down Expand Up @@ -358,14 +364,24 @@ perf-smoke:
perf-nightly:
./scripts/perf_nightly.sh

# Run a Duckgres scenario file against a configured dev environment
[group('test')]
scenario scenario="tests/scenario/scenarios/provision_smoke.yaml":
./scripts/scenario_run.sh {{scenario}}

# Run the dev provision smoke scenario
[group('test')]
scenario-smoke:
./scripts/scenario_run.sh tests/scenario/scenarios/provision_smoke.yaml

# Lint (matches CI — uses golangci-lint, not go vet)
[group('test')]
lint:
golangci-lint run

# Run what CI runs locally (excluding kind-backed K8s integration)
[group('test')]
ci: lint test-unit test-cache-proxy test-integration test-controlplane test-configstore-integration test-controlplane-k8s
ci: lint test-unit test-scenario test-cache-proxy test-integration test-controlplane test-configstore-integration test-controlplane-k8s

# === Metrics ===

Expand Down
106 changes: 106 additions & 0 deletions scripts/scenario_run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
#!/usr/bin/env bash
set -euo pipefail

usage() {
cat <<'USAGE'
Usage: scripts/scenario_run.sh [--check-env] [--scenario-file PATH] [--output-base DIR] [--run-id ID] [PATH]

Runs a Duckgres scenario through the Go test entry point.

Required environment:
DUCKGRES_SCENARIO_API_BASE
DUCKGRES_SCENARIO_INTERNAL_SECRET
DUCKGRES_SCENARIO_PG_HOST (libpq hostaddr / direct TCP address)
DUCKGRES_SCENARIO_SNI_SUFFIX

Optional environment:
DUCKGRES_SCENARIO_OUTPUT_BASE
DUCKGRES_SCENARIO_RUN_ID
DUCKGRES_SCENARIO_PG_PORT
DUCKGRES_SCENARIO_PG_CONNECT_TIMEOUT
DUCKGRES_SCENARIO_MAX_RUNTIME
DUCKGRES_SCENARIO_GO_TEST_TIMEOUT
USAGE
}

scenario_file="${DUCKGRES_SCENARIO_FILE:-tests/scenario/scenarios/provision_smoke.yaml}"
output_base="${DUCKGRES_SCENARIO_OUTPUT_BASE:-artifacts/scenario}"
run_id="${DUCKGRES_SCENARIO_RUN_ID:-}"
max_runtime="${DUCKGRES_SCENARIO_MAX_RUNTIME:-30m}"
go_test_timeout="${DUCKGRES_SCENARIO_GO_TEST_TIMEOUT:-60m}"
check_env_only=0

while [ "$#" -gt 0 ]; do
case "$1" in
--check-env)
check_env_only=1
shift
;;
--scenario-file)
scenario_file="${2:?--scenario-file requires a path}"
shift 2
;;
--output-base)
output_base="${2:?--output-base requires a directory}"
shift 2
;;
--run-id)
run_id="${2:?--run-id requires an id}"
shift 2
;;
-h|--help)
usage
exit 0
;;
--*)
echo "Unknown option: $1" >&2
usage >&2
exit 2
;;
*)
scenario_file="$1"
shift
;;
esac
done

required=(
DUCKGRES_SCENARIO_API_BASE
DUCKGRES_SCENARIO_INTERNAL_SECRET
DUCKGRES_SCENARIO_PG_HOST
DUCKGRES_SCENARIO_SNI_SUFFIX
)
missing=()
for name in "${required[@]}"; do
if [ -z "${!name:-}" ]; then
missing+=("$name")
fi
done

if [ "${#missing[@]}" -ne 0 ]; then
echo "Missing required Duckgres scenario environment:" >&2
for name in "${missing[@]}"; do
echo " - $name" >&2
done
exit 2
fi

if [ "$check_env_only" -eq 1 ]; then
echo "Duckgres scenario environment is configured."
exit 0
fi

args=(
go test -count=1 ./tests/scenario
-timeout "$go_test_timeout"
-run TestScenarioRunner
-scenario-run
-scenario-file "$scenario_file"
-scenario-output-base "$output_base"
-scenario-max-runtime "$max_runtime"
)
if [ -n "$run_id" ]; then
args+=(-scenario-run-id "$run_id")
fi

"${args[@]}"
Loading
Loading