Files
felhom.eu/documentation/audits/day-2026-10-08/r762/bench/check/benchcheck.sh
T

54 lines
5.0 KiB
Bash
Executable File

#!/bin/bash
# R-762 bench check (2026-10-08): the gunicorn definition up on its own, then the login page, its CSS, a photo read back,
# the workers in the container's own log, and the app's memory (anon). Project r762b only; down -v at the end.
set -u
W=/root/r762b; O=$W/out; rm -rf $W; mkdir -p $O; cd $W
cp /opt/upg/templates/wger@gunicorn/docker-compose.yml docker-compose.yml
umask 077
printf 'DOMAIN=bench.invalid\nSUBDOMAIN=fitness\nSECRET_KEY=%s\n' "$(head -c 32 /dev/urandom | od -An -tx1 | tr -d ' \n')" > .env
docker compose -p r762b up -d > $O/up.txt 2>&1
ID=$(docker inspect -f '{{.Id}}' wger); CG=$(find /sys/fs/cgroup -maxdepth 6 -type d -name "*$ID*" | head -1)
touch $W/.sampling; ( max=0; while [ -f $W/.sampling ]; do a=$(awk '$1=="anon"{print $2}' $CG/memory.stat 2>/dev/null); [ -n "$a" ] && [ "$a" -gt "$max" ] && max=$a && echo $max > $O/anon-max; sleep 0.5; done ) &
echo "cgroup: $CG" > $O/cg.txt
IP=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' wger)
FIP=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' wger-files)
t0=$(date +%s); for i in $(seq 1 300); do c=$(curl -s -o /dev/null -w '%{http_code}' http://$IP:8000/en/user/login); [ "$c" = 200 ] && break; sleep 1; done
echo "ready_after_s=$(( $(date +%s)-t0 )) login=$c" > $O/checks.txt
curl -s http://$IP:8000/en/user/login | grep -o '/static/[^"]*\.css' | head -3 | while read l; do echo "css $l via wger-files: $(curl -s -o /dev/null -w '%{http_code} %{size_download}B %{content_type}' http://$FIP$l)"; done >> $O/checks.txt
# a photo through wger's own gallery API, signed in through its own login form as the image's seeded admin (bench-only
# throwaway box; the password is not written anywhere). As a browser behind traefik: Host + X-Forwarded-Proto https, the
# cookies carried by hand (Django's csrftoken is Secure; curl drops it over http).
H=(-H "Host: fitness.bench.invalid" -H "X-Forwarded-Proto: https" -H "Origin: https://fitness.bench.invalid" -H "Referer: https://fitness.bench.invalid/en/user/login")
curl -s -D $W/h1 -o $W/b1 "${H[@]}" http://$IP:8000/en/user/login
CSRFC=$(grep -i '^set-cookie: csrftoken=' $W/h1 | sed 's/^[^=]*=//; s/;.*//' | tr -d '\r')
FORM=$(grep -o 'name="csrfmiddlewaretoken" value="[^"]*"' $W/b1 | head -1 | sed 's/.*value="//; s/"$//')
curl -s -D $W/h2 -o /dev/null "${H[@]}" -H "Cookie: csrftoken=$CSRFC" --data-urlencode "csrfmiddlewaretoken=$FORM" --data-urlencode login=admin --data-urlencode password=<image-default, redacted> http://$IP:8000/en/user/login
SID=$(grep -i '^set-cookie: sessionid=' $W/h2 | sed 's/^[^=]*=//; s/;.*//' | tr -d '\r')
CSRF2=$(grep -i '^set-cookie: csrftoken=' $W/h2 | sed 's/^[^=]*=//; s/;.*//' | tr -d '\r'); [ -n "$CSRF2" ] && CSRFC=$CSRF2
echo "web login: $(head -1 $W/h2 | tr -d '\r'), session cookie: $([ -n "$SID" ] && echo yes || echo no)" >> $O/checks.txt
rm -f $W/h1 $W/h2 $W/b1
python3 - > $W/p.png <<'PY'
import struct, zlib, sys, os
n=64; rgb=os.urandom(3); raw=b"".join(b"\x00"+rgb*n for _ in range(n))
def ch(t,d): return struct.pack(">I",len(d))+t+d+struct.pack(">I",zlib.crc32(t+d)&0xffffffff)
sys.stdout.buffer.write(b"\x89PNG\r\n\x1a\n"+ch(b"IHDR",struct.pack(">IIBBBBB",n,n,8,2,0,0,0))+ch(b"IDAT",zlib.compress(raw))+ch(b"IEND",b""))
PY
SZ=$(stat -c %s $W/p.png)
R=$(curl -s -w '\n%{http_code}' "${H[@]}" -H "Cookie: csrftoken=$CSRFC; sessionid=$SID" -H "X-CSRFToken: $CSRFC" -F "image=@$W/p.png;type=image/png" -F date=2026-10-08 -F description=r762 http://$IP:8000/api/v2/gallery/)
echo "POST /api/v2/gallery/ (${SZ} B PNG) -> $(echo "$R" | tail -1)" >> $O/checks.txt
P=$(echo "$R" | head -n -1 | python3 -c 'import json,sys,re; print(re.sub(r"^https?://[^/]+","",json.load(sys.stdin).get("image","")))' 2>/dev/null)
[ -z "$P" ] && echo "photo: NO PATH returned (the upload failed)" >> $O/checks.txt
[ -n "$P" ] && echo "photo $P via wger-files: $(curl -s -o /dev/null -w '%{http_code} %{size_download}B %{content_type}' http://$FIP$P)" >> $O/checks.txt
echo "control: an unknown /static/ file via wger-files: $(curl -s -o /dev/null -w '%{http_code}' http://$FIP/static/r762-nonexistent.css)" >> $O/checks.txt
# light load: 20 login GETs, 10 parallel
seq 1 20 | xargs -P 10 -I{} curl -s -o /dev/null -w 'conc %{http_code} %{time_total}\n' http://$IP:8000/en/user/login > $O/conc.txt
sleep 3; rm -f $W/.sampling; sleep 1
grep -E '^(oom|oom_kill) ' $CG/memory.events > $O/events.txt; cat $CG/memory.max > $O/memory.max; cat $CG/memory.peak > $O/memory.peak 2>/dev/null
docker inspect -f 'restarts={{.RestartCount}} oomkilled={{.State.OOMKilled}} status={{.State.Status}} health={{.State.Health.Status}}' wger > $O/inspect.txt
docker exec wger sh -c 'env | grep -E "^(WGER_USE_GUNICORN|WEB_CONCURRENCY)="' > $O/env-in-container.txt
docker logs wger 2>&1 | sed 's/<image-default, redacted>/<image-default, redacted>/g' > $O/wger.log
grep -E 'Using gunicorn|Using django|Booting worker|Listening at|Starting gunicorn|Starting development server' $O/wger.log > $O/server-lines.txt
docker compose -p r762b down -v > $O/down.txt 2>&1
rm -f $W/p.png .env
echo done