1
0
mirror of https://github.com/coturn/coturn.git synced 2026-10-24 00:08:57 +03:00
Files
coturn/examples/run_tests_prom.sh
Pavel Punsky 36b2f14516 Update help with missing arguments (#2079)
## Summary
- Remove the deprecated `--no-dtls` and `--no-cli` turnserver flags —
both were already no-ops (DTLS and CLI are off unless `--dtls`/`--cli`
is passed), so the flags only added deprecation-warning noise.
- Update `README.turnserver` and the `Usage` help text in `mainrelay.c`
to drop the removed flags.
- Strip now-unused `--no-dtls`/`--no-cli` invocations from the example
test scripts and update `run_tests_dtls_default.sh` to drop the coverage
for the deprecated flag's warning/compat behavior.
- Update `CLAUDE.md`'s test-suite notes and load-test snippet to match.

## Test plan
- [ ] `cmake --build build && ctest --test-dir build
--output-on-failure`
- [ ] `cd examples && ./run_tests.sh && ./run_tests_conf.sh &&
./run_tests_dtls_default.sh`

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-09-07 15:43:30 -07:00

230 lines
8.7 KiB
Bash
Executable File

#!/bin/bash
# Detect cmake build and adjust path
BINDIR="../bin"
if [ ! -f $BINDIR/turnserver ]; then
BINDIR="../build/bin"
fi
# turnserver startup is near-instant on Linux but takes ~5s on macOS, where the
# thread-barrier rendezvous in netengine.c degrades to a fixed sleep(5) because
# libpthread lacks pthread_barrier_*. Rather than sleeping a fixed interval, the
# helpers below poll for readiness (the metrics endpoint, or the server log) up
# to this timeout.
POLL_TIMEOUT=30
TURNSERVER_LOG="$(mktemp "${TMPDIR:-/tmp}/run_tests_prom.XXXXXX")"
turnserver_pid=""
# Pin the TURN listeners to loopback and skip the TLS/DTLS listeners: this test
# only exercises the Prometheus exporter (served independently by libmicrohttpd),
# and leaving address auto-discovery on makes startup slow and flaky on hosts
# with tentative/temporary IPv6 addresses (the DTLS/UDP bind retries with
# sleep(1) until Duplicate Address Detection completes).
COMMON_ARGS="-L 127.0.0.1 -E 127.0.0.1 --no-tls --log-file=stdout"
# stop_turnserver: stop the running turnserver and wait for it to exit so its
# listening ports are released before the next instance binds them. SIGKILL is
# used deliberately: graceful SIGTERM shutdown can take up to ~5s (the auth
# housekeeping thread sleeps in 5s cycles), and this test does not care about a
# clean drain.
function stop_turnserver() {
if [ -n "$turnserver_pid" ]; then
kill -KILL "$turnserver_pid" 2>/dev/null
wait "$turnserver_pid" 2>/dev/null
turnserver_pid=""
fi
}
function cleanup() {
stop_turnserver
rm -f "$TURNSERVER_LOG"
}
trap cleanup EXIT
# start_turnserver <args...>: launch turnserver in the background with its output
# captured to TURNSERVER_LOG so the negative tests can poll for its decision.
function start_turnserver() {
: > "$TURNSERVER_LOG"
# shellcheck disable=SC2086
"$BINDIR/turnserver" $COMMON_ARGS "$@" > "$TURNSERVER_LOG" 2>&1 &
turnserver_pid="$!"
}
# wait_for_prom_decision: block until turnserver has logged that it settled its
# Prometheus startup decision (exporter started, or explicitly not started).
# Used by the no-response tests, which cannot poll the (absent) endpoint.
function wait_for_prom_decision() {
local deadline=$((SECONDS + POLL_TIMEOUT))
while [ "$SECONDS" -lt "$deadline" ]; do
if grep -Eq "prometheus collector started successfully|prometheus collector disabled, not started|could not read certificate|without TLS support|no certificate/key is configured" "$TURNSERVER_LOG"; then
return 0
fi
sleep 0.2
done
echo "FAIL (turnserver did not settle its prometheus decision within ${POLL_TIMEOUT}s)"
cat "$TURNSERVER_LOG"
exit 1
}
function assert_prom_no_response() {
wget --quiet --output-document=/dev/null --tries=1 "$1"
status="$?"
if [ "$status" -eq 0 ]; then
echo FAIL
exit 1
else
echo OK
fi
}
function assert_prom_response() {
# Poll until the endpoint returns something that looks like the expected body,
# or fail after POLL_TIMEOUT.
local deadline=$((SECONDS + POLL_TIMEOUT))
while [ "$SECONDS" -lt "$deadline" ]; do
if wget --quiet --output-document=- --tries=1 "$1" 2>/dev/null |
grep -q 'TYPE\|HELP\|counter\|gauge'; then
echo OK
return 0
fi
sleep 0.2
done
echo FAIL
cat "$TURNSERVER_LOG"
exit 1
}
function assert_prom_response_tls() {
# Same as assert_prom_response but over HTTPS with a self-signed cert, so
# certificate verification is disabled. A plaintext HTTP request to the same
# URL must NOT succeed (the endpoint is TLS-only).
local deadline=$((SECONDS + POLL_TIMEOUT))
local ok=0
while [ "$SECONDS" -lt "$deadline" ]; do
if wget --no-check-certificate --quiet --output-document=- --tries=1 "$1" 2>/dev/null |
grep -q 'TYPE\|HELP\|counter\|gauge'; then
ok=1
break
fi
sleep 0.2
done
if [ "$ok" -ne 1 ]; then
echo FAIL
cat "$TURNSERVER_LOG"
exit 1
fi
# The HTTPS endpoint must reject a plaintext HTTP request on the same port.
http_url="${1/https:/http:}"
wget --quiet --output-document=/dev/null --tries=1 "$http_url"
if [ "$?" -eq 0 ]; then
echo "FAIL (plaintext HTTP accepted on TLS endpoint)"
exit 1
fi
echo OK
}
function assert_prom_metric_nonzero() {
metric="$1"
body="$2"
if echo "$body" | grep -Eq "^${metric}(_total)? [1-9][0-9]*(\\.[0-9]+)?$"; then
echo OK
else
echo "FAIL: metric ${metric} was not non-zero"
exit 1
fi
}
echo "Running without prometheus"
start_turnserver
wait_for_prom_decision
assert_prom_no_response "http://localhost:9641/metrics"
stop_turnserver
echo "Running turnserver with prometheus, using defaults"
start_turnserver --prometheus
assert_prom_response "http://localhost:9641/metrics"
stop_turnserver
echo "Running turnserver with prometheus, using custom address and port"
start_turnserver --prometheus --prometheus-address="127.0.0.1" --prometheus-port="8080"
assert_prom_response "http://127.0.0.1:8080/metrics"
stop_turnserver
echo "Running turnserver with prometheus, using custom path"
start_turnserver --prometheus --prometheus-path="/coturn/metrics"
assert_prom_response "http://localhost:9641/coturn/metrics"
stop_turnserver
# --prometheus-tls implies --prometheus, so the exporter is enabled without a
# separate --prometheus flag below.
echo "Running turnserver with prometheus over TLS, explicit --prometheus-cert/--prometheus-key"
start_turnserver --prometheus-tls \
--prometheus-cert=etc/turn_server_cert.pem --prometheus-key=etc/turn_server_pkey.pem
assert_prom_response_tls "https://localhost:9641/metrics"
stop_turnserver
echo "Running turnserver with prometheus over TLS, cert/key inherited from server --cert/--pkey"
start_turnserver --prometheus-tls \
--cert=etc/turn_server_cert.pem --pkey=etc/turn_server_pkey.pem
assert_prom_response_tls "https://localhost:9641/metrics"
stop_turnserver
echo "Running turnserver with prometheus TLS but an unreadable cert: endpoint must not start"
start_turnserver --prometheus-tls \
--prometheus-cert=/nonexistent/cert.pem --prometheus-key=/nonexistent/key.pem
wait_for_prom_decision
assert_prom_no_response "https://localhost:9641/metrics"
stop_turnserver
# COMMON_ARGS already supplies -L/-E 127.0.0.1 and --no-tls.
echo "Running turnserver with prometheus 401 mitigation counters"
start_turnserver --prometheus --prometheus-address="127.0.0.1" --prometheus-port="8081" \
--use-auth-secret --static-auth-secret=secret --realm=north.gov \
--allow-loopback-peers --listening-port=3479 \
--unauthorized-ratelimit --unauthorized-ratelimit-rps=1
assert_prom_response "http://127.0.0.1:8081/metrics"
# The two 401-mitigation metric families have deliberately different exposure
# timing, and a single bad-cred client run satisfies only one at a time:
# * turn_unauthenticated_401_* are per-thread accumulators flushed into the
# registry on the engine's 1 Hz timer, so they only appear ~1s AFTER traffic.
# * turn_ratelimit_occupied_buckets is computed live at scrape time and decays
# to 0 within RATELIMIT_WINDOW_SECS (1s) of the most recent 401.
# turnutils_uclient emits its whole 401 burst in well under a second and then
# idles, so right after a burst the gauge is live but the counters have not
# flushed, and a second later the counters appear but the gauge has decayed.
# Keep bad-cred traffic flowing continuously (a short-lived client restarted in a
# loop) so a rate-limit window stays open across at least one counter-flush tick,
# then poll until a single scrape shows both families non-zero.
flood_stop="$(mktemp "${TMPDIR:-/tmp}/run_tests_prom_flood.XXXXXX")"
(
while [ -e "$flood_stop" ]; do
timeout 1s "$BINDIR/turnutils_uclient" \
-e 127.0.0.1 -X -g -u baduser -W wrongsecret -p 3479 127.0.0.1 \
> /dev/null 2>&1
done
) &
flood_pid="$!"
prom_metrics=""
deadline=$((SECONDS + POLL_TIMEOUT))
while [ "$SECONDS" -lt "$deadline" ]; do
scrape="$(wget --quiet --output-document=- --tries=1 "http://127.0.0.1:8081/metrics" 2>/dev/null)"
if echo "$scrape" | grep -Eq "^turn_ratelimit_occupied_buckets [1-9][0-9]*(\\.[0-9]+)?$" &&
echo "$scrape" | grep -Eq "^turn_unauthenticated_401_requests(_total)? [1-9][0-9]*(\\.[0-9]+)?$"; then
prom_metrics="$scrape"
break
fi
sleep 0.2
done
rm -f "$flood_stop"
wait "$flood_pid" 2>/dev/null
assert_prom_metric_nonzero "turn_unauthenticated_401_requests" "$prom_metrics"
assert_prom_metric_nonzero "turn_unauthenticated_401_responses" "$prom_metrics"
assert_prom_metric_nonzero "turn_unauthenticated_401_dropped_responses" "$prom_metrics"
# The single bad-cred source occupies exactly one live bucket; capacity is the
# fixed table size. (Collisions stay 0 with one source, so are not asserted here.)
assert_prom_metric_nonzero "turn_ratelimit_occupied_buckets" "$prom_metrics"
assert_prom_metric_nonzero "turn_ratelimit_total_buckets" "$prom_metrics"
stop_turnserver