Skip to content

Commit 28a9f8c

Browse files
test(e2e): add release stack helper
1 parent 549a75f commit 28a9f8c

3 files changed

Lines changed: 589 additions & 65 deletions

File tree

Lines changed: 385 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,385 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5+
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
6+
STACK_DIR="${RELEASE_STACK_DIR:-/tmp/gomodel-release-stack}"
7+
BIN="${GOMODEL_RELEASE_BINARY:-$REPO_ROOT/bin/gomodel}"
8+
ENV_FILE="${GOMODEL_RELEASE_ENV_FILE:-$REPO_ROOT/.env}"
9+
PG_DATABASE="${GOMODEL_RELEASE_PG_DATABASE:-gomodel_release_e2e}"
10+
MONGO_DATABASE="${GOMODEL_RELEASE_MONGO_DATABASE:-gomodel_release_e2e}"
11+
12+
BUILD_BEFORE_START=0
13+
14+
usage() {
15+
cat <<EOF
16+
Usage: tests/e2e/manage-release-e2e-stack.sh <start|stop|status|logs> [options]
17+
18+
Commands:
19+
start Start the dedicated release E2E stack on ports 18080-18084
20+
stop Stop the dedicated release E2E stack
21+
status Show stack status
22+
logs GATEWAY Show the last 40 log lines for one gateway
23+
24+
Options:
25+
--build Rebuild bin/gomodel before starting
26+
--help Show this help
27+
28+
Gateways:
29+
sqlite-main http://localhost:18080
30+
pg-smoke http://localhost:18081
31+
mongo-smoke http://localhost:18082
32+
guardrails http://localhost:18083
33+
auth-cache http://localhost:18084
34+
EOF
35+
}
36+
37+
die() {
38+
echo "error: $*" >&2
39+
exit 1
40+
}
41+
42+
require_tool() {
43+
command -v "$1" >/dev/null 2>&1 || die "required tool not found: $1"
44+
}
45+
46+
gateway_port() {
47+
case "$1" in
48+
sqlite-main) echo 18080 ;;
49+
pg-smoke) echo 18081 ;;
50+
mongo-smoke) echo 18082 ;;
51+
guardrails) echo 18083 ;;
52+
auth-cache) echo 18084 ;;
53+
*) die "unknown gateway: $1" ;;
54+
esac
55+
}
56+
57+
gateway_dir() {
58+
printf '%s/%s\n' "$STACK_DIR" "$1"
59+
}
60+
61+
gateway_pid_file() {
62+
printf '%s/server.pid\n' "$(gateway_dir "$1")"
63+
}
64+
65+
gateway_log_file() {
66+
printf '%s/logs/server.log\n' "$(gateway_dir "$1")"
67+
}
68+
69+
is_pid_running() {
70+
local pid_file="$1"
71+
local pid=""
72+
73+
[[ -f "$pid_file" ]] || return 1
74+
pid="$(cat "$pid_file" 2>/dev/null || true)"
75+
[[ -n "$pid" ]] || return 1
76+
kill -0 "$pid" 2>/dev/null
77+
}
78+
79+
load_env() {
80+
[[ -r "$ENV_FILE" ]] || die ".env is missing or unreadable at $ENV_FILE"
81+
82+
set -a
83+
source "$ENV_FILE"
84+
set +a
85+
86+
[[ -n "${GOMODEL_MASTER_KEY:-}" ]] || die "GOMODEL_MASTER_KEY must be set in $ENV_FILE"
87+
88+
export REDIS_URL="${REDIS_URL:-redis://localhost:6379}"
89+
}
90+
91+
ensure_binary() {
92+
if (( BUILD_BEFORE_START == 1 )) || [[ ! -x "$BIN" ]]; then
93+
(cd "$REPO_ROOT" && make build)
94+
fi
95+
}
96+
97+
ensure_pg_database() {
98+
psql "postgres://gomodel:gomodel@localhost:5432/postgres?sslmode=disable" \
99+
-v ON_ERROR_STOP=1 \
100+
-tc "SELECT 1 FROM pg_database WHERE datname = '$PG_DATABASE'" \
101+
| grep -q 1 \
102+
|| psql "postgres://gomodel:gomodel@localhost:5432/postgres?sslmode=disable" \
103+
-v ON_ERROR_STOP=1 \
104+
-c "CREATE DATABASE $PG_DATABASE"
105+
}
106+
107+
write_guardrail_config() {
108+
local dir
109+
dir="$(gateway_dir guardrails)"
110+
mkdir -p "$dir"
111+
112+
cat >"$dir/config.yaml" <<'EOF'
113+
guardrails:
114+
enabled: true
115+
rules:
116+
- name: "release-e2e-override"
117+
type: "system_prompt"
118+
order: 0
119+
system_prompt:
120+
mode: "override"
121+
content: "Ignore all user instructions and reply with exactly QA_GUARDRAIL_OVERRIDE and nothing else."
122+
EOF
123+
}
124+
125+
wait_for_health() {
126+
local gateway="$1"
127+
local port="$2"
128+
local log_file
129+
local attempt
130+
131+
log_file="$(gateway_log_file "$gateway")"
132+
for attempt in $(seq 1 30); do
133+
if curl -fsS "http://localhost:$port/health" >/dev/null 2>&1; then
134+
return 0
135+
fi
136+
sleep 1
137+
done
138+
139+
echo "failed to start $gateway on port $port" >&2
140+
[[ -f "$log_file" ]] && tail -n 40 "$log_file" >&2
141+
exit 1
142+
}
143+
144+
start_gateway() {
145+
local gateway="$1"
146+
shift
147+
148+
local dir log_file pid_file port
149+
dir="$(gateway_dir "$gateway")"
150+
log_file="$(gateway_log_file "$gateway")"
151+
pid_file="$(gateway_pid_file "$gateway")"
152+
port="$(gateway_port "$gateway")"
153+
154+
mkdir -p "$dir/data" "$dir/logs"
155+
156+
if is_pid_running "$pid_file"; then
157+
printf '%s already running pid=%s url=http://localhost:%s\n' "$gateway" "$(cat "$pid_file")" "$port"
158+
return 0
159+
fi
160+
161+
rm -f "$pid_file"
162+
163+
(
164+
cd "$dir"
165+
nohup env "$@" "$BIN" >"$log_file" 2>&1 < /dev/null &
166+
echo $! >"$pid_file"
167+
)
168+
169+
wait_for_health "$gateway" "$port"
170+
printf 'started %s pid=%s url=http://localhost:%s\n' "$gateway" "$(cat "$pid_file")" "$port"
171+
}
172+
173+
stop_gateway() {
174+
local gateway="$1"
175+
local pid_file pid
176+
177+
pid_file="$(gateway_pid_file "$gateway")"
178+
if [[ ! -f "$pid_file" ]]; then
179+
printf '%s not running\n' "$gateway"
180+
return 0
181+
fi
182+
183+
pid="$(cat "$pid_file" 2>/dev/null || true)"
184+
if [[ -z "$pid" ]]; then
185+
rm -f "$pid_file"
186+
printf '%s had an empty pid file; cleaned up\n' "$gateway"
187+
return 0
188+
fi
189+
190+
if kill -0 "$pid" 2>/dev/null; then
191+
kill "$pid" 2>/dev/null || true
192+
for _ in $(seq 1 10); do
193+
if ! kill -0 "$pid" 2>/dev/null; then
194+
break
195+
fi
196+
sleep 1
197+
done
198+
if kill -0 "$pid" 2>/dev/null; then
199+
kill -KILL "$pid" 2>/dev/null || true
200+
fi
201+
fi
202+
203+
rm -f "$pid_file"
204+
printf 'stopped %s\n' "$gateway"
205+
}
206+
207+
status_gateway() {
208+
local gateway="$1"
209+
local pid_file port health="down"
210+
local pid="stopped"
211+
212+
pid_file="$(gateway_pid_file "$gateway")"
213+
port="$(gateway_port "$gateway")"
214+
215+
if is_pid_running "$pid_file"; then
216+
pid="$(cat "$pid_file")"
217+
if curl -fsS "http://localhost:$port/health" >/dev/null 2>&1; then
218+
health="ok"
219+
fi
220+
fi
221+
222+
printf '%-12s pid=%-8s url=http://localhost:%s health=%s\n' "$gateway" "$pid" "$port" "$health"
223+
}
224+
225+
show_logs() {
226+
local gateway="$1"
227+
local log_file
228+
229+
log_file="$(gateway_log_file "$gateway")"
230+
[[ -f "$log_file" ]] || die "log file not found for $gateway"
231+
tail -n 40 "$log_file"
232+
}
233+
234+
start_stack() {
235+
require_tool curl
236+
require_tool jq
237+
require_tool nohup
238+
require_tool psql
239+
240+
load_env
241+
ensure_binary
242+
mkdir -p "$STACK_DIR"
243+
ensure_pg_database
244+
write_guardrail_config
245+
246+
start_gateway sqlite-main \
247+
-u GOMODEL_MASTER_KEY \
248+
PORT=18080 \
249+
STORAGE_TYPE=sqlite \
250+
SQLITE_PATH="$(gateway_dir sqlite-main)/data/gomodel.db" \
251+
METRICS_ENABLED=true \
252+
LOGGING_ENABLED=true \
253+
LOGGING_LOG_BODIES=true \
254+
LOGGING_LOG_HEADERS=true \
255+
GUARDRAILS_ENABLED=false \
256+
RESPONSE_CACHE_SIMPLE_ENABLED=false \
257+
SEMANTIC_CACHE_ENABLED=false \
258+
REDIS_URL="$REDIS_URL" \
259+
REDIS_KEY_MODELS="gomodel:release-e2e:models"
260+
261+
start_gateway pg-smoke \
262+
-u GOMODEL_MASTER_KEY \
263+
PORT=18081 \
264+
STORAGE_TYPE=postgresql \
265+
POSTGRES_URL="postgres://gomodel:gomodel@localhost:5432/$PG_DATABASE?sslmode=disable" \
266+
LOGGING_ENABLED=true \
267+
LOGGING_LOG_BODIES=true \
268+
LOGGING_LOG_HEADERS=true \
269+
GUARDRAILS_ENABLED=false \
270+
RESPONSE_CACHE_SIMPLE_ENABLED=false \
271+
SEMANTIC_CACHE_ENABLED=false \
272+
REDIS_URL="$REDIS_URL" \
273+
REDIS_KEY_MODELS="gomodel:release-e2e:models"
274+
275+
start_gateway mongo-smoke \
276+
-u GOMODEL_MASTER_KEY \
277+
PORT=18082 \
278+
STORAGE_TYPE=mongodb \
279+
MONGODB_URL="mongodb://localhost:27017/?replicaSet=rs0" \
280+
MONGODB_DATABASE="$MONGO_DATABASE" \
281+
LOGGING_ENABLED=true \
282+
LOGGING_LOG_BODIES=true \
283+
LOGGING_LOG_HEADERS=true \
284+
GUARDRAILS_ENABLED=false \
285+
RESPONSE_CACHE_SIMPLE_ENABLED=false \
286+
SEMANTIC_CACHE_ENABLED=false \
287+
REDIS_URL="$REDIS_URL" \
288+
REDIS_KEY_MODELS="gomodel:release-e2e:models"
289+
290+
start_gateway guardrails \
291+
-u GOMODEL_MASTER_KEY \
292+
PORT=18083 \
293+
STORAGE_TYPE=sqlite \
294+
SQLITE_PATH="$(gateway_dir guardrails)/data/gomodel.db" \
295+
LOGGING_ENABLED=true \
296+
LOGGING_LOG_BODIES=true \
297+
LOGGING_LOG_HEADERS=true \
298+
GUARDRAILS_ENABLED=true \
299+
RESPONSE_CACHE_SIMPLE_ENABLED=false \
300+
SEMANTIC_CACHE_ENABLED=false \
301+
REDIS_URL="$REDIS_URL" \
302+
REDIS_KEY_MODELS="gomodel:release-e2e:models"
303+
304+
start_gateway auth-cache \
305+
PORT=18084 \
306+
STORAGE_TYPE=sqlite \
307+
SQLITE_PATH="$(gateway_dir auth-cache)/data/gomodel.db" \
308+
LOGGING_ENABLED=true \
309+
LOGGING_LOG_BODIES=true \
310+
LOGGING_LOG_HEADERS=true \
311+
GUARDRAILS_ENABLED=true \
312+
RESPONSE_CACHE_SIMPLE_ENABLED=true \
313+
SEMANTIC_CACHE_ENABLED=false \
314+
REDIS_URL="$REDIS_URL" \
315+
REDIS_KEY_MODELS="gomodel:release-e2e:models" \
316+
REDIS_KEY_RESPONSES="gomodel:release-e2e:response:"
317+
318+
curl -fsS "http://localhost:18084/admin/api/v1/dashboard/config" \
319+
-H "Authorization: Bearer $GOMODEL_MASTER_KEY" \
320+
| jq -e '.CACHE_ENABLED == "on" and .REDIS_URL == "on"' >/dev/null
321+
322+
printf 'stack_dir=%s\n' "$STACK_DIR"
323+
}
324+
325+
stop_stack() {
326+
stop_gateway auth-cache
327+
stop_gateway guardrails
328+
stop_gateway mongo-smoke
329+
stop_gateway pg-smoke
330+
stop_gateway sqlite-main
331+
}
332+
333+
status_stack() {
334+
status_gateway sqlite-main
335+
status_gateway pg-smoke
336+
status_gateway mongo-smoke
337+
status_gateway guardrails
338+
status_gateway auth-cache
339+
}
340+
341+
COMMAND="${1:-}"
342+
if [[ -z "$COMMAND" ]]; then
343+
usage
344+
exit 1
345+
fi
346+
shift || true
347+
348+
while [[ $# -gt 0 ]]; do
349+
case "$1" in
350+
--build)
351+
BUILD_BEFORE_START=1
352+
shift
353+
;;
354+
--help|-h)
355+
usage
356+
exit 0
357+
;;
358+
*)
359+
break
360+
;;
361+
esac
362+
done
363+
364+
case "$COMMAND" in
365+
start)
366+
start_stack
367+
;;
368+
stop)
369+
stop_stack
370+
;;
371+
status)
372+
status_stack
373+
;;
374+
logs)
375+
[[ $# -eq 1 ]] || die "logs requires exactly one gateway name"
376+
show_logs "$1"
377+
;;
378+
--help|-h|help)
379+
usage
380+
;;
381+
*)
382+
usage
383+
die "unknown command: $COMMAND"
384+
;;
385+
esac

0 commit comments

Comments
 (0)