Skip to content
Draft
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
17 changes: 5 additions & 12 deletions .github/workflows/scripts/run-and-measure.sh
Original file line number Diff line number Diff line change
Expand Up @@ -37,25 +37,18 @@ COMPOSE=(docker compose -f docker-compose.yml -f docker-compose.coverage.yml)

"${COMPOSE[@]}" up -d --build

# Both 200 and 401 are success signals.
for i in $(seq 1 120); do
code=$(curl -sS -o /dev/null -w '%{http_code}' \
"http://127.0.0.1:${RESTHEART_APP_PORT}/" 2>/dev/null || echo "")
if [ "$code" = "200" ] || [ "$code" = "401" ]; then break; fi
sleep 2
done

if [ "$code" != "200" ] && [ "$code" != "401" ]; then
echo "::error::restheart did not bind on port ${RESTHEART_APP_PORT} within 240s (last code: ${code:-empty})"
# flow.sh bootstrap waits for RESTHeart to answer for as long as its
# container runs (no count of tries: a slow runner is waited for), and
# fails at once when the container has exited.
if ! bash flow.sh bootstrap; then
echo "::error::flow.sh bootstrap failed (above): RESTHeart did not come up on port ${RESTHEART_APP_PORT}, or seeding it failed"
echo "----- restheart container logs -----"
docker logs "${RESTHEART_APP_CONTAINER}" --tail 200 2>&1 || true
echo "----- mongo container logs -----"
docker logs "${RESTHEART_MONGO_CONTAINER}" --tail 100 2>&1 || true
"${COMPOSE[@]}" down -v --remove-orphans || true
exit 1
fi

bash flow.sh bootstrap 240
bash flow.sh record-traffic

# JaCoCo TCP-dump + report (no JVM stop needed).
Expand Down
6 changes: 3 additions & 3 deletions restheart-mongo/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ restheart-mongo/

The sample is keploy-independent: `docker compose up && bash flow.sh bootstrap && bash flow.sh record-traffic` runs end-to-end against bare RESTHeart. Lane scripts wrap that exact same path inside `keploy record` / `keploy test`.

* `bootstrap` — wait for RESTHeart to start serving and PUT the seed collections (`items`, `people`, `places`, `halpeople`, `relpeople`, `gql-apps`, `acl`, `_schemas`, `avatars.files`, `range_files.files`, `imported_csv`) so subsequent record-traffic calls have something to find.
* `bootstrap` — wait for RESTHeart to answer (for as long as its container runs: no deadline) and PUT the seed collections (`items`, `people`, `places`, `halpeople`, `relpeople`, `gql-apps`, `acl`, `_schemas`, `avatars.files`, `range_files.files`, `imported_csv`) so subsequent record-traffic calls have something to find.
* `record-traffic` — drive the full RESTHeart REST surface listed above. Every call is fault-tolerant (`|| true`) so a single transient 4xx never aborts the run. keploy is the assertion layer.
* `coverage` — emits real Java line coverage via JaCoCo when the `docker-compose.coverage.yml` overlay is applied; otherwise a no-op (the base image is uninstrumented so this prints an info message and exits 0).

Expand All @@ -44,7 +44,7 @@ The sample is keploy-independent: `docker compose up && bash flow.sh bootstrap &

```sh
docker compose up -d
bash flow.sh bootstrap 240
bash flow.sh bootstrap
bash flow.sh record-traffic
docker compose down -v
```
Expand All @@ -58,7 +58,7 @@ The base image is uninstrumented. Apply the coverage overlay to attach the JaCoC
```sh
mkdir -p coverage
docker compose -f docker-compose.yml -f docker-compose.coverage.yml up -d --build
bash flow.sh bootstrap 240
bash flow.sh bootstrap
bash flow.sh record-traffic
bash flow.sh coverage
docker compose -f docker-compose.yml -f docker-compose.coverage.yml down -v
Expand Down
67 changes: 49 additions & 18 deletions restheart-mongo/flow.sh
Original file line number Diff line number Diff line change
Expand Up @@ -44,26 +44,56 @@ log_fired() {
printf '%s %s\n' "$1" "$2" >>"$RESTHEART_FIRED_ROUTES_FILE"
}

# restheart_answers: RESTHeart answers HTTP on its root: 200, or 401 (it
# is up and asks for credentials). A published port is not that: docker's
# proxy listens on it as soon as the container starts.
restheart_answers() {
local code
code=$(curl -sS -m 5 -o /dev/null -w '%{http_code}' "${base}/" 2>/dev/null || true)
[ "$code" = "200" ] || [ "$code" = "401" ]
}

# restheart_wait_for_app: waits until RESTHeart answers, for as long as its
# container ($RESTHEART_APP_CONTAINER) is created, running or restarting. No
# deadline: how long a JVM takes to start depends on the host, and a deadline
# that a loaded runner outlasts fails a healthy app (60 s here did, under
# keploy record on a busy CI host). A container that has exited, or is not
# there, ends the wait at once and fails it, with its last log lines; a docker
# that does not answer is no verdict either way; one that runs and never
# answers is left to the caller's own bound (a CI step's timeout). A lane
# that waits for the app itself first finds it answering at the first probe.
restheart_wait_for_app() {
local timeout=${1:-180}
local start_ts code
start_ts=$(date +%s)
while true; do
code=$(curl -sS -o /dev/null -w '%{http_code}' "${base}/" 2>/dev/null || echo "")
# 401 (auth required on root) is a SUCCESS signal — it
# means RESTHeart is up and responding to HTTP.
if [ "$code" = "200" ] || [ "$code" = "401" ]; then return 0; fi
if [ $(( $(date +%s) - start_ts )) -ge "$timeout" ]; then
echo "restheart_wait_for_app: timed out (last code: ${code:-<empty>})" >&2
return 1
fi
sleep 2
local state rc said=""
while ! restheart_answers; do
rc=0
state=$(docker inspect -f '{{.State.Status}}' "$RESTHEART_APP_CONTAINER" 2>&1) || rc=$?
case "$rc:$state" in
# No docker to ask (not installed, not executable): no wait can end.
126:* | 127:*)
echo "restheart_wait_for_app: cannot run docker (exit $rc): $state" >&2
return 1 ;;
# Not answering yet, but on its way: created, starting, restarting.
0:created | 0:running | 0:restarting) ;;
# Gone: it exited, died, or is not there.
0:* | *"No such"*)
# It may have answered, and gone, since the probe.
restheart_answers && return 0
[ "$rc" = 0 ] || state="not there"
echo "restheart_wait_for_app: RESTHeart does not answer on ${base}/ and its container ${RESTHEART_APP_CONTAINER} is ${state}" >&2
docker logs --tail 50 "$RESTHEART_APP_CONTAINER" >&2 2>&1 || true
return 1 ;;
# docker did not answer (a daemon slow under load, a socket it
# may not open): not a verdict, said once.
*)
[ -n "$said" ] || echo "restheart_wait_for_app: docker inspect failed (exit $rc), still waiting: $state" >&2
said=1 ;;
esac
sleep 1
done
}

restheart_bootstrap() {
local timeout=${1:-180}
restheart_wait_for_app "$timeout"
restheart_wait_for_app

# Seed the collections record-traffic depends on. Each PUT is
# idempotent (201 first time, 200 on subsequent runs) and
Expand All @@ -78,8 +108,7 @@ restheart_bootstrap() {
}

restheart_record_traffic() {
restheart_wait_for_app 60
sleep 5
restheart_wait_for_app

local encoded_doc_keys='%7B%22_id%22:1,%22name%22:1,%22age%22:1%7D'
local encoded_filter='%7B%22_id%22:%22jane%22%7D'
Expand Down Expand Up @@ -1315,7 +1344,9 @@ print(miss, cov)
}

case "${1:-}" in
bootstrap) restheart_bootstrap "${2:-180}" ;;
# A second argument (the seconds a caller once gave the wait) is
# accepted and not used: the wait is on the app, not on a clock.
bootstrap) restheart_bootstrap ;;
record-traffic) restheart_record_traffic ;;
coverage) restheart_report_coverage ;;
*)
Expand Down
Loading