diff --git a/apps/starry/README.md b/apps/starry/README.md index 61ec584006..234d0c5de6 100644 --- a/apps/starry/README.md +++ b/apps/starry/README.md @@ -142,11 +142,13 @@ packages in a staging root during prebuild, injects runtime artifacts to the app overlay, then runs nginx smoke tests inside StarryOS. ```bash -cargo xtask starry app run -t nginx --arch x86_64 +cargo xtask starry app qemu -t nginx --arch x86_64 ``` -`apps/starry/nginx` maintains four directories: `smoke`, `phase`, `stress`, and -`debug`. Currently only smoke is connected as nginx test entry in tgoskits workflows. +`apps/starry/nginx` keeps the CI-discovered smoke QEMU configs at the app root, +and keeps manual `all`/`phase`/`debug` QEMU configs under `qemu/`. The guest +entrypoint is `runner/nginx-runner.sh`; currently only smoke is connected as the +nginx test entry in tgoskits workflows. ## Orange Pi 5 Plus UVC diff --git a/apps/starry/nginx/README.md b/apps/starry/nginx/README.md index a307578f9b..e206ed4e4a 100644 --- a/apps/starry/nginx/README.md +++ b/apps/starry/nginx/README.md @@ -1,38 +1,35 @@ # Starry Nginx App -This app organizes nginx tests into four directories: +Starry 下 nginx 应用的构建与测试入口。 -- `smoke/`: CI entry smoke test (the only connected nginx test in tgoskits) -- `phase/`: stage-unit tests named by `x-x`, such as `nginx-1-3-lifecycle-tests.sh` -- `stress/`: pressure test planning and future scripts -- `debug/`: flexible issue-focused scripts +## 测试模式 -## CI-Connected Entry +guest 内统一入口 `/usr/bin/nginx-runner.sh `: -Only smoke is connected: +| mode | 用途 | CI | +|------|------|----| +| `smoke` | 仅 smoke | ✅ 唯一接入上层 CI | +| `phase ` | 单阶段复测 | ❌ 手工 | +| `all` | smoke + 全部 phase(阶段强隔离) | ❌ 手工 | +| `stress` | 压测(当前 skip) | ❌ 手工 | +| `debug ` | 单问题调试 | ❌ 手工 | -```bash -cargo xtask starry app run -t nginx --arch riscv64 -``` - -`qemu-*.toml` starts `/usr/bin/nginx-smoke-tests.sh` only. - -## Local Development CLI +QEMU 入口按用途分目录:根 `qemu-.toml`(CI)、`qemu/all/`(手工全量)、 +`qemu/phase/`(手工单阶段)、`qemu/debug/`(手工调试)。 -Unified local CLI script: +## 用法 ```bash -./apps/starry/nginx/nginx-cli-tests.sh smoke -./apps/starry/nginx/nginx-cli-tests.sh phase1 -./apps/starry/nginx/nginx-cli-tests.sh phase2 -./apps/starry/nginx/nginx-cli-tests.sh all -``` - -## Build/Prepare Logic +# CI smoke(四架构) +cargo xtask starry app qemu -t nginx --arch x86_64 \ + --qemu-config apps/starry/nginx/qemu-x86_64.toml -`prebuild.sh` injects only smoke entry and shared mirror helper into guest overlay: +# 手工单阶段 +cargo xtask starry app qemu -t nginx --arch x86_64 \ + --qemu-config apps/starry/nginx/qemu/phase/qemu-x86_64-phase31.toml +``` -- `/usr/bin/nginx-smoke-tests.sh` -- `/usr/bin/nginx-alpine-mirror.sh` +## 详细设计 -Mirror helper: `apps/starry/nginx/nginx-alpine-mirror.sh`. +完整设计(目录结构、统一入口 marker 规则、all 模式阶段隔离契约、TOML 整合规则、 +迁移步骤)见 [`www/nginx-ci-refactor-proposal.md`](../../../www/nginx-ci-refactor-proposal.md)。 diff --git a/apps/starry/nginx/build-aarch64-unknown-none-softfloat.toml b/apps/starry/nginx/build-aarch64-unknown-none-softfloat.toml new file mode 100644 index 0000000000..884aa834c1 --- /dev/null +++ b/apps/starry/nginx/build-aarch64-unknown-none-softfloat.toml @@ -0,0 +1,10 @@ +target = "aarch64-unknown-none-softfloat" +log = "Warn" +features = [ + "qemu", + "ax-driver/virtio-blk", + "ax-driver/virtio-net", + "ax-driver/virtio-gpu", + "ax-driver/virtio-input", + "ax-driver/virtio-socket", +] diff --git a/apps/starry/nginx/build-loongarch64-unknown-none-softfloat.toml b/apps/starry/nginx/build-loongarch64-unknown-none-softfloat.toml new file mode 100644 index 0000000000..a1f2d2a931 --- /dev/null +++ b/apps/starry/nginx/build-loongarch64-unknown-none-softfloat.toml @@ -0,0 +1,14 @@ +target = "loongarch64-unknown-none-softfloat" +axconfig_overrides = ["plat.phys-memory-size=0x8000_0000"] +log = "Warn" +features = [ + "ax-hal/loongarch64-qemu-virt", + "qemu", + "ax-driver/plat-static", + "ax-driver/virtio-blk", + "ax-driver/virtio-net", + "ax-driver/virtio-gpu", + "ax-driver/virtio-input", + "ax-driver/virtio-socket", +] +plat_dyn = false diff --git a/apps/starry/nginx/debug/ISSUE-004-bad-method-probe-instability.md b/apps/starry/nginx/debug/ISSUE-004-bad-method-probe-instability.md new file mode 100644 index 0000000000..3b7f3f4e38 --- /dev/null +++ b/apps/starry/nginx/debug/ISSUE-004-bad-method-probe-instability.md @@ -0,0 +1,48 @@ +# ISSUE-004: BAD Method Probe Instability + +## Status + +- State: deferred (temporarily bypassed in phase2) +- Scope: `BAD / HTTP/1.1` verification path in stage 2 +- Impact: does not block phase2 main flow + +## Symptom + +- In some runs, `busybox nc` returns an empty response (``) for both: + - `BAD / HTTP/1.1` + - `GET / HTTP/1.1` +- This can make a strict raw-probe assertion fail even when nginx behavior is normal. + +## Confirmed Observations + +- `curl -X BAD http://127.0.0.1:8080/...` stably returns `405`. +- `nc.openbsd` raw BAD request stably returns `HTTP/1.1 405 Not Allowed`. +- `busybox nc` may return `` under the same request pattern. + +## Current Decision + +- Keep phase2 BAD-method node as bypass (known issue log only). +- Do not use `busybox nc` as pass/fail oracle for this node. +- Continue tracking through debug scripts: + - `nginx-2-0-bad-method-debug.sh` + - `nginx-2-0-bad-method-matrix.sh` + +## Repro Commands + +```bash +cargo xtask starry app qemu -t nginx --arch x86_64 --qemu-config apps/starry/nginx/qemu/debug/qemu-x86_64-bad-method-debug.toml +cargo xtask starry app qemu -t nginx --arch x86_64 --qemu-config apps/starry/nginx/qemu/debug/qemu-x86_64-bad-method-matrix.toml +``` + +Optional phase20 (HTTP basic) retest: + +```bash +cargo xtask starry app qemu -t nginx --arch x86_64 --qemu-config apps/starry/nginx/qemu/phase/qemu-x86_64-phase20.toml +``` + +## Next Follow-up + +- Add a minimal socket-level probe outside nginx to isolate whether this is: + - `busybox nc` behavior under StarryOS, or + - StarryOS socket timing/EOF behavior exposed by `busybox nc`. +- After root cause is confirmed and fixed, restore strict assertion for the phase2 BAD-method node. diff --git a/apps/starry/nginx/debug/ISSUE-005-x86-short-connection-timeout.md b/apps/starry/nginx/debug/ISSUE-005-x86-short-connection-timeout.md new file mode 100644 index 0000000000..c1a31c5153 --- /dev/null +++ b/apps/starry/nginx/debug/ISSUE-005-x86-short-connection-timeout.md @@ -0,0 +1,119 @@ +# ISSUE-005: x86 Short Connection Timeout + +## Status + +- State: investigating +- Scope: x86_64 Starry nginx phase31 short-connection loop +- Impact: can fail `nginx-runner.sh all` at `phase31` with `NGINX_PHASE31_TEST_FAILED` + +## Symptom + +- `phase31` runs many short HTTP connections against nginx on `127.0.0.1:8080`. +- On x86_64, the loop can exceed the per-probe or per-phase timeout budget. +- The terminal failure usually appears as: + +```text +NGINX_RUNNER_PHASE_BEGIN phase=phase31 +NGINX_PHASE31_TEST_FAILED +NGINX_PHASE31_LOG: 100 short connections +NGINX_RUNNER_PHASE_FAIL phase=phase31 rc=1 +NGINX_RUNNER_FAILED phase=phase31 +``` + +Older logs also showed: + +```text +NGINX_PHASE31_LOG: watchdog timeout +Terminated +``` + +## Confirmed Observations + +- riscv64 passed the same nginx phase path. +- x86_64 standalone `phase31` can fail, so the issue is not only cross-phase state from `all`. +- A focused short-connection debug run once failed around iteration 143 with `curl rc=7` and `connection refused` while nginx received `SIGTERM`. +- Raising the debug runner budget to 1800s made the timeout-wrapped short-connection debug pass 120 iterations. +- Running the same debug loop without per-request `timeout` also passed 120 iterations. +- The x86 loop is still very slow: `curl` reports tens of milliseconds of HTTP time, but the wall-clock loop averages several seconds per request. +- A timing microbench shows the slow path starts before nginx/socket handling: + +| Bench | x86_64 | riscv64 | +| --- | ---: | ---: | +| shell arithmetic, 100 iters | 1s | 0s | +| shell builtin `:`, 100 iters | 1s | 0s | +| `/bin/true`, 100 iters | 32s | 10s | +| `/bin/busybox true`, 100 iters | 38s | 10s | +| `/bin/echo ok`, 100 iters | 36s | 15s | +| `/bin/sleep 0`, 100 iters | 30s | 19s | +| `/bin/date +%s`, 20 iters | 9s | 2s | +| `/bin/sleep 1`, 5 iters | 6s | 5s | +| `timeout 5 /bin/true`, 100 iters | 63s | 23s | +| direct nginx `curl`, 40 iters | 91s | 31s | +| `timeout 10 curl`, 40 iters | 106s | 34s | + +- `sleep 1` is roughly correct, so the primary issue is not a simple timer-frequency error. +- Shell builtins are fast, while every external command has high fixed cost, so the primary issue is likely in x86 process launch/exec/exit/wait or user/kernel transition overhead. +- `timeout` adds another external process and timer/signal layer, which makes the phase31 `timeout curl` loop more vulnerable. + +## Current Hypothesis + +- The HTTP request itself is not the main cost. +- The x86_64 path spends excessive wall time in short-lived external command handling. +- The current strongest suspects are x86 `execve` page-table work, x86 user/kernel transition overhead, and vfork/exec/wait scheduling latency. +- The original `timeout 5 curl ...` assertion is therefore close to the x86 timing cliff and can fail nondeterministically. + +## Debugging Work So Far + +- Reproduced the original failure shape from `phase31`: `NGINX_PHASE31_TEST_FAILED` followed by `NGINX_RUNNER_FAILED phase=phase31`. +- Verified that standalone x86_64 `phase31` can fail, so the issue is not only caused by earlier phases in `all`. +- Added `nginx-3-1-short-connection-debug.sh` to run the same short-connection pattern with per-iteration curl status, stderr, nginx process state, socket state, and log tails. +- The first timeout-wrapped debug run failed around iteration 143 with `curl rc=7` and `connection refused`; nginx had received `SIGTERM`, which matched the runner/default watchdog budget expiring rather than a stable nginx accept failure. +- Raised the debug runner budget to 1800s and confirmed timeout-wrapped short connections can complete 120 iterations on x86_64. +- Disabled per-request `timeout` in a direct debug run and confirmed 120 iterations also pass, but still take several hundred seconds. +- Added `nginx-3-1-x86-timing-debug.sh` to split shell-only work from external-command, timeout, and curl loops. +- Added a riscv64 timing-debug QEMU config and ran the same timing script as an architecture baseline. + +## Phase Workaround + +- `apps/starry/nginx/phase/nginx-3-1-short-connection-tests.sh` keeps the same HTTP assertion: 100 independent `curl -fsS` short connections must succeed. +- The per-curl wall-clock timeout was raised from 5s to 15s through `SHORT_CONN_CURL_TIMEOUT`. +- This does not weaken the response correctness check; it only avoids treating the current x86 external-command overhead as an nginx failure. +- The runner phase budget remains the outer bound. The measured x86 timing debug still fits well below the standalone phase budget after this per-operation relaxation. + +## Code Pointers + +- `os/StarryOS/kernel/src/syscall/task/clone.rs`: `CloneArgs::do_clone`, vfork setup and parent wait. +- `os/StarryOS/kernel/src/syscall/task/execve.rs`: `do_execve`, per-exec address-space creation, ELF load, and page-table switch. +- `os/StarryOS/kernel/src/task/mod.rs`: `ProcessData::wait_vfork_done` / `notify_vfork_done`. +- `components/axcpu/src/x86_64/uspace.rs`: x86 user context entry/exit and per-run FS/GS MSR handling. +- `components/axcpu/src/x86_64/trap.S`: `enter_user` syscall/iret return path. +- `components/axcpu/src/x86_64/context.rs`: task context switch and user page-table switch. + +## Suspicious Implementation Details + +- `execve` creates and installs a fresh address space for every external command. On x86_64 this includes kernel mapping copy, CR3 switch, and TLB effects that shell builtins avoid. +- x86 user entry/exit does FS/GS MSR handling around `UserContext::run()`. Short-lived commands execute many syscall/user-return cycles during dynamic loader startup and process exit. +- Fresh x86 `UserContext::new()` after `execve` may initially return through the `iretq` path instead of the faster syscall-return path. +- BusyBox shell likely uses `vfork`/`CLONE_VFORK` for external commands. The parent blocks in `ProcessData::wait_vfork_done()` until the child execs or exits, so scheduling or wakeup latency is paid once per command. +- `timeout curl` adds another external process plus timer/signal/wait behavior, which explains why it is significantly slower than direct `curl` and why phase31 is sensitive to a 5s per-operation deadline. + +## Debug Scripts + +- `nginx-3-1-short-connection-debug.sh`: nginx short-connection reproduction with detailed curl/nginx/socket logs. +- `nginx-3-1-x86-timing-debug.sh`: microbench to compare shell loop, `/bin/true`, `timeout true`, direct curl, and timeout-wrapped curl. + +## Repro Commands + +```bash +cargo xtask starry app qemu -t nginx --arch x86_64 --qemu-config apps/starry/nginx/qemu/debug/qemu-x86_64-short-connection-debug.toml +cargo xtask starry app qemu -t nginx --arch x86_64 --qemu-config apps/starry/nginx/qemu/debug/qemu-x86_64-short-connection-direct-debug.toml +cargo xtask starry app qemu -t nginx --arch x86_64 --qemu-config apps/starry/nginx/qemu/debug/qemu-x86_64-timing-debug.toml +cargo xtask starry app qemu -t nginx --arch riscv64 --qemu-config apps/starry/nginx/qemu/debug/qemu-riscv64-timing-debug.toml +``` + +## Next Follow-up + +- The bottleneck appears before networking: inspect x86 short-lived process execution first. +- Compare `execve` page-table switching costs on x86, especially CR3/TLB work in `components/axcpu/src/x86_64/context.rs` and address-space replacement in `do_execve`. +- Inspect x86 user/kernel round-trip overhead in `components/axcpu/src/x86_64/uspace.rs` and `components/axcpu/src/x86_64/trap.S`. +- Inspect vfork/exec/wait wakeup latency in `CloneArgs::do_clone`, `ProcessData::wait_vfork_done`, `notify_vfork_done`, and `sys_waitpid`. diff --git a/apps/starry/nginx/debug/README.md b/apps/starry/nginx/debug/README.md index 44f936a83f..c8b5509686 100644 --- a/apps/starry/nginx/debug/README.md +++ b/apps/starry/nginx/debug/README.md @@ -5,8 +5,13 @@ This directory stores flexible debug scripts for single issue reproduction and d Current scripts: - `nginx-http-basic-tests.sh`: early HTTP basic script kept for issue-level debugging. +- `nginx-2-0-bad-method-debug.sh`: focused probe for stage 2.0 BAD method (`BAD / HTTP/1.1`) instability. +- `nginx-3-1-short-connection-debug.sh`: focused reproduction for phase31 short-connection timeout behavior. +- `nginx-3-1-x86-timing-debug.sh`: x86_64 timing microbench for process, timeout, and curl loops. Rule: - Debug scripts are free-form and can focus on one syscall or one behavior path. -- Debug scripts are not connected to tgoskits nginx CI entry. +- Debug scripts are not auto-discovered by tgoskits nginx CI. Run them manually through + `cargo xtask starry app qemu -t nginx --arch --qemu-config apps/starry/nginx/qemu/debug/.toml`, + which enters the guest via `/usr/bin/nginx-runner.sh debug `. diff --git a/apps/starry/nginx/debug/nginx-2-0-bad-method-debug.sh b/apps/starry/nginx/debug/nginx-2-0-bad-method-debug.sh new file mode 100644 index 0000000000..c5f1545e88 --- /dev/null +++ b/apps/starry/nginx/debug/nginx-2-0-bad-method-debug.sh @@ -0,0 +1,110 @@ +#!/bin/sh +set -eu + +BASE=/tmp/nginx-phase2-bad-method +CONF="$BASE/conf/http-basic.conf" +WWW="$BASE/www" +OUT="$BASE/out" +LOGDIR="$BASE/logs" +TIMEOUT_CMD= + +log() { printf 'NGINX_BAD_METHOD_DEBUG_LOG: %s\n' "$*"; } +fail() { printf 'NGINX_BAD_METHOD_DEBUG_FAILED\n'; log "$*"; exit 1; } + +init_timeout_cmd() { + if command -v timeout >/dev/null 2>&1; then TIMEOUT_CMD='timeout'; return; fi + if busybox timeout 2>&1 | grep -qi 'usage'; then TIMEOUT_CMD='busybox timeout'; return; fi + fail "timeout command not available" +} + +run_with_timeout() { sec=$1; shift; $TIMEOUT_CMD "$sec" "$@"; } + +cleanup_nginx() { + killall -q nginx 2>/dev/null || true + sleep 1 + killall -q -9 nginx 2>/dev/null || true +} + +prepare_packages() { + repo_file=/etc/apk/repositories + original_repos="$(cat "$repo_file")" + for mirror in https://mirrors.cernet.edu.cn/alpine https://dl-cdn.alpinelinux.org/alpine; do + printf '%s\n' "$original_repos" | sed "s#http://[^/]*/alpine/#$mirror/#g;s#https://[^/]*/alpine/#$mirror/#g" > "$repo_file" + rm -f /lib/apk/db/lock + if run_with_timeout 40 apk --timeout 40 update && run_with_timeout 40 apk --timeout 40 add nginx curl busybox-extras; then return 0; fi + done + return 1 +} + +prepare_tree() { + rm -rf "$BASE" + mkdir -p "$BASE/conf" "$WWW" "$LOGDIR" "$OUT" + printf 'BAD_METHOD_DEBUG_OK\n' > "$WWW/index.html" + cat > "$CONF" <<'EOF' +daemon off; +master_process off; +worker_processes 1; +error_log /tmp/nginx-phase2-bad-method/logs/error.log debug; +pid /tmp/nginx-phase2-bad-method/nginx.pid; +events { worker_connections 64; } +http { include /etc/nginx/mime.types; access_log /tmp/nginx-phase2-bad-method/logs/access.log; server { listen 127.0.0.1:8080; root /tmp/nginx-phase2-bad-method/www; location / { index index.html; } } } +EOF +} + +start_nginx() { + nginx -t -c "$CONF" -p "$BASE/" || return 1 + nginx -c "$CONF" -p "$BASE/" > "$LOGDIR/nginx-stdout.log" 2>&1 & + i=0 + while [ "$i" -lt 6 ]; do + run_with_timeout 5 curl -fsS -o /dev/null http://127.0.0.1:8080/ >/dev/null 2>&1 && return 0 + i=$((i + 1)) + sleep 1 + done + return 1 +} + +probe_bad_method() { + if command -v nc >/dev/null 2>&1; then + NC='nc' + elif busybox nc 2>&1 | grep -qi 'usage'; then + NC='busybox nc' + else + fail "nc not available" + fi + code=$(run_with_timeout 3 curl -sS -o /dev/null -w '%{http_code}' -X BAD http://127.0.0.1:8080/ || true) + [ -n "$code" ] || code='' + log "curl_bad_method_status=$code" + + i=1 + while [ "$i" -le 5 ]; do + out="$OUT/bad-$i.raw" + { printf 'BAD / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n'; } | run_with_timeout 4 sh -c "$NC 127.0.0.1 8080" > "$out" || true + tr -d '\r' < "$out" > "$OUT/bad-$i.norm" + status=$(sed -n '1p' "$OUT/bad-$i.norm" || true) + [ -n "$status" ] || status='' + log "probe=$i status=$status" + i=$((i + 1)) + done + if grep -Eq '^HTTP/1.1 (400|405)' "$OUT"/bad-*.norm; then + printf 'NGINX_BAD_METHOD_DEBUG_PASSED\n' + return 0 + fi + log "no 400/405 hit in 5 probes" + return 1 +} + +init_timeout_cmd +trap cleanup_nginx EXIT INT TERM +prepare_packages || fail "prepare packages" +prepare_tree || fail "prepare tree" +start_nginx || fail "start nginx" +probe_bad_method || { + if [ -f "$LOGDIR/error.log" ]; then + tail_line=$(sed -n '$p' "$LOGDIR/error.log" || true) + [ -n "$tail_line" ] || tail_line='' + log "error_log_tail=$tail_line" + fi + cleanup_nginx + fail "bad method probes" +} +cleanup_nginx diff --git a/apps/starry/nginx/debug/nginx-2-0-bad-method-matrix.sh b/apps/starry/nginx/debug/nginx-2-0-bad-method-matrix.sh new file mode 100644 index 0000000000..0f1550ed3c --- /dev/null +++ b/apps/starry/nginx/debug/nginx-2-0-bad-method-matrix.sh @@ -0,0 +1,127 @@ +#!/bin/sh +set -eu + +BASE=/tmp/nginx-bad-method-matrix +CONF="$BASE/conf/nginx.conf" +LOGDIR="$BASE/logs" +OUT="$BASE/out" +WWW="$BASE/www" +TIMEOUT_CMD= + +log() { printf 'NGINX_BAD_METHOD_MATRIX_LOG: %s\n' "$*"; } +fail() { printf 'NGINX_BAD_METHOD_MATRIX_FAILED\n'; log "$*"; exit 1; } + +init_timeout_cmd() { + if command -v timeout >/dev/null 2>&1; then TIMEOUT_CMD='timeout'; return; fi + if busybox timeout 2>&1 | grep -qi 'usage'; then TIMEOUT_CMD='busybox timeout'; return; fi + fail "timeout command not available" +} + +run_with_timeout() { sec=$1; shift; $TIMEOUT_CMD "$sec" "$@"; } + +cleanup_nginx() { + killall -q nginx 2>/dev/null || true + sleep 1 + killall -q -9 nginx 2>/dev/null || true +} + +prepare_packages() { + repo_file=/etc/apk/repositories + original_repos="$(cat "$repo_file")" + for mirror in https://mirrors.cernet.edu.cn/alpine https://dl-cdn.alpinelinux.org/alpine; do + printf '%s\n' "$original_repos" | sed "s#http://[^/]*/alpine/#$mirror/#g;s#https://[^/]*/alpine/#$mirror/#g" > "$repo_file" + rm -f /lib/apk/db/lock + if run_with_timeout 40 apk --timeout 40 update && run_with_timeout 40 apk --timeout 40 add nginx curl busybox-extras netcat-openbsd; then return 0; fi + done + return 1 +} + +prepare_tree() { + rm -rf "$BASE" + mkdir -p "$BASE/conf" "$LOGDIR" "$OUT" "$WWW" + printf 'ok\n' > "$WWW/index.html" + cat > "$CONF" <<'EOF' +daemon off; +master_process off; +worker_processes 1; +error_log /tmp/nginx-bad-method-matrix/logs/error.log debug; +pid /tmp/nginx-bad-method-matrix/nginx.pid; +events { worker_connections 64; } +http { + include /etc/nginx/mime.types; + keepalive_timeout 5; + access_log /tmp/nginx-bad-method-matrix/logs/access.log; + server { + listen 127.0.0.1:8080; + root /tmp/nginx-bad-method-matrix/www; + location / { index index.html; } + } +} +EOF +} + +start_nginx() { + nginx -t -c "$CONF" -p "$BASE/" || return 1 + nginx -c "$CONF" -p "$BASE/" > "$LOGDIR/nginx.stdout" 2>&1 & + i=0 + while [ "$i" -lt 8 ]; do + run_with_timeout 5 curl -fsS -o /dev/null http://127.0.0.1:8080/index.html >/dev/null 2>&1 && return 0 + i=$((i + 1)) + sleep 1 + done + return 1 +} + +one_probe() { + name=$1 + cmd=$2 + out="$OUT/$name.raw" + run_with_timeout 5 sh -c "$cmd" > "$out" 2>&1 || true + tr -d '\r' < "$out" > "$OUT/$name.norm" + first=$(sed -n '1p' "$OUT/$name.norm" || true) + [ -n "$first" ] || first='' + log "$name first_line=$first" + if grep -Eq '^HTTP/1.1 (400|405)' "$OUT/$name.norm"; then + log "$name hit=400_or_405" + fi +} + +run_matrix() { + code=$(run_with_timeout 3 curl -sS -o /dev/null -w '%{http_code}' http://127.0.0.1:8080/ || true) + [ -n "$code" ] || code='' + log "curl_get_http_code=$code" + code=$(run_with_timeout 3 curl -sS -o /dev/null -w '%{http_code}' -X BAD http://127.0.0.1:8080/ || true) + [ -n "$code" ] || code='' + log "curl_x_bad_http_code=$code" + + one_probe nc_default "printf 'BAD / HTTP/1.1\\r\\nHost: localhost\\r\\nConnection: close\\r\\n\\r\\n' | nc 127.0.0.1 8080" + one_probe nc_w2 "printf 'BAD / HTTP/1.1\\r\\nHost: localhost\\r\\nConnection: close\\r\\n\\r\\n' | nc -w 2 127.0.0.1 8080" + one_probe nc_http10 "printf 'BAD / HTTP/1.0\\r\\nHost: localhost\\r\\nConnection: close\\r\\n\\r\\n' | nc -w 2 127.0.0.1 8080" + one_probe nc_get "printf 'GET / HTTP/1.1\\r\\nHost: localhost\\r\\nConnection: close\\r\\n\\r\\n' | nc -w 2 127.0.0.1 8080" + + if busybox nc 2>&1 | grep -qi 'usage'; then + one_probe bb_nc_bad "printf 'BAD / HTTP/1.1\\r\\nHost: localhost\\r\\nConnection: close\\r\\n\\r\\n' | busybox nc -w 2 127.0.0.1 8080" + one_probe bb_nc_get "printf 'GET / HTTP/1.1\\r\\nHost: localhost\\r\\nConnection: close\\r\\n\\r\\n' | busybox nc -w 2 127.0.0.1 8080" + fi + + if command -v nc.openbsd >/dev/null 2>&1; then + one_probe nco_bad "printf 'BAD / HTTP/1.1\\r\\nHost: localhost\\r\\nConnection: close\\r\\n\\r\\n' | nc.openbsd -w 2 127.0.0.1 8080" + one_probe nco_get "printf 'GET / HTTP/1.1\\r\\nHost: localhost\\r\\nConnection: close\\r\\n\\r\\n' | nc.openbsd -w 2 127.0.0.1 8080" + fi + + if [ -f "$LOGDIR/error.log" ]; then + tail_line=$(sed -n '$p' "$LOGDIR/error.log" || true) + [ -n "$tail_line" ] || tail_line='' + log "error_log_tail=$tail_line" + fi + + printf 'NGINX_BAD_METHOD_MATRIX_DONE\n' +} + +init_timeout_cmd +trap cleanup_nginx EXIT INT TERM +prepare_packages || fail "prepare packages" +prepare_tree || fail "prepare tree" +start_nginx || fail "start nginx" +run_matrix +cleanup_nginx diff --git a/apps/starry/nginx/debug/nginx-3-1-short-connection-debug.sh b/apps/starry/nginx/debug/nginx-3-1-short-connection-debug.sh new file mode 100644 index 0000000000..f042d474bb --- /dev/null +++ b/apps/starry/nginx/debug/nginx-3-1-short-connection-debug.sh @@ -0,0 +1,150 @@ +#!/bin/sh +set -eu + +. /usr/bin/nginx-runner-lib.sh + +BASE=/tmp/nginx-phase31-debug +CONF="$BASE/conf/short-connection.conf" +WWW="$BASE/www" +OUT="$BASE/out" +LOGDIR="$BASE/logs" +TIMEOUT_CMD= +ITERATIONS="${NGINX_SHORT_CONN_DEBUG_ITERATIONS:-120}" +USE_TIMEOUT="${NGINX_SHORT_CONN_DEBUG_USE_TIMEOUT:-1}" + +log() { printf 'NGINX_SHORT_CONN_DEBUG_LOG: %s\n' "$*"; } +fail() { printf 'NGINX_SHORT_CONN_DEBUG_FAILED\n'; log "$*"; exit 1; } + +init_timeout_cmd() { + if command -v timeout >/dev/null 2>&1; then + TIMEOUT_CMD='timeout' + return + fi + if busybox timeout 2>&1 | grep -qi 'usage'; then + TIMEOUT_CMD='busybox timeout' + return + fi + fail "timeout command not available" +} + +run_with_timeout() { + sec=$1 + shift + $TIMEOUT_CMD "$sec" "$@" +} + +run_curl_probe() { + sec=$1 + shift + if [ "$USE_TIMEOUT" = "0" ]; then + "$@" + else + run_with_timeout "$sec" "$@" + fi +} + +cleanup_nginx() { + killall -q nginx 2>/dev/null || true + sleep 1 + killall -q -9 nginx 2>/dev/null || true +} + +prepare_tree() { + rm -rf "$BASE" + mkdir -p "$BASE/conf" "$WWW" "$OUT" "$LOGDIR" + printf 'phase31 short connection debug file\n' > "$WWW/small.txt" + cat > "$CONF" <<'EOF' +daemon off; +master_process off; +worker_processes 1; +error_log /tmp/nginx-phase31-debug/logs/error.log debug; +pid /tmp/nginx-phase31-debug/nginx.pid; +events { worker_connections 64; } +http { include /etc/nginx/mime.types; access_log /tmp/nginx-phase31-debug/logs/access.log; sendfile off; keepalive_timeout 5; server { listen 127.0.0.1:8080; root /tmp/nginx-phase31-debug/www; location / { index index.html; } } } +EOF +} + +dump_state() { + log "nginx_pids=$(pgrep nginx 2>/dev/null | tr '\n' ' ' || true)" + if command -v ss >/dev/null 2>&1; then + ss -tan 2>/dev/null | grep '127.0.0.1:8080' || true + elif command -v netstat >/dev/null 2>&1; then + netstat -tan 2>/dev/null | grep '127.0.0.1:8080' || true + fi + if [ -f "$LOGDIR/access.log" ]; then + log "access_count=$(wc -l < "$LOGDIR/access.log")" + log "access_tail_begin" + tail -n 8 "$LOGDIR/access.log" 2>/dev/null || true + log "access_tail_end" + fi + if [ -f "$LOGDIR/error.log" ]; then + log "error_tail_begin" + tail -n 30 "$LOGDIR/error.log" 2>/dev/null || true + log "error_tail_end" + fi + if [ -f "$LOGDIR/nginx-stdout.log" ]; then + log "nginx_stdout_tail_begin" + tail -n 20 "$LOGDIR/nginx-stdout.log" 2>/dev/null || true + log "nginx_stdout_tail_end" + fi +} + +start_nginx() { + nginx -t -c "$CONF" -p "$BASE/" || return 1 + nginx -c "$CONF" -p "$BASE/" > "$LOGDIR/nginx-stdout.log" 2>&1 & + i=0 + while [ "$i" -lt 10 ]; do + if run_with_timeout 5 curl -fsS -o /dev/null http://127.0.0.1:8080/small.txt >/dev/null 2>&1; then + return 0 + fi + i=$((i + 1)) + sleep 1 + done + return 1 +} + +probe_short_connections() { + log "iterations=$ITERATIONS use_timeout=$USE_TIMEOUT timeout_cmd=$TIMEOUT_CMD" + i=1 + while [ "$i" -le "$ITERATIONS" ]; do + body="$OUT/short-$i.body" + meta="$OUT/short-$i.meta" + err="$OUT/short-$i.err" + set +e + run_curl_probe 10 curl -sS -o "$body" -w 'code=%{http_code} size=%{size_download} time=%{time_total}\n' http://127.0.0.1:8080/small.txt > "$meta" 2> "$err" + rc=$? + set -e + meta_line=$(sed -n '1p' "$meta" 2>/dev/null || true) + [ -n "$meta_line" ] || meta_line='' + if [ "$rc" -ne 0 ]; then + log "failure iteration=$i rc=$rc meta=$meta_line" + if [ -s "$err" ]; then + log "curl_stderr_begin" + tail -n 20 "$err" 2>/dev/null || true + log "curl_stderr_end" + fi + dump_state + return 1 + fi + if ! grep -qx 'phase31 short connection debug file' "$body"; then + size=$(wc -c < "$body" 2>/dev/null || printf '0') + log "bad_body iteration=$i size=$size meta=$meta_line" + dump_state + return 1 + fi + if [ $((i % 10)) -eq 0 ]; then + log "progress iteration=$i meta=$meta_line" + fi + i=$((i + 1)) + done + log "completed iterations=$ITERATIONS" + printf 'NGINX_SHORT_CONN_DEBUG_PASSED\n' +} + +init_timeout_cmd +trap cleanup_nginx EXIT INT TERM +runner_ensure_packages || fail "prepare packages" +prepare_tree || fail "prepare tree" +start_nginx || { dump_state; fail "start nginx"; } +probe_short_connections || fail "short connections" +cleanup_nginx diff --git a/apps/starry/nginx/debug/nginx-3-1-x86-timing-debug.sh b/apps/starry/nginx/debug/nginx-3-1-x86-timing-debug.sh new file mode 100644 index 0000000000..34e0f3dea4 --- /dev/null +++ b/apps/starry/nginx/debug/nginx-3-1-x86-timing-debug.sh @@ -0,0 +1,155 @@ +#!/bin/sh +set -eu + +. /usr/bin/nginx-runner-lib.sh + +BASE=/tmp/nginx-x86-timing-debug +CONF="$BASE/conf/timing.conf" +WWW="$BASE/www" +LOGDIR="$BASE/logs" +TIMEOUT_CMD= +LOOP_ITERS="${NGINX_X86_TIMING_LOOP_ITERS:-100}" +CURL_ITERS="${NGINX_X86_TIMING_CURL_ITERS:-40}" +SLEEP_ITERS="${NGINX_X86_TIMING_SLEEP_ITERS:-5}" + +log() { printf 'NGINX_X86_TIMING_DEBUG_LOG: %s\n' "$*"; } +fail() { printf 'NGINX_X86_TIMING_DEBUG_FAILED\n'; log "$*"; exit 1; } + +now_s() { + cut -d. -f1 /proc/uptime +} + +elapsed_s() { + start=$1 + end=$(now_s) + printf '%s' $((end - start)) +} + +init_timeout_cmd() { + if command -v timeout >/dev/null 2>&1; then + TIMEOUT_CMD='timeout' + return + fi + if busybox timeout 2>&1 | grep -qi 'usage'; then + TIMEOUT_CMD='busybox timeout' + return + fi + fail "timeout command not available" +} + +run_with_timeout() { + sec=$1 + shift + $TIMEOUT_CMD "$sec" "$@" +} + +cleanup_nginx() { + killall -q nginx 2>/dev/null || true + sleep 1 + killall -q -9 nginx 2>/dev/null || true +} + +prepare_nginx() { + rm -rf "$BASE" + mkdir -p "$BASE/conf" "$WWW" "$LOGDIR" + printf 'x86 timing debug file\n' > "$WWW/small.txt" + cat > "$CONF" <<'EOF' +daemon off; +master_process off; +worker_processes 1; +error_log /tmp/nginx-x86-timing-debug/logs/error.log notice; +pid /tmp/nginx-x86-timing-debug/nginx.pid; +events { worker_connections 64; } +http { include /etc/nginx/mime.types; access_log off; sendfile off; keepalive_timeout 5; server { listen 127.0.0.1:8080; root /tmp/nginx-x86-timing-debug/www; location / { index index.html; } } } +EOF + nginx -t -c "$CONF" -p "$BASE/" || return 1 + nginx -c "$CONF" -p "$BASE/" > "$LOGDIR/nginx-stdout.log" 2>&1 & + i=0 + while [ "$i" -lt 10 ]; do + if run_with_timeout 5 curl -fsS -o /dev/null http://127.0.0.1:8080/small.txt >/dev/null 2>&1; then + return 0 + fi + i=$((i + 1)) + sleep 1 + done + return 1 +} + +bench_shell_arithmetic() { + start=$(now_s) + i=0 + while [ "$i" -lt "$LOOP_ITERS" ]; do + i=$((i + 1)) + done + log "bench=shell_arithmetic iterations=$LOOP_ITERS elapsed_s=$(elapsed_s "$start")" +} + +bench_shell_builtin() { + start=$(now_s) + i=0 + while [ "$i" -lt "$LOOP_ITERS" ]; do + : + i=$((i + 1)) + done + log "bench=shell_builtin iterations=$LOOP_ITERS elapsed_s=$(elapsed_s "$start")" +} + +bench_command_loop() { + name=$1 + iterations=$2 + shift 2 + start=$(now_s) + i=0 + while [ "$i" -lt "$iterations" ]; do + "$@" >/dev/null 2>&1 || return 1 + i=$((i + 1)) + done + log "bench=$name iterations=$iterations elapsed_s=$(elapsed_s "$start") cmd=$*" +} + +bench_true() { + bench_command_loop true "$LOOP_ITERS" /bin/true +} + +bench_timeout_true() { + start=$(now_s) + i=0 + while [ "$i" -lt "$LOOP_ITERS" ]; do + run_with_timeout 5 /bin/true || return 1 + i=$((i + 1)) + done + log "bench=timeout_true iterations=$LOOP_ITERS elapsed_s=$(elapsed_s "$start") timeout_cmd=$TIMEOUT_CMD" +} + +bench_curl() { + name=$1 + use_timeout=$2 + start=$(now_s) + i=0 + while [ "$i" -lt "$CURL_ITERS" ]; do + if [ "$use_timeout" = "1" ]; then + run_with_timeout 10 curl -fsS -o /dev/null http://127.0.0.1:8080/small.txt >/dev/null 2>&1 || return 1 + else + curl -fsS -o /dev/null http://127.0.0.1:8080/small.txt >/dev/null 2>&1 || return 1 + fi + i=$((i + 1)) + done + log "bench=$name iterations=$CURL_ITERS elapsed_s=$(elapsed_s "$start")" +} + +init_timeout_cmd +trap cleanup_nginx EXIT INT TERM +runner_ensure_packages || fail "prepare packages" +bench_shell_arithmetic || fail "shell arithmetic" +bench_shell_builtin || fail "shell builtin" +bench_true || fail "true loop" +bench_command_loop busybox_true "$LOOP_ITERS" /bin/busybox true || fail "busybox true loop" +bench_command_loop echo "$LOOP_ITERS" /bin/echo ok || fail "echo loop" +bench_command_loop sleep0 "$LOOP_ITERS" /bin/sleep 0 || fail "sleep0 loop" +bench_command_loop date 20 /bin/date +%s || fail "date loop" +bench_command_loop sleep1 "$SLEEP_ITERS" /bin/sleep 1 || fail "sleep1 loop" +bench_timeout_true || fail "timeout true loop" +prepare_nginx || fail "start nginx" +bench_curl direct_curl 0 || fail "direct curl loop" +bench_curl timeout_curl 1 || fail "timeout curl loop" +printf 'NGINX_X86_TIMING_DEBUG_PASSED\n' diff --git a/apps/starry/nginx/debug/nginx-4-2-sendfile-on-debug.sh b/apps/starry/nginx/debug/nginx-4-2-sendfile-on-debug.sh new file mode 100644 index 0000000000..5106eb6e50 --- /dev/null +++ b/apps/starry/nginx/debug/nginx-4-2-sendfile-on-debug.sh @@ -0,0 +1,105 @@ +#!/bin/sh +set -eu + +BASE=/tmp/nginx-phase42-debug +CONF="$BASE/conf/sendfile-on.conf" +WWW="$BASE/www" +OUT="$BASE/out" +LOGDIR="$BASE/logs" +TIMEOUT_CMD= + +log() { printf 'NGINX_PHASE42_DEBUG_LOG: %s\n' "$*"; } +fail() { printf 'NGINX_PHASE42_DEBUG_FAILED\n'; log "$*"; exit 1; } + +init_timeout_cmd() { + if command -v timeout >/dev/null 2>&1; then TIMEOUT_CMD='timeout'; return; fi + if busybox timeout 2>&1 | grep -qi 'usage'; then TIMEOUT_CMD='busybox timeout'; return; fi + fail "timeout command not available" +} + +run_with_timeout() { sec=$1; shift; $TIMEOUT_CMD "$sec" "$@"; } + +cleanup_nginx() { + killall -q nginx 2>/dev/null || true + sleep 1 + killall -q -9 nginx 2>/dev/null || true +} + +prepare_packages() { + repo_file=/etc/apk/repositories + original_repos="$(cat "$repo_file")" + for mirror in https://mirrors.cernet.edu.cn/alpine https://dl-cdn.alpinelinux.org/alpine; do + printf '%s\n' "$original_repos" | sed "s#http://[^/]*/alpine/#$mirror/#g;s#https://[^/]*/alpine/#$mirror/#g" > "$repo_file" + rm -f /lib/apk/db/lock + if run_with_timeout 40 apk --timeout 40 update && run_with_timeout 40 apk --timeout 40 add nginx curl busybox-extras; then return 0; fi + done + return 1 +} + +prepare_tree() { + rm -rf "$BASE" + mkdir -p "$BASE/conf" "$WWW" "$OUT" "$LOGDIR" + dd if=/dev/zero of="$WWW/large.bin" bs=1024 count=1024 >/dev/null 2>&1 + cat > "$CONF" <<'EOF' +daemon off; +master_process off; +worker_processes 1; +error_log /tmp/nginx-phase42-debug/logs/error.log debug; +pid /tmp/nginx-phase42-debug/nginx.pid; +events { worker_connections 64; } +http { + include /etc/nginx/mime.types; + access_log /tmp/nginx-phase42-debug/logs/access.log; + sendfile on; + keepalive_timeout 5; + server { + listen 127.0.0.1:8080; + root /tmp/nginx-phase42-debug/www; + } +} +EOF +} + +start_nginx() { + nginx -t -c "$CONF" -p "$BASE/" || return 1 + nginx -c "$CONF" -p "$BASE/" > "$LOGDIR/nginx-stdout.log" 2>&1 & + i=0 + while [ "$i" -lt 8 ]; do + run_with_timeout 5 curl -fsS -o /dev/null http://127.0.0.1:8080/large.bin >/dev/null 2>&1 && return 0 + i=$((i + 1)) + sleep 1 + done + return 1 +} + +probe_sendfile_on() { + i=1 + while [ "$i" -le 5 ]; do + out="$OUT/large-$i.bin" + hdr="$OUT/large-$i.h" + if run_with_timeout 15 curl -sS -D "$hdr" -o "$out" http://127.0.0.1:8080/large.bin; then + size=$(wc -c < "$out") + log "probe=$i curl_ok size=$size" + else + size=$(wc -c < "$out" 2>/dev/null || printf '0') + log "probe=$i curl_fail size=$size" + fi + i=$((i + 1)) + done + + if [ -f "$LOGDIR/error.log" ]; then + tail_line=$(sed -n '$p' "$LOGDIR/error.log" || true) + [ -n "$tail_line" ] || tail_line='' + log "error_log_tail=$tail_line" + fi + + printf 'NGINX_PHASE42_DEBUG_DONE\n' +} + +init_timeout_cmd +trap cleanup_nginx EXIT INT TERM +prepare_packages || fail "prepare packages" +prepare_tree || fail "prepare tree" +start_nginx || fail "start nginx" +probe_sendfile_on +cleanup_nginx diff --git a/apps/starry/nginx/nginx-alpine-mirror.sh b/apps/starry/nginx/nginx-alpine-mirror.sh deleted file mode 100644 index 6b9ccb330d..0000000000 --- a/apps/starry/nginx/nginx-alpine-mirror.sh +++ /dev/null @@ -1,53 +0,0 @@ -#!/bin/sh - -set -eu - -NGINX_APK_MIRROR_TIMEOUT_SEC="${NGINX_APK_MIRROR_TIMEOUT_SEC:-45}" -NGINX_APK_REPO_FILE="/tmp/nginx-apk-repositories" -NGINX_APK_ATTEMPT_LOG="/tmp/nginx-apk-attempt.log" - -write_repo_file() { - mirror="$1" - cat >"$NGINX_APK_REPO_FILE" <"$NGINX_APK_ATTEMPT_LOG" 2>&1; then - echo "NGINX_APK_MIRROR_OK: $mirror" - return 0 - fi - - rc=$? - if [ "$rc" -eq 124 ]; then - echo "NGINX_APK_MIRROR_FAIL: $mirror (timeout ${NGINX_APK_MIRROR_TIMEOUT_SEC}s)" - else - echo "NGINX_APK_MIRROR_FAIL: $mirror (apk rc=$rc)" - fi - sed -n '1,120p' "$NGINX_APK_ATTEMPT_LOG" || true - return 1 -} - -nginx_apk_add_with_fallback() { - mirror_cn="https://mirrors.tuna.tsinghua.edu.cn/alpine/latest-stable" - mirror_global="https://dl-cdn.alpinelinux.org/alpine/latest-stable" - - if run_apk_add "$mirror_cn" "$@"; then - return 0 - fi - if run_apk_add "$mirror_global" "$@"; then - return 0 - fi - - echo "NGINX_APK_ALL_MIRRORS_FAILED" - return 1 -} diff --git a/apps/starry/nginx/nginx-cli-tests.sh b/apps/starry/nginx/nginx-cli-tests.sh deleted file mode 100644 index 21eeb50fe5..0000000000 --- a/apps/starry/nginx/nginx-cli-tests.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/sh -set -eu - -cmd=${1:-all} - -case "$cmd" in - smoke) - sh ./smoke/nginx-smoke-tests.sh - ;; - phase1) - sh ./phase/nginx-1-3-lifecycle-tests.sh - ;; - phase2) - sh ./phase/nginx-2-0-http-basic-tests.sh - ;; - all) - sh ./smoke/nginx-smoke-tests.sh - sh ./phase/nginx-1-3-lifecycle-tests.sh - sh ./phase/nginx-2-0-http-basic-tests.sh - printf 'NGINX_CLI_ALL_PASSED\n' - ;; - *) - printf 'usage: %s [smoke|phase1|phase2|all]\n' "$0" - exit 2 - ;; -esac diff --git a/apps/starry/nginx/phase/README.md b/apps/starry/nginx/phase/README.md index e25d2e0c55..fc6987ae7a 100644 --- a/apps/starry/nginx/phase/README.md +++ b/apps/starry/nginx/phase/README.md @@ -10,4 +10,6 @@ Current scripts: Rule: - Each phase script must include all checks of that phase, including checks already covered by smoke. -- Phase scripts are managed for development iteration and are not connected as tgoskits nginx CI entry. +- Phase scripts are not auto-discovered by tgoskits nginx CI. Run them manually through + `cargo xtask starry app qemu -t nginx --arch --qemu-config apps/starry/nginx/qemu/phase/qemu--phaseXX.toml`, + which enters the guest via `/usr/bin/nginx-runner.sh phase phaseXX`. diff --git a/apps/starry/nginx/phase/nginx-0-0-env-rlimit-tests.sh b/apps/starry/nginx/phase/nginx-0-0-env-rlimit-tests.sh new file mode 100644 index 0000000000..a4168c3e24 --- /dev/null +++ b/apps/starry/nginx/phase/nginx-0-0-env-rlimit-tests.sh @@ -0,0 +1,65 @@ +#!/bin/sh +set -eu + +. /usr/bin/nginx-runner-lib.sh + +BASE=/tmp/nginx-phase00 +CONF="$BASE/conf/rlimit.conf" +WWW="$BASE/www" +LOGDIR="$BASE/logs" +TIMEOUT_CMD= + +log() { printf 'NGINX_PHASE00_LOG: %s\n' "$*"; } +fail() { printf 'NGINX_PHASE00_TEST_FAILED\n'; log "$*"; exit 1; } + +init_timeout_cmd() { + if command -v timeout >/dev/null 2>&1; then TIMEOUT_CMD='timeout'; return; fi + if busybox timeout 2>&1 | grep -qi 'usage'; then TIMEOUT_CMD='busybox timeout'; return; fi + fail "timeout command not available" +} + +run_with_timeout() { sec=$1; shift; $TIMEOUT_CMD "$sec" "$@"; } + +prepare_packages() { + runner_ensure_packages || return 1 +} + +prepare_tree() { + rm -rf "$BASE" + mkdir -p "$BASE/conf" "$WWW" "$LOGDIR" + printf 'phase00\n' > "$WWW/index.html" + cat > "$CONF" <<'EOF' +daemon off; +master_process off; +worker_processes 1; +error_log /tmp/nginx-phase00/logs/error.log debug; +pid /tmp/nginx-phase00/nginx.pid; +events { worker_connections 64; } +http { + include /etc/nginx/mime.types; + access_log /tmp/nginx-phase00/logs/access.log; + server { listen 127.0.0.1:8080; root /tmp/nginx-phase00/www; } +} +EOF +} + +test_rlimit_probe() { + before=$(ulimit -n) + [ -n "$before" ] + ulimit -n 1024 + after=$(ulimit -n) + [ "$after" = "1024" ] || [ "$after" -ge 1024 ] + nginx -t -c "$CONF" -p "$BASE/" + ulimit -n "$before" + log "rlimit_nofile_before=$before after=$after" +} + +init_timeout_cmd +# Defensive cleanup: this phase only runs `nginx -t` (no daemon), but reap any +# stray nginx on exit so a failure mid-run cannot leak a process. Timeout is +# owned by the runner, so no in-script watchdog here. +trap 'killall -q nginx 2>/dev/null || true' EXIT INT TERM +prepare_packages || fail "prepare packages" +prepare_tree || fail "prepare tree" +test_rlimit_probe || fail "getrlimit/setrlimit probe" +printf 'NGINX_PHASE00_TEST_PASSED\n' diff --git a/apps/starry/nginx/phase/nginx-1-2-lifecycle-tests.sh b/apps/starry/nginx/phase/nginx-1-2-lifecycle-tests.sh new file mode 100644 index 0000000000..3803d3551d --- /dev/null +++ b/apps/starry/nginx/phase/nginx-1-2-lifecycle-tests.sh @@ -0,0 +1,113 @@ +#!/bin/sh +set -eu + +. /usr/bin/nginx-runner-lib.sh + +BASE=/tmp/nginx-phase12 +CONF_DIR="$BASE/conf" +LOG_DIR="$BASE/logs" +OUT="$BASE/out" +WWW="$BASE/www" +TIMEOUT_CMD= + +log() { printf 'NGINX_PHASE12_LOG: %s\n' "$*"; } +fail() { printf 'NGINX_PHASE12_TEST_FAILED\n'; log "$*"; exit 1; } + +init_timeout_cmd() { + if command -v timeout >/dev/null 2>&1; then TIMEOUT_CMD='timeout'; return; fi + if busybox timeout 2>&1 | grep -qi 'usage'; then TIMEOUT_CMD='busybox timeout'; return; fi + fail "timeout command not available" +} + +run_with_timeout() { sec=$1; shift; $TIMEOUT_CMD "$sec" "$@"; } + +cleanup_nginx() { + killall -q nginx 2>/dev/null || true + sleep 1 + killall -q -9 nginx 2>/dev/null || true +} + +prepare_packages() { + runner_ensure_packages || return 1 +} + +prepare_files() { + rm -rf "$BASE" + mkdir -p "$CONF_DIR" "$LOG_DIR" "$OUT" "$WWW" + printf 'PHASE12_OK\n' > "$WWW/index.html" + cat > "$CONF_DIR/master1.conf" <<'EOF' +daemon off; +master_process on; +worker_processes 1; +error_log /tmp/nginx-phase12/logs/error-master1.log debug; +pid /tmp/nginx-phase12/nginx-master1.pid; +events { worker_connections 64; } +http { include /etc/nginx/mime.types; access_log /tmp/nginx-phase12/logs/access-master1.log; server { listen 127.0.0.1:8081; root /tmp/nginx-phase12/www; location / { index index.html; } } } +EOF +} + +wait_http_ok() { + url=$1 + i=0 + while [ "$i" -lt 6 ]; do + if run_with_timeout 5 curl -fsS "$url" -o "$OUT/http.body" >/dev/null 2>&1; then return 0; fi + i=$((i + 1)) + sleep 1 + done + return 1 +} + +count_nginx_procs() { + if command -v pgrep >/dev/null 2>&1; then + pgrep -xc nginx + return + fi + ps | grep '/usr/sbin/nginx\| nginx$' | grep -v grep | wc -l +} + +assert_no_worker_or_zombie() { + if ps -ef | grep -E 'nginx: worker process|nginx: master process' | grep -v grep >/dev/null 2>&1; then + return 1 + fi + if ps -ef | grep ' Z ' | grep -E 'nginx( |$)' | grep -v grep >/dev/null 2>&1; then + return 1 + fi + return 0 +} + +test_master1_lifecycle() { + nginx -t -c "$CONF_DIR/master1.conf" -p "$BASE/" || return 1 + nginx -c "$CONF_DIR/master1.conf" -p "$BASE/" > "$LOG_DIR/master1.stdout" 2>&1 & + wait_http_ok http://127.0.0.1:8081/ || return 1 + + procs=$(count_nginx_procs) + log "phase1.2 nginx_proc_count_before_reload=$procs" + [ "$procs" -ge 2 ] || return 1 + + run_with_timeout 2 nginx -s reload -c "$CONF_DIR/master1.conf" -p "$BASE/" >/dev/null 2>&1 || return 1 + wait_http_ok http://127.0.0.1:8081/ || return 1 + + procs=$(count_nginx_procs) + log "phase1.2 nginx_proc_count_after_reload=$procs" + [ "$procs" -ge 2 ] || return 1 + + run_with_timeout 2 nginx -s quit -c "$CONF_DIR/master1.conf" -p "$BASE/" >/dev/null 2>&1 || return 1 + + i=0 + while [ "$i" -lt 6 ]; do + if assert_no_worker_or_zombie; then + return 0 + fi + i=$((i + 1)) + sleep 1 + done + return 1 +} + +init_timeout_cmd +trap cleanup_nginx EXIT INT TERM +prepare_packages || fail "prepare packages" +prepare_files || fail "prepare files" +test_master1_lifecycle || fail "phase1.2" +cleanup_nginx +printf 'NGINX_PHASE12_TEST_PASSED\n' diff --git a/apps/starry/nginx/phase/nginx-1-3-lifecycle-tests.sh b/apps/starry/nginx/phase/nginx-1-3-lifecycle-tests.sh index e6a75ecac4..fe25e5d0cd 100644 --- a/apps/starry/nginx/phase/nginx-1-3-lifecycle-tests.sh +++ b/apps/starry/nginx/phase/nginx-1-3-lifecycle-tests.sh @@ -1,6 +1,8 @@ #!/bin/sh set -eu +. /usr/bin/nginx-runner-lib.sh + BASE=/tmp/nginx-phase1 WWW="$BASE/www" CONF_DIR="$BASE/conf" @@ -36,16 +38,7 @@ cleanup_nginx() { } prepare_packages() { - repo_file=/etc/apk/repositories - original_repos="$(cat "$repo_file")" - for mirror in https://mirrors.cernet.edu.cn/alpine https://dl-cdn.alpinelinux.org/alpine; do - printf '%s\n' "$original_repos" | sed "s#http://[^/]*/alpine/#$mirror/#g;s#https://[^/]*/alpine/#$mirror/#g" > "$repo_file" - rm -f /lib/apk/db/lock - if run_with_timeout 40 apk --timeout 40 update && run_with_timeout 40 apk --timeout 40 add nginx curl busybox-extras procps; then - return 0 - fi - done - return 1 + runner_ensure_packages || return 1 } prepare_files() { @@ -85,7 +78,7 @@ wait_http_ok() { url=$1 i=0 while [ "$i" -lt 6 ]; do - if run_with_timeout 1 curl -fsS "$url" -o "$OUT/tmp.body" >/dev/null 2>&1; then + if run_with_timeout 5 curl -fsS "$url" -o "$OUT/tmp.body" >/dev/null 2>&1; then return 0 fi sleep 1 @@ -94,31 +87,28 @@ wait_http_ok() { return 1 } -test_master2_known_issue() { +test_master2() { nginx -t -c "$CONF_DIR/master2.conf" -p "$BASE/" || return 1 nginx -c "$CONF_DIR/master2.conf" -p "$BASE/" > "$LOG_DIR/master2.stdout" 2>&1 & - wait_http_ok http://127.0.0.1:8082/ || { log "KNOWN_ISSUE: phase1.3 startup block"; cleanup_nginx; return 0; } - workers=$(ps -ef | grep 'nginx: worker process' | grep -v grep | wc -l) - log "phase1.3 worker_count=$workers" + wait_http_ok http://127.0.0.1:8082/ || return 1 + if command -v pgrep >/dev/null 2>&1; then + workers=$(pgrep -xc nginx) + else + workers=$(ps | grep '/usr/sbin/nginx\| nginx$' | grep -v grep | wc -l) + fi + log "phase1.3 nginx_proc_count=$workers" + [ "$workers" -ge 3 ] || return 1 i=1 while [ "$i" -le 3 ]; do - run_with_timeout 1 curl -fsS http://127.0.0.1:8082/ -o "$OUT/m2-$i.body" >/dev/null 2>&1 || { - log "KNOWN_ISSUE: phase1.3 request block at $i" - cleanup_nginx - return 0 - } + run_with_timeout 5 curl -fsS http://127.0.0.1:8082/ -o "$OUT/m2-$i.body" >/dev/null 2>&1 || return 1 i=$((i + 1)) done - run_with_timeout 2 nginx -s quit -c "$CONF_DIR/master2.conf" -p "$BASE/" >/dev/null 2>&1 || { - log "KNOWN_ISSUE: phase1.3 quit block" - cleanup_nginx - return 0 - } + run_with_timeout 2 nginx -s quit -c "$CONF_DIR/master2.conf" -p "$BASE/" >/dev/null 2>&1 || return 1 return 0 } init_timeout_cmd -( sleep 90; log "watchdog timeout"; kill -TERM $$ ) & +trap cleanup_nginx EXIT INT TERM prepare_packages || fail "prepare packages" prepare_files || fail "prepare files" nginx -t -c "$CONF_DIR/single.conf" -p "$BASE/" || fail "single config" @@ -129,6 +119,6 @@ nginx -t -c "$CONF_DIR/master1.conf" -p "$BASE/" || fail "master1 config" nginx -c "$CONF_DIR/master1.conf" -p "$BASE/" > "$LOG_DIR/master1.stdout" 2>&1 & wait_http_ok http://127.0.0.1:8081/ || fail "phase1.2" run_with_timeout 2 nginx -s quit -c "$CONF_DIR/master1.conf" -p "$BASE/" >/dev/null 2>&1 || fail "master1 quit" -test_master2_known_issue || fail "phase1.3" +test_master2 || fail "phase1.3" cleanup_nginx printf 'NGINX_PHASE1_TEST_PASSED\n' diff --git a/apps/starry/nginx/phase/nginx-2-0-http-basic-tests.sh b/apps/starry/nginx/phase/nginx-2-0-http-basic-tests.sh index 0cee77f74a..0c846ea0e9 100644 --- a/apps/starry/nginx/phase/nginx-2-0-http-basic-tests.sh +++ b/apps/starry/nginx/phase/nginx-2-0-http-basic-tests.sh @@ -1,6 +1,8 @@ #!/bin/sh set -eu +. /usr/bin/nginx-runner-lib.sh + BASE=/tmp/nginx-phase2 CONF="$BASE/conf/http-basic.conf" WWW="$BASE/www" @@ -22,14 +24,7 @@ run_with_timeout() { sec=$1; shift; $TIMEOUT_CMD "$sec" "$@"; } cleanup_nginx() { killall -q nginx 2>/dev/null || true; sleep 1; killall -q -9 nginx 2>/dev/null || true; } prepare_packages() { - repo_file=/etc/apk/repositories - original_repos="$(cat "$repo_file")" - for mirror in https://mirrors.cernet.edu.cn/alpine https://dl-cdn.alpinelinux.org/alpine; do - printf '%s\n' "$original_repos" | sed "s#http://[^/]*/alpine/#$mirror/#g;s#https://[^/]*/alpine/#$mirror/#g" > "$repo_file" - rm -f /lib/apk/db/lock - if run_with_timeout 40 apk --timeout 40 update && run_with_timeout 40 apk --timeout 40 add nginx curl busybox-extras; then return 0; fi - done - return 1 + runner_ensure_packages || return 1 } prepare_tree() { @@ -52,24 +47,37 @@ EOF start_nginx() { nginx -t -c "$CONF" -p "$BASE/" || return 1 nginx -c "$CONF" -p "$BASE/" > "$LOGDIR/nginx-stdout.log" 2>&1 & - i=0; while [ "$i" -lt 6 ]; do run_with_timeout 1 curl -fsS -o /dev/null http://127.0.0.1:8080/ >/dev/null 2>&1 && return 0; i=$((i+1)); sleep 1; done + i=0 + while [ "$i" -lt 6 ]; do + run_with_timeout 5 curl -fsS -o /dev/null http://127.0.0.1:8080/small.txt >/dev/null 2>&1 && return 0 + i=$((i + 1)) + sleep 1 + done + if [ -f "$LOGDIR/nginx-stdout.log" ]; then + tail_line=$(sed -n '$p' "$LOGDIR/nginx-stdout.log" || true) + [ -n "$tail_line" ] && log "start_nginx stdout tail: $tail_line" + fi return 1 } init_timeout_cmd -( sleep 90; log "watchdog timeout"; kill -TERM $$ ) & +trap cleanup_nginx EXIT INT TERM prepare_packages || fail "prepare packages" prepare_tree || fail "prepare tree" start_nginx || fail "start nginx" -code=$(run_with_timeout 1 curl -sS -o /dev/null -w '%{http_code}' http://127.0.0.1:8080/small.txt || true); [ "$code" = "200" ] || fail "GET /small.txt" -code=$(run_with_timeout 1 curl -sS -o /dev/null -w '%{http_code}' http://127.0.0.1:8080/empty.txt || true); [ "$code" = "200" ] || fail "GET /empty.txt" -code=$(run_with_timeout 1 curl -sS -o "$OUT/dir.body" -w '%{http_code}' http://127.0.0.1:8080/dir/ || true); [ "$code" = "200" ] && grep -qx 'HTTP_BASIC_DIR_INDEX_OK' "$OUT/dir.body" || fail "GET /dir/" -code=$(run_with_timeout 1 curl -sS -o /dev/null -w '%{http_code}' http://127.0.0.1:8080/dir || true); [ "$code" = "301" ] || [ "$code" = "302" ] || fail "GET /dir" +code=$(run_with_timeout 5 curl -sS -o /dev/null -w '%{http_code}' http://127.0.0.1:8080/small.txt || true); [ "$code" = "200" ] || fail "GET /small.txt" +code=$(run_with_timeout 5 curl -sS -o /dev/null -w '%{http_code}' http://127.0.0.1:8080/empty.txt || true); [ "$code" = "200" ] || fail "GET /empty.txt" +code=$(run_with_timeout 5 curl -sS -o "$OUT/dir.body" -w '%{http_code}' http://127.0.0.1:8080/dir/ || true); [ "$code" = "200" ] && grep -qx 'HTTP_BASIC_DIR_INDEX_OK' "$OUT/dir.body" || fail "GET /dir/" +code=$(run_with_timeout 5 curl -sS -o /dev/null -w '%{http_code}' http://127.0.0.1:8080/dir || true); [ "$code" = "301" ] || [ "$code" = "302" ] || fail "GET /dir" if command -v nc >/dev/null 2>&1 || busybox nc 2>&1 | grep -qi 'usage'; then NC='nc'; command -v nc >/dev/null 2>&1 || NC='busybox nc' { printf 'BAD / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n'; } | run_with_timeout 1 sh -c "$NC 127.0.0.1 8080" > "$OUT/bad.raw" || true tr -d '\r' < "$OUT/bad.raw" > "$OUT/bad.norm" - grep -Eq '^HTTP/1.1 (400|405)' "$OUT/bad.norm" || log "KNOWN_ISSUE: BAD method raw request path unstable or blocked" + bad_status=$(sed -n '1p' "$OUT/bad.norm" || true) + if ! grep -Eq '^HTTP/1.1 (400|405)' "$OUT/bad.norm"; then + [ -n "$bad_status" ] || bad_status='' + log "KNOWN_ISSUE: BAD method raw request path unstable or blocked, status=$bad_status" + fi fi cleanup_nginx printf 'NGINX_PHASE2_TEST_PASSED\n' diff --git a/apps/starry/nginx/phase/nginx-3-1-short-connection-tests.sh b/apps/starry/nginx/phase/nginx-3-1-short-connection-tests.sh new file mode 100755 index 0000000000..ab31fad7f4 --- /dev/null +++ b/apps/starry/nginx/phase/nginx-3-1-short-connection-tests.sh @@ -0,0 +1,93 @@ +#!/bin/sh +set -eu + +. /usr/bin/nginx-runner-lib.sh + +BASE=/tmp/nginx-phase31 +CONF="$BASE/conf/short-connection.conf" +WWW="$BASE/www" +OUT="$BASE/out" +LOGDIR="$BASE/logs" +TIMEOUT_CMD= +# x86_64 Starry currently has a high fixed cost for short-lived external +# commands (vfork/exec/exit/wait or user/kernel transition path), tracked in +# debug/ISSUE-005-x86-short-connection-timeout.md. Keep the HTTP assertion the +# same, but give each curl probe enough wall-clock budget for x86. +SHORT_CONN_CURL_TIMEOUT=15 + +log() { printf 'NGINX_PHASE31_LOG: %s\n' "$*"; } +fail() { printf 'NGINX_PHASE31_TEST_FAILED\n'; log "$*"; exit 1; } + +init_timeout_cmd() { + if command -v timeout >/dev/null 2>&1; then + TIMEOUT_CMD='timeout' + return + fi + if busybox timeout 2>&1 | grep -qi 'usage'; then + TIMEOUT_CMD='busybox timeout' + return + fi + fail "timeout command not available" +} + +run_with_timeout() { + sec=$1 + shift + $TIMEOUT_CMD "$sec" "$@" +} + +cleanup_nginx() { + killall -q nginx 2>/dev/null || true + sleep 1 + killall -q -9 nginx 2>/dev/null || true +} + +prepare_packages() { + runner_ensure_packages || return 1 +} + +prepare_tree() { + rm -rf "$BASE" + mkdir -p "$BASE/conf" "$WWW" "$OUT" "$LOGDIR" + printf 'phase31 short connection file\n' > "$WWW/small.txt" + cat > "$CONF" <<'EOF' +daemon off; +master_process off; +worker_processes 1; +error_log /tmp/nginx-phase31/logs/error.log debug; +pid /tmp/nginx-phase31/nginx.pid; +events { worker_connections 64; } +http { include /etc/nginx/mime.types; access_log /tmp/nginx-phase31/logs/access.log; sendfile off; keepalive_timeout 5; server { listen 127.0.0.1:8080; root /tmp/nginx-phase31/www; location / { index index.html; } } } +EOF +} + +start_nginx() { + nginx -t -c "$CONF" -p "$BASE/" || return 1 + nginx -c "$CONF" -p "$BASE/" > "$LOGDIR/nginx-stdout.log" 2>&1 & + i=0 + while [ "$i" -lt 6 ]; do + if run_with_timeout "$SHORT_CONN_CURL_TIMEOUT" curl -fsS -o /dev/null http://127.0.0.1:8080/small.txt >/dev/null 2>&1; then + return 0 + fi + i=$((i + 1)) + sleep 1 + done + return 1 +} + +test_short_connection_100() { + i=1 + while [ "$i" -le 100 ]; do + run_with_timeout "$SHORT_CONN_CURL_TIMEOUT" curl -fsS -o /dev/null http://127.0.0.1:8080/small.txt >/dev/null 2>&1 || return 1 + i=$((i + 1)) + done +} + +init_timeout_cmd +trap cleanup_nginx EXIT INT TERM +prepare_packages || fail "prepare packages" +prepare_tree || fail "prepare tree" +start_nginx || fail "start nginx" +test_short_connection_100 || fail "100 short connections" +cleanup_nginx +printf 'NGINX_PHASE31_TEST_PASSED\n' diff --git a/apps/starry/nginx/phase/nginx-3-2-keepalive-tests.sh b/apps/starry/nginx/phase/nginx-3-2-keepalive-tests.sh new file mode 100644 index 0000000000..a0d45a3441 --- /dev/null +++ b/apps/starry/nginx/phase/nginx-3-2-keepalive-tests.sh @@ -0,0 +1,141 @@ +#!/bin/sh +set -eu + +. /usr/bin/nginx-runner-lib.sh + +BASE=/tmp/nginx-phase32 +CONF="$BASE/conf/keepalive.conf" +WWW="$BASE/www" +OUT="$BASE/out" +LOGDIR="$BASE/logs" +TIMEOUT_CMD= +NC_CMD= + +log() { printf 'NGINX_PHASE32_LOG: %s\n' "$*"; } +fail() { printf 'NGINX_PHASE32_TEST_FAILED\n'; log "$*"; exit 1; } + +init_timeout_cmd() { + if command -v timeout >/dev/null 2>&1; then + TIMEOUT_CMD='timeout' + return + fi + if busybox timeout 2>&1 | grep -qi 'usage'; then + TIMEOUT_CMD='busybox timeout' + return + fi + fail "timeout command not available" +} + +run_with_timeout() { + sec=$1 + shift + $TIMEOUT_CMD "$sec" "$@" +} + +init_nc_cmd() { + if command -v nc.openbsd >/dev/null 2>&1; then + NC_CMD='nc.openbsd -w 2' + return + fi + if command -v nc >/dev/null 2>&1; then + NC_CMD='nc -w 2' + return + fi + if busybox nc 2>&1 | grep -qi 'usage'; then + NC_CMD='busybox nc -w 2' + return + fi + fail "nc command not available" +} + +cleanup_nginx() { + killall -q nginx 2>/dev/null || true + sleep 1 + killall -q -9 nginx 2>/dev/null || true +} + +prepare_packages() { + runner_ensure_packages || return 1 +} + +prepare_tree() { + rm -rf "$BASE" + mkdir -p "$BASE/conf" "$WWW" "$OUT" "$LOGDIR" + printf 'phase32 keepalive file\n' > "$WWW/small.txt" + cat > "$CONF" <<'EOF' +daemon off; +master_process off; +worker_processes 1; +error_log /tmp/nginx-phase32/logs/error.log debug; +pid /tmp/nginx-phase32/nginx.pid; +events { worker_connections 64; } +http { + include /etc/nginx/mime.types; + access_log /tmp/nginx-phase32/logs/access.log; + sendfile off; + keepalive_timeout 5; + server { + listen 127.0.0.1:8080; + root /tmp/nginx-phase32/www; + location / { index index.html; } + } +} +EOF +} + +start_nginx() { + nginx -t -c "$CONF" -p "$BASE/" || return 1 + nginx -c "$CONF" -p "$BASE/" > "$LOGDIR/nginx-stdout.log" 2>&1 & + i=0 + while [ "$i" -lt 6 ]; do + if run_with_timeout 5 curl -fsS -o /dev/null http://127.0.0.1:8080/small.txt >/dev/null 2>&1; then + return 0 + fi + i=$((i + 1)) + sleep 1 + done + return 1 +} + +test_keepalive_two_requests() { + run_with_timeout 10 sh -c "{ \ + printf 'GET /small.txt HTTP/1.1\\r\\nHost: localhost\\r\\nConnection: keep-alive\\r\\n\\r\\n'; \ + printf 'GET /small.txt HTTP/1.1\\r\\nHost: localhost\\r\\nConnection: close\\r\\n\\r\\n'; \ + } | $NC_CMD 127.0.0.1 8080" > "$OUT/keepalive-two.raw" + tr -d '\r' < "$OUT/keepalive-two.raw" > "$OUT/keepalive-two.norm" + count=$(grep -c '^HTTP/1.1 200' "$OUT/keepalive-two.norm" || true) + [ "$count" -eq 2 ] +} + +test_connection_close_behavior() { + run_with_timeout 10 sh -c "{ \ + printf 'GET /small.txt HTTP/1.1\\r\\nHost: localhost\\r\\nConnection: close\\r\\n\\r\\n'; \ + printf 'GET /small.txt HTTP/1.1\\r\\nHost: localhost\\r\\nConnection: keep-alive\\r\\n\\r\\n'; \ + } | $NC_CMD 127.0.0.1 8080" > "$OUT/conn-close.raw" || true + tr -d '\r' < "$OUT/conn-close.raw" > "$OUT/conn-close.norm" + count=$(grep -c '^HTTP/1.1 200' "$OUT/conn-close.norm" || true) + [ "$count" -eq 1 ] +} + +test_idle_timeout_close() { + run_with_timeout 20 sh -c "{ \ + printf 'GET /small.txt HTTP/1.1\\r\\nHost: localhost\\r\\nConnection: keep-alive\\r\\n\\r\\n'; \ + sleep 7; \ + printf 'GET /small.txt HTTP/1.1\\r\\nHost: localhost\\r\\nConnection: close\\r\\n\\r\\n'; \ + } | $NC_CMD 127.0.0.1 8080" > "$OUT/idle-timeout.raw" || true + tr -d '\r' < "$OUT/idle-timeout.raw" > "$OUT/idle-timeout.norm" + count=$(grep -c '^HTTP/1.1 200' "$OUT/idle-timeout.norm" || true) + [ "$count" -eq 1 ] +} + +init_timeout_cmd +init_nc_cmd +trap cleanup_nginx EXIT INT TERM +prepare_packages || fail "prepare packages" +prepare_tree || fail "prepare tree" +start_nginx || fail "start nginx" +test_keepalive_two_requests || fail "same connection two requests keepalive" +test_connection_close_behavior || fail "Connection: close behavior" +test_idle_timeout_close || fail "idle timeout closes keepalive connection" +cleanup_nginx +printf 'NGINX_PHASE32_TEST_PASSED\n' diff --git a/apps/starry/nginx/phase/nginx-3-3-slow-header-tests.sh b/apps/starry/nginx/phase/nginx-3-3-slow-header-tests.sh new file mode 100644 index 0000000000..0f2e66666c --- /dev/null +++ b/apps/starry/nginx/phase/nginx-3-3-slow-header-tests.sh @@ -0,0 +1,123 @@ +#!/bin/sh +set -eu + +. /usr/bin/nginx-runner-lib.sh + +BASE=/tmp/nginx-phase33 +CONF="$BASE/conf/slow-header.conf" +WWW="$BASE/www" +OUT="$BASE/out" +LOGDIR="$BASE/logs" +TIMEOUT_CMD= +NC_CMD= + +log() { printf 'NGINX_PHASE33_LOG: %s\n' "$*"; } +fail() { printf 'NGINX_PHASE33_TEST_FAILED\n'; log "$*"; exit 1; } + +init_timeout_cmd() { + if command -v timeout >/dev/null 2>&1; then TIMEOUT_CMD='timeout'; return; fi + if busybox timeout 2>&1 | grep -qi 'usage'; then TIMEOUT_CMD='busybox timeout'; return; fi + fail "timeout command not available" +} + +run_with_timeout() { sec=$1; shift; $TIMEOUT_CMD "$sec" "$@"; } + +init_nc_cmd() { + if command -v nc.openbsd >/dev/null 2>&1; then NC_CMD='nc.openbsd -w 2'; return; fi + if command -v nc >/dev/null 2>&1; then NC_CMD='nc -w 2'; return; fi + if busybox nc 2>&1 | grep -qi 'usage'; then NC_CMD='busybox nc -w 2'; return; fi + fail "nc command not available" +} + +cleanup_nginx() { + killall -q nginx 2>/dev/null || true + sleep 1 + killall -q -9 nginx 2>/dev/null || true +} + +prepare_packages() { + runner_ensure_packages || return 1 +} + +prepare_tree() { + rm -rf "$BASE" + mkdir -p "$BASE/conf" "$WWW" "$OUT" "$LOGDIR" + printf 'phase33 slow header file\n' > "$WWW/small.txt" + cat > "$CONF" <<'EOF' +daemon off; +master_process off; +worker_processes 1; +error_log /tmp/nginx-phase33/logs/error.log debug; +pid /tmp/nginx-phase33/nginx.pid; +events { worker_connections 64; } +http { + include /etc/nginx/mime.types; + access_log /tmp/nginx-phase33/logs/access.log; + sendfile off; + keepalive_timeout 5; + client_header_timeout 4; + server { + listen 127.0.0.1:8080; + root /tmp/nginx-phase33/www; + location / { index index.html; } + } +} +EOF +} + +start_nginx() { + nginx -t -c "$CONF" -p "$BASE/" || return 1 + nginx -c "$CONF" -p "$BASE/" > "$LOGDIR/nginx-stdout.log" 2>&1 & + i=0 + while [ "$i" -lt 8 ]; do + run_with_timeout 5 curl -fsS -o /dev/null http://127.0.0.1:8080/small.txt >/dev/null 2>&1 && return 0 + i=$((i + 1)) + sleep 1 + done + return 1 +} + +test_slow_header_within_timeout() { + run_with_timeout 12 sh -c "{ \ + printf 'GET /small.txt HTTP/1.1\\r\\n'; \ + sleep 1; \ + printf 'Host: localhost\\r\\n'; \ + sleep 1; \ + printf 'Connection: close\\r\\n\\r\\n'; \ + } | $NC_CMD 127.0.0.1 8080" > "$OUT/slow-ok.raw" + tr -d '\r' < "$OUT/slow-ok.raw" > "$OUT/slow-ok.norm" + grep -Eq '^HTTP/1.1 200' "$OUT/slow-ok.norm" +} + +test_slow_header_timeout_close() { + run_with_timeout 12 sh -c "{ \ + printf 'GET /small.txt HTTP/1.1\\r\\n'; \ + sleep 6; \ + printf 'Host: localhost\\r\\nConnection: close\\r\\n\\r\\n'; \ + } | $NC_CMD 127.0.0.1 8080" > "$OUT/slow-timeout.raw" || true + tr -d '\r' < "$OUT/slow-timeout.raw" > "$OUT/slow-timeout.norm" + ! grep -Eq '^HTTP/1.1 200' "$OUT/slow-timeout.norm" +} + +test_slow_header_not_block_other_conn() { + run_with_timeout 15 sh -c "{ \ + { printf 'GET /small.txt HTTP/1.1\\r\\n'; sleep 6; printf 'Host: localhost\\r\\nConnection: close\\r\\n\\r\\n'; } \ + | $NC_CMD 127.0.0.1 8080 >/dev/null 2>&1 & \ + sleep 1; \ + curl -fsS -o '$OUT/parallel.body' http://127.0.0.1:8080/small.txt >/dev/null; \ + wait; \ + }" + grep -qx 'phase33 slow header file' "$OUT/parallel.body" +} + +init_timeout_cmd +init_nc_cmd +trap cleanup_nginx EXIT INT TERM +prepare_packages || fail "prepare packages" +prepare_tree || fail "prepare tree" +start_nginx || fail "start nginx" +test_slow_header_within_timeout || fail "slow header under timeout" +test_slow_header_timeout_close || fail "slow header timeout close" +test_slow_header_not_block_other_conn || fail "slow header should not block others" +cleanup_nginx +printf 'NGINX_PHASE33_TEST_PASSED\n' diff --git a/apps/starry/nginx/phase/nginx-4-1-sendfile-off-tests.sh b/apps/starry/nginx/phase/nginx-4-1-sendfile-off-tests.sh new file mode 100644 index 0000000000..2a0992d94a --- /dev/null +++ b/apps/starry/nginx/phase/nginx-4-1-sendfile-off-tests.sh @@ -0,0 +1,92 @@ +#!/bin/sh +set -eu + +. /usr/bin/nginx-runner-lib.sh + +BASE=/tmp/nginx-phase41 +CONF="$BASE/conf/sendfile-off.conf" +WWW="$BASE/www" +OUT="$BASE/out" +LOGDIR="$BASE/logs" +TIMEOUT_CMD= + +log() { printf 'NGINX_PHASE41_LOG: %s\n' "$*"; } +fail() { printf 'NGINX_PHASE41_TEST_FAILED\n'; log "$*"; exit 1; } + +init_timeout_cmd() { + if command -v timeout >/dev/null 2>&1; then TIMEOUT_CMD='timeout'; return; fi + if busybox timeout 2>&1 | grep -qi 'usage'; then TIMEOUT_CMD='busybox timeout'; return; fi + fail "timeout command not available" +} + +run_with_timeout() { sec=$1; shift; $TIMEOUT_CMD "$sec" "$@"; } + +cleanup_nginx() { + killall -q nginx 2>/dev/null || true + sleep 1 + killall -q -9 nginx 2>/dev/null || true +} + +prepare_packages() { + runner_ensure_packages || return 1 +} + +prepare_tree() { + rm -rf "$BASE" + mkdir -p "$BASE/conf" "$WWW" "$OUT" "$LOGDIR" + dd if=/dev/zero of="$WWW/large.bin" bs=1024 count=1024 >/dev/null 2>&1 + cat > "$CONF" <<'EOF' +daemon off; +master_process off; +worker_processes 1; +error_log /tmp/nginx-phase41/logs/error.log debug; +pid /tmp/nginx-phase41/nginx.pid; +events { worker_connections 64; } +http { + include /etc/nginx/mime.types; + access_log /tmp/nginx-phase41/logs/access.log; + sendfile off; + server { + listen 127.0.0.1:8080; + root /tmp/nginx-phase41/www; + } +} +EOF +} + +start_nginx() { + nginx -t -c "$CONF" -p "$BASE/" || return 1 + nginx -c "$CONF" -p "$BASE/" > "$LOGDIR/nginx-stdout.log" 2>&1 & + i=0 + while [ "$i" -lt 8 ]; do + run_with_timeout 5 curl -fsS -o /dev/null http://127.0.0.1:8080/large.bin >/dev/null 2>&1 && return 0 + i=$((i + 1)) + sleep 1 + done + return 1 +} + +test_sendfile_off_large_once() { + run_with_timeout 10 curl -fsS -o "$OUT/large-once.bin" http://127.0.0.1:8080/large.bin + [ "$(wc -c < "$OUT/large-once.bin")" -eq 1048576 ] + cmp "$WWW/large.bin" "$OUT/large-once.bin" +} + +test_sendfile_off_large_stability() { + i=1 + while [ "$i" -le 5 ]; do + run_with_timeout 10 curl -fsS -o "$OUT/large-$i.bin" http://127.0.0.1:8080/large.bin || return 1 + cmp "$WWW/large.bin" "$OUT/large-$i.bin" || return 1 + i=$((i + 1)) + done +} + +init_timeout_cmd +trap cleanup_nginx EXIT INT TERM +prepare_packages || fail "prepare packages" +prepare_tree || fail "prepare tree" +start_nginx || fail "start nginx" +test_sendfile_off_large_once || fail "sendfile off large file" +test_sendfile_off_large_stability || fail "sendfile off stability" +cleanup_nginx +printf 'NGINX_PHASE41_TEST_PASSED\n' diff --git a/apps/starry/nginx/phase/nginx-4-2-sendfile-on-tests.sh b/apps/starry/nginx/phase/nginx-4-2-sendfile-on-tests.sh new file mode 100644 index 0000000000..97a19b3dc9 --- /dev/null +++ b/apps/starry/nginx/phase/nginx-4-2-sendfile-on-tests.sh @@ -0,0 +1,92 @@ +#!/bin/sh +set -eu + +. /usr/bin/nginx-runner-lib.sh + +BASE=/tmp/nginx-phase42 +CONF="$BASE/conf/sendfile-on.conf" +WWW="$BASE/www" +OUT="$BASE/out" +LOGDIR="$BASE/logs" +TIMEOUT_CMD= + +log() { printf 'NGINX_PHASE42_LOG: %s\n' "$*"; } +fail() { printf 'NGINX_PHASE42_TEST_FAILED\n'; log "$*"; exit 1; } + +init_timeout_cmd() { + if command -v timeout >/dev/null 2>&1; then TIMEOUT_CMD='timeout'; return; fi + if busybox timeout 2>&1 | grep -qi 'usage'; then TIMEOUT_CMD='busybox timeout'; return; fi + fail "timeout command not available" +} + +run_with_timeout() { sec=$1; shift; $TIMEOUT_CMD "$sec" "$@"; } + +cleanup_nginx() { + killall -q nginx 2>/dev/null || true + sleep 1 + killall -q -9 nginx 2>/dev/null || true +} + +prepare_packages() { + runner_ensure_packages || return 1 +} + +prepare_tree() { + rm -rf "$BASE" + mkdir -p "$BASE/conf" "$WWW" "$OUT" "$LOGDIR" + dd if=/dev/zero of="$WWW/large.bin" bs=1024 count=1024 >/dev/null 2>&1 + cat > "$CONF" <<'EOF' +daemon off; +master_process off; +worker_processes 1; +error_log /tmp/nginx-phase42/logs/error.log debug; +pid /tmp/nginx-phase42/nginx.pid; +events { worker_connections 64; } +http { + include /etc/nginx/mime.types; + access_log /tmp/nginx-phase42/logs/access.log; + sendfile on; + server { + listen 127.0.0.1:8080; + root /tmp/nginx-phase42/www; + } +} +EOF +} + +start_nginx() { + nginx -t -c "$CONF" -p "$BASE/" || return 1 + nginx -c "$CONF" -p "$BASE/" > "$LOGDIR/nginx-stdout.log" 2>&1 & + i=0 + while [ "$i" -lt 8 ]; do + run_with_timeout 5 curl -fsS -o /dev/null http://127.0.0.1:8080/large.bin >/dev/null 2>&1 && return 0 + i=$((i + 1)) + sleep 1 + done + return 1 +} + +test_sendfile_on_large_once() { + run_with_timeout 10 curl -fsS -o "$OUT/large-once.bin" http://127.0.0.1:8080/large.bin + [ "$(wc -c < "$OUT/large-once.bin")" -eq 1048576 ] + cmp "$WWW/large.bin" "$OUT/large-once.bin" +} + +test_sendfile_on_large_stability() { + i=1 + while [ "$i" -le 5 ]; do + run_with_timeout 10 curl -fsS -o "$OUT/large-$i.bin" http://127.0.0.1:8080/large.bin || return 1 + cmp "$WWW/large.bin" "$OUT/large-$i.bin" || return 1 + i=$((i + 1)) + done +} + +init_timeout_cmd +trap cleanup_nginx EXIT INT TERM +prepare_packages || fail "prepare packages" +prepare_tree || fail "prepare tree" +start_nginx || fail "start nginx" +test_sendfile_on_large_once || fail "sendfile on large file" +test_sendfile_on_large_stability || fail "sendfile on stability" +cleanup_nginx +printf 'NGINX_PHASE42_TEST_PASSED\n' diff --git a/apps/starry/nginx/phase/nginx-4-3-range-tests.sh b/apps/starry/nginx/phase/nginx-4-3-range-tests.sh new file mode 100644 index 0000000000..f96182290c --- /dev/null +++ b/apps/starry/nginx/phase/nginx-4-3-range-tests.sh @@ -0,0 +1,99 @@ +#!/bin/sh +set -eu + +. /usr/bin/nginx-runner-lib.sh + +BASE=/tmp/nginx-phase43 +CONF="$BASE/conf/range.conf" +WWW="$BASE/www" +OUT="$BASE/out" +LOGDIR="$BASE/logs" +TIMEOUT_CMD= + +log() { printf 'NGINX_PHASE43_LOG: %s\n' "$*"; } +fail() { printf 'NGINX_PHASE43_TEST_FAILED\n'; log "$*"; exit 1; } + +init_timeout_cmd() { + if command -v timeout >/dev/null 2>&1; then TIMEOUT_CMD='timeout'; return; fi + if busybox timeout 2>&1 | grep -qi 'usage'; then TIMEOUT_CMD='busybox timeout'; return; fi + fail "timeout command not available" +} + +run_with_timeout() { sec=$1; shift; $TIMEOUT_CMD "$sec" "$@"; } + +cleanup_nginx() { + killall -q nginx 2>/dev/null || true + sleep 1 + killall -q -9 nginx 2>/dev/null || true +} + +prepare_packages() { + runner_ensure_packages || return 1 +} + +prepare_tree() { + rm -rf "$BASE" + mkdir -p "$BASE/conf" "$WWW" "$OUT" "$LOGDIR" + dd if=/dev/zero of="$WWW/large.bin" bs=1024 count=1024 >/dev/null 2>&1 + cat > "$CONF" <<'EOF' +daemon off; +master_process off; +worker_processes 1; +error_log /tmp/nginx-phase43/logs/error.log debug; +pid /tmp/nginx-phase43/nginx.pid; +events { worker_connections 64; } +http { + include /etc/nginx/mime.types; + access_log /tmp/nginx-phase43/logs/access.log; + sendfile on; + server { + listen 127.0.0.1:8080; + root /tmp/nginx-phase43/www; + } +} +EOF +} + +start_nginx() { + nginx -t -c "$CONF" -p "$BASE/" || return 1 + nginx -c "$CONF" -p "$BASE/" > "$LOGDIR/nginx-stdout.log" 2>&1 & + i=0 + while [ "$i" -lt 8 ]; do + run_with_timeout 5 curl -fsS -o /dev/null http://127.0.0.1:8080/large.bin >/dev/null 2>&1 && return 0 + i=$((i + 1)) + sleep 1 + done + return 1 +} + +test_range_0_15() { + run_with_timeout 10 curl -fsS -D "$OUT/r0-15.h" -H 'Range: bytes=0-15' -o "$OUT/r0-15.bin" http://127.0.0.1:8080/large.bin + [ "$(wc -c < "$OUT/r0-15.bin")" -eq 16 ] + grep -qi '^HTTP/1.1 206' "$OUT/r0-15.h" + grep -qi '^Content-Range: bytes 0-15/1048576' "$OUT/r0-15.h" +} + +test_range_100_199() { + run_with_timeout 10 curl -fsS -D "$OUT/r100-199.h" -H 'Range: bytes=100-199' -o "$OUT/r100-199.bin" http://127.0.0.1:8080/large.bin + [ "$(wc -c < "$OUT/r100-199.bin")" -eq 100 ] + grep -qi '^HTTP/1.1 206' "$OUT/r100-199.h" + grep -qi '^Content-Range: bytes 100-199/1048576' "$OUT/r100-199.h" +} + +test_range_suffix_64() { + run_with_timeout 10 curl -fsS -D "$OUT/r-64.h" -H 'Range: bytes=-64' -o "$OUT/r-64.bin" http://127.0.0.1:8080/large.bin + [ "$(wc -c < "$OUT/r-64.bin")" -eq 64 ] + grep -qi '^HTTP/1.1 206' "$OUT/r-64.h" + grep -qi '^Content-Range: bytes 1048512-1048575/1048576' "$OUT/r-64.h" +} + +init_timeout_cmd +trap cleanup_nginx EXIT INT TERM +prepare_packages || fail "prepare packages" +prepare_tree || fail "prepare tree" +start_nginx || fail "start nginx" +test_range_0_15 || fail "range bytes=0-15" +test_range_100_199 || fail "range bytes=100-199" +test_range_suffix_64 || fail "range bytes=-64" +cleanup_nginx +printf 'NGINX_PHASE43_TEST_PASSED\n' diff --git a/apps/starry/nginx/phase/nginx-5-0-request-body-tests.sh b/apps/starry/nginx/phase/nginx-5-0-request-body-tests.sh new file mode 100644 index 0000000000..5431beed74 --- /dev/null +++ b/apps/starry/nginx/phase/nginx-5-0-request-body-tests.sh @@ -0,0 +1,98 @@ +#!/bin/sh +set -eu + +. /usr/bin/nginx-runner-lib.sh + +BASE=/tmp/nginx-phase50 +CONF="$BASE/conf/request-body.conf" +WWW="$BASE/www" +OUT="$BASE/out" +LOGDIR="$BASE/logs" +TIMEOUT_CMD= + +log() { printf 'NGINX_PHASE50_LOG: %s\n' "$*"; } +fail() { printf 'NGINX_PHASE50_TEST_FAILED\n'; log "$*"; exit 1; } + +init_timeout_cmd() { + if command -v timeout >/dev/null 2>&1; then TIMEOUT_CMD='timeout'; return; fi + if busybox timeout 2>&1 | grep -qi 'usage'; then TIMEOUT_CMD='busybox timeout'; return; fi + fail "timeout command not available" +} + +run_with_timeout() { sec=$1; shift; $TIMEOUT_CMD "$sec" "$@"; } + +cleanup_nginx() { + killall -q nginx 2>/dev/null || true + sleep 1 + killall -q -9 nginx 2>/dev/null || true +} + +prepare_packages() { + runner_ensure_packages || return 1 +} + +prepare_tree() { + rm -rf "$BASE" + mkdir -p "$BASE/conf" "$WWW" "$OUT" "$LOGDIR" "$BASE/client_temp" + printf 'phase50 body test file\n' > "$WWW/index.html" + cat > "$CONF" <<'EOF' +daemon off; +master_process off; +worker_processes 1; +error_log /tmp/nginx-phase50/logs/error.log debug; +pid /tmp/nginx-phase50/nginx.pid; +events { worker_connections 64; } +http { + include /etc/nginx/mime.types; + access_log /tmp/nginx-phase50/logs/access.log; + client_max_body_size 4k; + client_body_buffer_size 1k; + client_body_temp_path /tmp/nginx-phase50/client_temp; + server { + listen 127.0.0.1:8080; + root /tmp/nginx-phase50/www; + location / { index index.html; } + } +} +EOF +} + +start_nginx() { + nginx -t -c "$CONF" -p "$BASE/" || return 1 + nginx -c "$CONF" -p "$BASE/" > "$LOGDIR/nginx-stdout.log" 2>&1 & + i=0 + while [ "$i" -lt 8 ]; do + run_with_timeout 5 curl -fsS -o /dev/null http://127.0.0.1:8080/ >/dev/null 2>&1 && return 0 + i=$((i + 1)) + sleep 1 + done + return 1 +} + +test_post_small() { + code=$(run_with_timeout 8 curl -sS -o "$OUT/post-small.body" -w '%{http_code}' -X POST --data 'abc' http://127.0.0.1:8080/ || true) + [ "$code" = "405" ] || [ "$code" = "404" ] || [ "$code" = "200" ] +} + +test_post_over_buffer_path() { + dd if=/dev/zero of="$OUT/post-over-buffer.bin" bs=1024 count=2 >/dev/null 2>&1 + code=$(run_with_timeout 12 curl -sS -o "$OUT/post-over-buffer.body" -w '%{http_code}' -X POST --data-binary "@$OUT/post-over-buffer.bin" http://127.0.0.1:8080/ || true) + [ "$code" = "405" ] || [ "$code" = "404" ] || [ "$code" = "200" ] || [ "$code" = "413" ] +} + +test_post_too_large_413() { + dd if=/dev/zero of="$OUT/post-too-large.bin" bs=1024 count=8 >/dev/null 2>&1 + code=$(run_with_timeout 12 curl -sS -o "$OUT/post-too-large.body" -w '%{http_code}' -X POST --data-binary "@$OUT/post-too-large.bin" http://127.0.0.1:8080/ || true) + [ "$code" = "413" ] +} + +init_timeout_cmd +trap cleanup_nginx EXIT INT TERM +prepare_packages || fail "prepare packages" +prepare_tree || fail "prepare tree" +start_nginx || fail "start nginx" +test_post_small || fail "small POST" +test_post_over_buffer_path || fail "over buffer POST path" +test_post_too_large_413 || fail "too large POST 413" +cleanup_nginx +printf 'NGINX_PHASE50_TEST_PASSED\n' diff --git a/apps/starry/nginx/phase/nginx-6-0-log-fs-tests.sh b/apps/starry/nginx/phase/nginx-6-0-log-fs-tests.sh new file mode 100644 index 0000000000..55bd85acc2 --- /dev/null +++ b/apps/starry/nginx/phase/nginx-6-0-log-fs-tests.sh @@ -0,0 +1,158 @@ +#!/bin/sh +set -eu + +. /usr/bin/nginx-runner-lib.sh + +BASE=/tmp/nginx-phase60 +CONF="$BASE/conf/log-fs.conf" +CONF_PREFIX="$BASE/conf/log-fs-prefix.conf" +WWW="$BASE/www" +OUT="$BASE/out" +LOGDIR="$BASE/logs" +TIMEOUT_CMD= + +log() { printf 'NGINX_PHASE60_LOG: %s\n' "$*"; } +fail() { printf 'NGINX_PHASE60_TEST_FAILED\n'; log "$*"; exit 1; } + +init_timeout_cmd() { + if command -v timeout >/dev/null 2>&1; then TIMEOUT_CMD='timeout'; return; fi + if busybox timeout 2>&1 | grep -qi 'usage'; then TIMEOUT_CMD='busybox timeout'; return; fi + fail "timeout command not available" +} + +run_with_timeout() { sec=$1; shift; $TIMEOUT_CMD "$sec" "$@"; } + +cleanup_nginx() { + killall -q nginx 2>/dev/null || true + sleep 1 + killall -q -9 nginx 2>/dev/null || true +} + +prepare_packages() { + runner_ensure_packages || return 1 +} + +prepare_tree() { + rm -rf "$BASE" + mkdir -p "$BASE/conf" "$WWW" "$OUT" "$LOGDIR" + printf 'phase60 log fs test\n' > "$WWW/index.html" + cat > "$CONF" <<'EOF' +daemon off; +master_process off; +worker_processes 1; +error_log /tmp/nginx-phase60/logs/error.log debug; +pid /tmp/nginx-phase60/nginx.pid; +events { worker_connections 64; } +http { + include /etc/nginx/mime.types; + access_log /tmp/nginx-phase60/logs/access.log; + server { + listen 127.0.0.1:8080; + root /tmp/nginx-phase60/www; + location / { index index.html; } + } +} +EOF +} + +start_nginx() { + nginx -t -c "$CONF" -p "$BASE/" || return 1 + nginx -c "$CONF" -p "$BASE/" > "$LOGDIR/nginx-stdout.log" 2>&1 & + i=0 + while [ "$i" -lt 8 ]; do + run_with_timeout 5 curl -fsS -o /dev/null http://127.0.0.1:8080/ >/dev/null 2>&1 && return 0 + i=$((i + 1)) + sleep 1 + done + return 1 +} + +test_access_log_line_growth() { + before=$(wc -l < "$LOGDIR/access.log" 2>/dev/null || printf '0') + run_with_timeout 5 curl -fsS -o /dev/null http://127.0.0.1:8080/ + run_with_timeout 5 curl -fsS -o /dev/null http://127.0.0.1:8080/ + after=$(wc -l < "$LOGDIR/access.log" 2>/dev/null || printf '0') + [ "$after" -ge $((before + 2)) ] +} + +test_error_log_writable() { + test -f "$LOGDIR/error.log" +} + +test_pid_file_present() { + test -s "$BASE/nginx.pid" +} + +test_reopen_logs() { + run_with_timeout 5 nginx -s reopen -c "$CONF" -p "$BASE/" + run_with_timeout 5 curl -fsS -o /dev/null http://127.0.0.1:8080/ + test -s "$LOGDIR/access.log" +} + +test_pid_removed_after_stop() { + run_with_timeout 5 nginx -s quit -c "$CONF" -p "$BASE/" + i=0 + while [ "$i" -lt 6 ]; do + [ ! -e "$BASE/nginx.pid" ] && return 0 + sleep 1 + i=$((i + 1)) + done + return 1 +} + +prepare_prefix_conf() { + mkdir -p "$BASE/prefix/conf" "$BASE/prefix/www" + printf 'phase60 prefix test\n' > "$BASE/prefix/www/index.html" + cat > "$CONF_PREFIX" <<'EOF' +daemon off; +master_process off; +worker_processes 1; +error_log logs/error.log debug; +pid run/nginx.pid; +events { worker_connections 64; } +http { + include /etc/nginx/mime.types; + access_log logs/access.log; + server { + listen 127.0.0.1:8081; + root www; + location / { index index.html; } + } +} +EOF +} + +test_prefix_relative_paths() { + mkdir -p "$BASE/prefix/logs" "$BASE/prefix/run" + nginx -t -c "$CONF_PREFIX" -p "$BASE/prefix/" || return 1 + nginx -c "$CONF_PREFIX" -p "$BASE/prefix/" > "$LOGDIR/nginx-prefix-stdout.log" 2>&1 & + i=0 + while [ "$i" -lt 6 ]; do + if run_with_timeout 5 curl -fsS -o "$OUT/prefix.body" http://127.0.0.1:8081/ >/dev/null 2>&1; then + break + fi + i=$((i + 1)) + sleep 1 + done + [ "$i" -lt 6 ] || return 1 + grep -qx 'phase60 prefix test' "$OUT/prefix.body" + test -s "$BASE/prefix/logs/access.log" + test -s "$BASE/prefix/run/nginx.pid" + run_with_timeout 5 nginx -s quit -c "$CONF_PREFIX" -p "$BASE/prefix/" + return 0 +} + +init_timeout_cmd +trap cleanup_nginx EXIT INT TERM +prepare_packages || fail "prepare packages" +prepare_tree || fail "prepare tree" +start_nginx || fail "start nginx" +test_access_log_line_growth || fail "access log line growth" +test_error_log_writable || fail "error log writable" +test_pid_file_present || fail "pid file present" +test_reopen_logs || fail "reopen logs" +test_pid_removed_after_stop || fail "pid removed after stop" +prepare_prefix_conf || fail "prepare prefix conf" +test_prefix_relative_paths || fail "prefix relative paths" +cleanup_nginx +printf 'NGINX_PHASE60_TEST_PASSED\n' diff --git a/apps/starry/nginx/phase/nginx-7-0-signal-lifecycle-tests.sh b/apps/starry/nginx/phase/nginx-7-0-signal-lifecycle-tests.sh new file mode 100644 index 0000000000..824bd01f61 --- /dev/null +++ b/apps/starry/nginx/phase/nginx-7-0-signal-lifecycle-tests.sh @@ -0,0 +1,124 @@ +#!/bin/sh +set -eu + +. /usr/bin/nginx-runner-lib.sh + +BASE=/tmp/nginx-phase70 +CONF="$BASE/conf/signal-lifecycle.conf" +WWW="$BASE/www" +OUT="$BASE/out" +LOGDIR="$BASE/logs" +TIMEOUT_CMD= +MASTER_PID= + +log() { printf 'NGINX_PHASE70_LOG: %s\n' "$*"; } +fail() { printf 'NGINX_PHASE70_TEST_FAILED\n'; log "$*"; exit 1; } + +init_timeout_cmd() { + if command -v timeout >/dev/null 2>&1; then TIMEOUT_CMD='timeout'; return; fi + if busybox timeout 2>&1 | grep -qi 'usage'; then TIMEOUT_CMD='busybox timeout'; return; fi + fail "timeout command not available" +} + +run_with_timeout() { sec=$1; shift; $TIMEOUT_CMD "$sec" "$@"; } + +cleanup_nginx() { + killall -q nginx 2>/dev/null || true + sleep 1 + killall -q -9 nginx 2>/dev/null || true +} + +prepare_packages() { + runner_ensure_packages || return 1 +} + +prepare_tree() { + rm -rf "$BASE" + mkdir -p "$BASE/conf" "$WWW" "$OUT" "$LOGDIR" + printf 'phase70 signal lifecycle\n' > "$WWW/index.html" + cat > "$CONF" <<'EOF' +daemon off; +master_process on; +worker_processes 1; +error_log /tmp/nginx-phase70/logs/error.log debug; +pid /tmp/nginx-phase70/nginx.pid; +events { worker_connections 64; } +http { + include /etc/nginx/mime.types; + access_log /tmp/nginx-phase70/logs/access.log; + server { + listen 127.0.0.1:8080; + root /tmp/nginx-phase70/www; + location / { index index.html; } + } +} +EOF +} + +start_nginx() { + nginx -t -c "$CONF" -p "$BASE/" || return 1 + nginx -c "$CONF" -p "$BASE/" > "$LOGDIR/nginx-stdout.log" 2>&1 & + i=0 + while [ "$i" -lt 8 ]; do + run_with_timeout 5 curl -fsS -o /dev/null http://127.0.0.1:8080/ >/dev/null 2>&1 && break + i=$((i + 1)) + sleep 1 + done + [ "$i" -lt 8 ] || return 1 + MASTER_PID=$(cat "$BASE/nginx.pid") + [ -n "$MASTER_PID" ] +} + +test_stop_fast_exit() { + run_with_timeout 5 nginx -s stop -c "$CONF" -p "$BASE/" + i=0 + while [ "$i" -lt 6 ]; do + if ! kill -0 "$MASTER_PID" 2>/dev/null; then return 0; fi + sleep 1 + i=$((i + 1)) + done + return 1 +} + +restart_nginx() { + start_nginx +} + +test_reload_works() { + run_with_timeout 5 nginx -s reload -c "$CONF" -p "$BASE/" + sleep 1 + run_with_timeout 5 curl -fsS -o "$OUT/reload.body" http://127.0.0.1:8080/ + grep -qx 'phase70 signal lifecycle' "$OUT/reload.body" +} + +test_reopen_works() { + run_with_timeout 5 nginx -s reopen -c "$CONF" -p "$BASE/" + run_with_timeout 5 curl -fsS -o /dev/null http://127.0.0.1:8080/ + test -s "$LOGDIR/access.log" +} + +test_worker_kill_recover() { + old_worker=$(ps | grep 'nginx: worker process' | grep -v grep | awk '{print $1}' | sed -n '1p') + [ -n "$old_worker" ] + kill -TERM "$old_worker" + sleep 2 + new_worker=$(ps | grep 'nginx: worker process' | grep -v grep | awk '{print $1}' | sed -n '1p') + [ -n "$new_worker" ] + [ "$new_worker" != "$old_worker" ] + kill -0 "$MASTER_PID" + run_with_timeout 5 curl -fsS -o "$OUT/after-worker-kill.body" http://127.0.0.1:8080/ + grep -qx 'phase70 signal lifecycle' "$OUT/after-worker-kill.body" +} + +init_timeout_cmd +trap cleanup_nginx EXIT INT TERM +prepare_packages || fail "prepare packages" +prepare_tree || fail "prepare tree" +start_nginx || fail "start nginx" +test_stop_fast_exit || fail "nginx -s stop fast exit" +restart_nginx || fail "restart nginx" +test_reload_works || fail "nginx -s reload" +test_reopen_works || fail "nginx -s reopen" +test_worker_kill_recover || fail "worker kill recover" +cleanup_nginx +printf 'NGINX_PHASE70_TEST_PASSED\n' diff --git a/apps/starry/nginx/phase/nginx-9-0-config-feature-tests.sh b/apps/starry/nginx/phase/nginx-9-0-config-feature-tests.sh new file mode 100644 index 0000000000..5cce8fe838 --- /dev/null +++ b/apps/starry/nginx/phase/nginx-9-0-config-feature-tests.sh @@ -0,0 +1,218 @@ +#!/bin/sh +set -eu + +. /usr/bin/nginx-runner-lib.sh + +BASE=/tmp/nginx-phase90 +CONF="$BASE/conf/config-feature.conf" +CONF_IPV6="$BASE/conf/ipv6.conf" +CONF_UNIX="$BASE/conf/unix.conf" +WWW="$BASE/www" +OUT="$BASE/out" +LOGDIR="$BASE/logs" +TIMEOUT_CMD= + +log() { printf 'NGINX_PHASE90_LOG: %s\n' "$*"; } +fail() { printf 'NGINX_PHASE90_TEST_FAILED\n'; log "$*"; exit 1; } + +init_timeout_cmd() { + if command -v timeout >/dev/null 2>&1; then TIMEOUT_CMD='timeout'; return; fi + if busybox timeout 2>&1 | grep -qi 'usage'; then TIMEOUT_CMD='busybox timeout'; return; fi + fail "timeout command not available" +} + +run_with_timeout() { sec=$1; shift; $TIMEOUT_CMD "$sec" "$@"; } + +cleanup_nginx() { + killall -q nginx 2>/dev/null || true + sleep 1 + killall -q -9 nginx 2>/dev/null || true +} + +prepare_packages() { + runner_ensure_packages || return 1 +} + +prepare_tree() { + rm -rf "$BASE" + mkdir -p "$BASE/conf" "$WWW/dir1" "$WWW/dir2" "$WWW/errors" "$WWW/alias-src" "$OUT" "$LOGDIR" + printf 'phase90 index\n' > "$WWW/index.html" + printf 'auto file\n' > "$WWW/dir1/a.txt" + printf 'try files ok\n' > "$WWW/dir2/fallback.txt" + printf 'custom 404 page\n' > "$WWW/errors/404.html" + printf 'alias data\n' > "$WWW/alias-src/file.txt" + # bigger text to make gzip behavior visible. + dd if=/dev/zero bs=1024 count=64 2>/dev/null | tr '\0' 'A' > "$WWW/large.txt" + cat > "$CONF" <<'EOF' +daemon off; +master_process off; +worker_processes 1; +error_log /tmp/nginx-phase90/logs/error.log debug; +pid /tmp/nginx-phase90/nginx.pid; +events { worker_connections 64; } +http { + include /etc/nginx/mime.types; + default_type text/plain; + access_log /tmp/nginx-phase90/logs/access.log; + + server { + listen 127.0.0.1:8080; + root /tmp/nginx-phase90/www; + + location / { index index.html; } + + location /auto/ { + alias /tmp/nginx-phase90/www/dir1/; + autoindex on; + } + + location /try { + try_files /no-file /dir2/fallback.txt =404; + } + + error_page 404 /errors/404.html; + + location /alias/ { + alias /tmp/nginx-phase90/www/alias-src/; + } + + location /gzip-off { + gzip off; + try_files /large.txt =404; + } + + location /gzip-on { + gzip on; + gzip_min_length 1; + gzip_types text/plain; + try_files /large.txt =404; + } + } +} +EOF + + cat > "$CONF_IPV6" <<'EOF' +daemon off; +master_process off; +worker_processes 1; +error_log /tmp/nginx-phase90/logs/error-ipv6.log debug; +pid /tmp/nginx-phase90/nginx-ipv6.pid; +events { worker_connections 64; } +http { + include /etc/nginx/mime.types; + access_log /tmp/nginx-phase90/logs/access-ipv6.log; + server { + listen [::1]:8081; + root /tmp/nginx-phase90/www; + location / { index index.html; } + } +} +EOF + + cat > "$CONF_UNIX" <<'EOF' +daemon off; +master_process off; +worker_processes 1; +error_log /tmp/nginx-phase90/logs/error-unix.log debug; +pid /tmp/nginx-phase90/nginx-unix.pid; +events { worker_connections 64; } +http { + include /etc/nginx/mime.types; + access_log /tmp/nginx-phase90/logs/access-unix.log; + server { + listen unix:/tmp/nginx-phase90/nginx.sock; + root /tmp/nginx-phase90/www; + location / { index index.html; } + } +} +EOF +} + +start_nginx() { + nginx -t -c "$CONF" -p "$BASE/" || return 1 + nginx -c "$CONF" -p "$BASE/" > "$LOGDIR/nginx-stdout.log" 2>&1 & + i=0 + while [ "$i" -lt 8 ]; do + run_with_timeout 5 curl -fsS -o /dev/null http://127.0.0.1:8080/ >/dev/null 2>&1 && return 0 + i=$((i + 1)) + sleep 1 + done + return 1 +} + +test_autoindex() { + run_with_timeout 6 curl -fsS -o "$OUT/auto.body" http://127.0.0.1:8080/auto/ + grep -q 'a.txt' "$OUT/auto.body" +} + +test_try_files() { + run_with_timeout 6 curl -fsS -o "$OUT/try.body" http://127.0.0.1:8080/try + grep -qx 'try files ok' "$OUT/try.body" +} + +test_error_page() { + code=$(run_with_timeout 6 curl -sS -o "$OUT/error-page.body" -w '%{http_code}' http://127.0.0.1:8080/definitely-missing || true) + [ "$code" = "404" ] + grep -qx 'custom 404 page' "$OUT/error-page.body" +} + +test_alias() { + run_with_timeout 6 curl -fsS -o "$OUT/alias.body" http://127.0.0.1:8080/alias/file.txt + grep -qx 'alias data' "$OUT/alias.body" +} + +test_gzip_off_on() { + run_with_timeout 8 curl -fsS -D "$OUT/gzip-off.h" -H 'Accept-Encoding: gzip' -o /dev/null http://127.0.0.1:8080/gzip-off + ! grep -qi '^Content-Encoding: gzip' "$OUT/gzip-off.h" + + run_with_timeout 8 curl -fsS -D "$OUT/gzip-on.h" -H 'Accept-Encoding: gzip' -o /dev/null http://127.0.0.1:8080/gzip-on + grep -qi '^Content-Encoding: gzip' "$OUT/gzip-on.h" +} + +test_ipv6_listen() { + cleanup_nginx + nginx -t -c "$CONF_IPV6" -p "$BASE/" || return 1 + nginx -c "$CONF_IPV6" -p "$BASE/" > "$LOGDIR/nginx-ipv6-stdout.log" 2>&1 & + i=0 + while [ "$i" -lt 6 ]; do + if run_with_timeout 2 curl -g -6 -fsS -o "$OUT/ipv6.body" http://[::1]:8081/ >/dev/null 2>&1; then + break + fi + i=$((i + 1)) + sleep 1 + done + [ "$i" -lt 6 ] || return 1 + grep -qx 'phase90 index' "$OUT/ipv6.body" +} + +test_unix_socket_listen() { + cleanup_nginx + rm -f /tmp/nginx-phase90/nginx.sock + nginx -t -c "$CONF_UNIX" -p "$BASE/" || return 1 + nginx -c "$CONF_UNIX" -p "$BASE/" > "$LOGDIR/nginx-unix-stdout.log" 2>&1 & + i=0 + while [ "$i" -lt 6 ]; do + if run_with_timeout 2 curl --unix-socket /tmp/nginx-phase90/nginx.sock -fsS -o "$OUT/unix.body" http://localhost/ >/dev/null 2>&1; then + break + fi + i=$((i + 1)) + sleep 1 + done + [ "$i" -lt 6 ] || return 1 + grep -qx 'phase90 index' "$OUT/unix.body" +} + +init_timeout_cmd +trap cleanup_nginx EXIT INT TERM +prepare_packages || fail "prepare packages" +prepare_tree || fail "prepare tree" +start_nginx || fail "start nginx" +test_autoindex || fail "autoindex on" +test_try_files || fail "try_files" +test_error_page || fail "error_page" +test_alias || fail "alias" +test_gzip_off_on || fail "gzip off/on" +test_ipv6_listen || fail "ipv6 listen" +test_unix_socket_listen || fail "unix domain socket listen" +cleanup_nginx +printf 'NGINX_PHASE90_TEST_PASSED\n' diff --git a/apps/starry/nginx/prebuild.sh b/apps/starry/nginx/prebuild.sh index 56551ed436..25899ab0a6 100644 --- a/apps/starry/nginx/prebuild.sh +++ b/apps/starry/nginx/prebuild.sh @@ -9,5 +9,36 @@ if [[ -z "$overlay_dir" ]]; then exit 1 fi +# Unified runner entry + shared library + apk mirror helper. All test modes +# (smoke/phase/all/stress/debug) go through nginx-runner.sh; the QEMU configs +# only ever set shell_init_cmd = "/usr/bin/nginx-runner.sh ...". +install -Dm0755 "$app_dir/runner/nginx-runner.sh" "$overlay_dir/usr/bin/nginx-runner.sh" +install -Dm0755 "$app_dir/runner/nginx-runner-lib.sh" "$overlay_dir/usr/bin/nginx-runner-lib.sh" +install -Dm0755 "$app_dir/runner/nginx-alpine-mirror.sh" "$overlay_dir/usr/bin/nginx-alpine-mirror.sh" + +# Smoke stage script (unchanged; runner wraps it and emits unified markers). install -Dm0755 "$app_dir/smoke/nginx-smoke-tests.sh" "$overlay_dir/usr/bin/nginx-smoke-tests.sh" -install -Dm0755 "$app_dir/nginx-alpine-mirror.sh" "$overlay_dir/usr/bin/nginx-alpine-mirror.sh" + +# Phase stage scripts, installed under standardized phaseNN guest names that +# match the runner's stage table and the qemu/phase/*.toml shell_init_cmd args. +install -Dm0755 "$app_dir/phase/nginx-0-0-env-rlimit-tests.sh" "$overlay_dir/usr/bin/nginx-phase00-tests.sh" +install -Dm0755 "$app_dir/phase/nginx-1-2-lifecycle-tests.sh" "$overlay_dir/usr/bin/nginx-phase12-tests.sh" +install -Dm0755 "$app_dir/phase/nginx-1-3-lifecycle-tests.sh" "$overlay_dir/usr/bin/nginx-phase13-tests.sh" +install -Dm0755 "$app_dir/phase/nginx-2-0-http-basic-tests.sh" "$overlay_dir/usr/bin/nginx-phase20-tests.sh" +install -Dm0755 "$app_dir/phase/nginx-3-1-short-connection-tests.sh" "$overlay_dir/usr/bin/nginx-phase31-tests.sh" +install -Dm0755 "$app_dir/phase/nginx-3-2-keepalive-tests.sh" "$overlay_dir/usr/bin/nginx-phase32-tests.sh" +install -Dm0755 "$app_dir/phase/nginx-3-3-slow-header-tests.sh" "$overlay_dir/usr/bin/nginx-phase33-tests.sh" +install -Dm0755 "$app_dir/phase/nginx-4-1-sendfile-off-tests.sh" "$overlay_dir/usr/bin/nginx-phase41-tests.sh" +install -Dm0755 "$app_dir/phase/nginx-4-2-sendfile-on-tests.sh" "$overlay_dir/usr/bin/nginx-phase42-tests.sh" +install -Dm0755 "$app_dir/phase/nginx-4-3-range-tests.sh" "$overlay_dir/usr/bin/nginx-phase43-tests.sh" +install -Dm0755 "$app_dir/phase/nginx-5-0-request-body-tests.sh" "$overlay_dir/usr/bin/nginx-phase50-tests.sh" +install -Dm0755 "$app_dir/phase/nginx-6-0-log-fs-tests.sh" "$overlay_dir/usr/bin/nginx-phase60-tests.sh" +install -Dm0755 "$app_dir/phase/nginx-7-0-signal-lifecycle-tests.sh" "$overlay_dir/usr/bin/nginx-phase70-tests.sh" +install -Dm0755 "$app_dir/phase/nginx-9-0-config-feature-tests.sh" "$overlay_dir/usr/bin/nginx-phase90-tests.sh" + +# Debug stage scripts (invoked via `nginx-runner.sh debug `). +install -Dm0755 "$app_dir/debug/nginx-2-0-bad-method-debug.sh" "$overlay_dir/usr/bin/nginx-bad-method-debug.sh" +install -Dm0755 "$app_dir/debug/nginx-2-0-bad-method-matrix.sh" "$overlay_dir/usr/bin/nginx-bad-method-matrix.sh" +install -Dm0755 "$app_dir/debug/nginx-3-1-short-connection-debug.sh" "$overlay_dir/usr/bin/nginx-short-connection-debug.sh" +install -Dm0755 "$app_dir/debug/nginx-3-1-x86-timing-debug.sh" "$overlay_dir/usr/bin/nginx-x86-timing-debug.sh" +install -Dm0755 "$app_dir/debug/nginx-4-2-sendfile-on-debug.sh" "$overlay_dir/usr/bin/nginx-sendfile-on-debug.sh" diff --git a/apps/starry/nginx/qemu-aarch64.toml b/apps/starry/nginx/qemu-aarch64.toml new file mode 100644 index 0000000000..14082f14a9 --- /dev/null +++ b/apps/starry/nginx/qemu-aarch64.toml @@ -0,0 +1,22 @@ +args = [ + "-nographic", + "-m", + "512M", + "-cpu", + "cortex-a53", + "-device", + "virtio-blk-pci,drive=disk0", + "-drive", + "id=disk0,if=none,format=raw,file=${workspace}/tmp/axbuild/rootfs/rootfs-aarch64-alpine.img", + "-device", + "virtio-net-pci,netdev=net0", + "-netdev", + "user,id=net0", +] +uefi = false +to_bin = true +shell_prefix = "root@starry:" +shell_init_cmd = "/usr/bin/nginx-runner.sh smoke" +success_regex = ["(?m)^NGINX_RUNNER_PASSED\\b"] +fail_regex = ["(?i)\\bpanic(?:ked)?\\b", "(?m)^NGINX_RUNNER_FAILED\\b"] +timeout = 3600 diff --git a/apps/starry/nginx/qemu-loongarch64.toml b/apps/starry/nginx/qemu-loongarch64.toml new file mode 100644 index 0000000000..500162257d --- /dev/null +++ b/apps/starry/nginx/qemu-loongarch64.toml @@ -0,0 +1,24 @@ +args = [ + "-machine", + "virt", + "-cpu", + "la464", + "-nographic", + "-m", + "2G", + "-device", + "virtio-blk-pci,drive=disk0", + "-drive", + "id=disk0,if=none,format=raw,file=${workspace}/tmp/axbuild/rootfs/rootfs-loongarch64-alpine.img", + "-device", + "virtio-net-pci,netdev=net0", + "-netdev", + "user,id=net0", +] +uefi = false +to_bin = true +shell_prefix = "root@starry:" +shell_init_cmd = "/usr/bin/nginx-runner.sh smoke" +success_regex = ["(?m)^NGINX_RUNNER_PASSED\\b"] +fail_regex = ["(?i)\\bpanic(?:ked)?\\b", "(?m)^NGINX_RUNNER_FAILED\\b"] +timeout = 3600 diff --git a/apps/starry/nginx/qemu-riscv64.toml b/apps/starry/nginx/qemu-riscv64.toml index a6aacc72b3..fb856c7642 100644 --- a/apps/starry/nginx/qemu-riscv64.toml +++ b/apps/starry/nginx/qemu-riscv64.toml @@ -16,7 +16,7 @@ args = [ uefi = false to_bin = true shell_prefix = "root@starry:" -shell_init_cmd = "/usr/bin/nginx-smoke-tests.sh" -success_regex = ["(?m)^NGINX_APP_SMOKE_PASSED\\s*$"] -fail_regex = ['(?i)\bpanic(?:ked)?\b', "(?m)^NGINX_APP_SMOKE_FAILED"] +shell_init_cmd = "/usr/bin/nginx-runner.sh smoke" +success_regex = ["(?m)^NGINX_RUNNER_PASSED\\b"] +fail_regex = ["(?i)\\bpanic(?:ked)?\\b", "(?m)^NGINX_RUNNER_FAILED\\b"] timeout = 3600 diff --git a/apps/starry/nginx/qemu-x86_64.toml b/apps/starry/nginx/qemu-x86_64.toml new file mode 100644 index 0000000000..1685613103 --- /dev/null +++ b/apps/starry/nginx/qemu-x86_64.toml @@ -0,0 +1,20 @@ +args = [ + "-nographic", + "-m", + "1G", + "-device", + "virtio-blk-pci,drive=disk0", + "-drive", + "id=disk0,if=none,format=raw,file=${workspace}/tmp/axbuild/rootfs/rootfs-x86_64-alpine.img", + "-device", + "virtio-net-pci,netdev=net0", + "-netdev", + "user,id=net0", +] +uefi = false +to_bin = false +shell_prefix = "root@starry:" +shell_init_cmd = "/usr/bin/nginx-runner.sh smoke" +success_regex = ["(?m)^NGINX_RUNNER_PASSED\\b"] +fail_regex = ["(?i)\\bpanic(?:ked)?\\b", "(?m)^NGINX_RUNNER_FAILED\\b"] +timeout = 3600 diff --git a/apps/starry/nginx/qemu/all/qemu-aarch64.toml b/apps/starry/nginx/qemu/all/qemu-aarch64.toml new file mode 100644 index 0000000000..220cf9fad6 --- /dev/null +++ b/apps/starry/nginx/qemu/all/qemu-aarch64.toml @@ -0,0 +1,22 @@ +args = [ + "-nographic", + "-m", + "512M", + "-cpu", + "cortex-a53", + "-device", + "virtio-blk-pci,drive=disk0", + "-drive", + "id=disk0,if=none,format=raw,file=${workspace}/tmp/axbuild/rootfs/rootfs-aarch64-alpine.img", + "-device", + "virtio-net-pci,netdev=net0", + "-netdev", + "user,id=net0", +] +uefi = false +to_bin = true +shell_prefix = "root@starry:" +shell_init_cmd = "/usr/bin/nginx-runner.sh all" +success_regex = ["(?m)^NGINX_RUNNER_PASSED\\b"] +fail_regex = ["(?i)\\bpanic(?:ked)?\\b", "(?m)^NGINX_RUNNER_FAILED\\b"] +timeout = 7200 diff --git a/apps/starry/nginx/qemu/all/qemu-loongarch64.toml b/apps/starry/nginx/qemu/all/qemu-loongarch64.toml new file mode 100644 index 0000000000..350cf87d27 --- /dev/null +++ b/apps/starry/nginx/qemu/all/qemu-loongarch64.toml @@ -0,0 +1,24 @@ +args = [ + "-machine", + "virt", + "-cpu", + "la464", + "-nographic", + "-m", + "2G", + "-device", + "virtio-blk-pci,drive=disk0", + "-drive", + "id=disk0,if=none,format=raw,file=${workspace}/tmp/axbuild/rootfs/rootfs-loongarch64-alpine.img", + "-device", + "virtio-net-pci,netdev=net0", + "-netdev", + "user,id=net0", +] +uefi = false +to_bin = true +shell_prefix = "root@starry:" +shell_init_cmd = "/usr/bin/nginx-runner.sh all" +success_regex = ["(?m)^NGINX_RUNNER_PASSED\\b"] +fail_regex = ["(?i)\\bpanic(?:ked)?\\b", "(?m)^NGINX_RUNNER_FAILED\\b"] +timeout = 7200 diff --git a/apps/starry/nginx/qemu/all/qemu-riscv64.toml b/apps/starry/nginx/qemu/all/qemu-riscv64.toml new file mode 100644 index 0000000000..3a981fea62 --- /dev/null +++ b/apps/starry/nginx/qemu/all/qemu-riscv64.toml @@ -0,0 +1,22 @@ +args = [ + "-nographic", + "-m", + "512M", + "-cpu", + "rv64", + "-device", + "virtio-blk-pci,drive=disk0", + "-drive", + "id=disk0,if=none,format=raw,file=${workspace}/tmp/axbuild/rootfs/rootfs-riscv64-alpine.img", + "-device", + "virtio-net-pci,netdev=net0", + "-netdev", + "user,id=net0", +] +uefi = false +to_bin = true +shell_prefix = "root@starry:" +shell_init_cmd = "/usr/bin/nginx-runner.sh all" +success_regex = ["(?m)^NGINX_RUNNER_PASSED\\b"] +fail_regex = ["(?i)\\bpanic(?:ked)?\\b", "(?m)^NGINX_RUNNER_FAILED\\b"] +timeout = 7200 diff --git a/apps/starry/nginx/qemu/all/qemu-x86_64.toml b/apps/starry/nginx/qemu/all/qemu-x86_64.toml new file mode 100644 index 0000000000..1cdb9555f1 --- /dev/null +++ b/apps/starry/nginx/qemu/all/qemu-x86_64.toml @@ -0,0 +1,20 @@ +args = [ + "-nographic", + "-m", + "1G", + "-device", + "virtio-blk-pci,drive=disk0", + "-drive", + "id=disk0,if=none,format=raw,file=${workspace}/tmp/axbuild/rootfs/rootfs-x86_64-alpine.img", + "-device", + "virtio-net-pci,netdev=net0", + "-netdev", + "user,id=net0", +] +uefi = false +to_bin = false +shell_prefix = "root@starry:" +shell_init_cmd = "/usr/bin/nginx-runner.sh all" +success_regex = ["(?m)^NGINX_RUNNER_PASSED\\b"] +fail_regex = ["(?i)\\bpanic(?:ked)?\\b", "(?m)^NGINX_RUNNER_FAILED\\b"] +timeout = 7200 diff --git a/apps/starry/nginx/qemu/debug/qemu-riscv64-timing-debug.toml b/apps/starry/nginx/qemu/debug/qemu-riscv64-timing-debug.toml new file mode 100644 index 0000000000..edbb215fc3 --- /dev/null +++ b/apps/starry/nginx/qemu/debug/qemu-riscv64-timing-debug.toml @@ -0,0 +1,22 @@ +args = [ + "-nographic", + "-m", + "512M", + "-cpu", + "rv64", + "-device", + "virtio-blk-pci,drive=disk0", + "-drive", + "id=disk0,if=none,format=raw,file=${workspace}/tmp/axbuild/rootfs/rootfs-riscv64-alpine.img", + "-device", + "virtio-net-pci,netdev=net0", + "-netdev", + "user,id=net0", +] +uefi = false +to_bin = true +shell_prefix = "root@starry:" +shell_init_cmd = "NGINX_RUNNER_PHASE_TIMEOUT=1800 /usr/bin/nginx-runner.sh debug x86-timing-debug" +success_regex = ["(?m)^NGINX_RUNNER_PASSED\\b"] +fail_regex = ["(?i)\\bpanic(?:ked)?\\b", "(?m)^NGINX_RUNNER_FAILED\\b"] +timeout = 3600 diff --git a/apps/starry/nginx/qemu/debug/qemu-x86_64-bad-method-debug.toml b/apps/starry/nginx/qemu/debug/qemu-x86_64-bad-method-debug.toml new file mode 100644 index 0000000000..2f0e7980d0 --- /dev/null +++ b/apps/starry/nginx/qemu/debug/qemu-x86_64-bad-method-debug.toml @@ -0,0 +1,20 @@ +args = [ + "-nographic", + "-m", + "1G", + "-device", + "virtio-blk-pci,drive=disk0", + "-drive", + "id=disk0,if=none,format=raw,file=${workspace}/tmp/axbuild/rootfs/rootfs-x86_64-alpine.img", + "-device", + "virtio-net-pci,netdev=net0", + "-netdev", + "user,id=net0", +] +uefi = false +to_bin = false +shell_prefix = "root@starry:" +shell_init_cmd = "/usr/bin/nginx-runner.sh debug bad-method-debug" +success_regex = ["(?m)^NGINX_RUNNER_PASSED\\b"] +fail_regex = ["(?i)\\bpanic(?:ked)?\\b", "(?m)^NGINX_RUNNER_FAILED\\b"] +timeout = 3600 diff --git a/apps/starry/nginx/qemu/debug/qemu-x86_64-bad-method-matrix.toml b/apps/starry/nginx/qemu/debug/qemu-x86_64-bad-method-matrix.toml new file mode 100644 index 0000000000..330f2fe185 --- /dev/null +++ b/apps/starry/nginx/qemu/debug/qemu-x86_64-bad-method-matrix.toml @@ -0,0 +1,20 @@ +args = [ + "-nographic", + "-m", + "1G", + "-device", + "virtio-blk-pci,drive=disk0", + "-drive", + "id=disk0,if=none,format=raw,file=${workspace}/tmp/axbuild/rootfs/rootfs-x86_64-alpine.img", + "-device", + "virtio-net-pci,netdev=net0", + "-netdev", + "user,id=net0", +] +uefi = false +to_bin = false +shell_prefix = "root@starry:" +shell_init_cmd = "/usr/bin/nginx-runner.sh debug bad-method-matrix" +success_regex = ["(?m)^NGINX_RUNNER_PASSED\\b"] +fail_regex = ["(?i)\\bpanic(?:ked)?\\b", "(?m)^NGINX_RUNNER_FAILED\\b"] +timeout = 3600 diff --git a/apps/starry/nginx/qemu/debug/qemu-x86_64-sendfile-on-debug.toml b/apps/starry/nginx/qemu/debug/qemu-x86_64-sendfile-on-debug.toml new file mode 100644 index 0000000000..5b5c58c474 --- /dev/null +++ b/apps/starry/nginx/qemu/debug/qemu-x86_64-sendfile-on-debug.toml @@ -0,0 +1,20 @@ +args = [ + "-nographic", + "-m", + "1G", + "-device", + "virtio-blk-pci,drive=disk0", + "-drive", + "id=disk0,if=none,format=raw,file=${workspace}/tmp/axbuild/rootfs/rootfs-x86_64-alpine.img", + "-device", + "virtio-net-pci,netdev=net0", + "-netdev", + "user,id=net0", +] +uefi = false +to_bin = false +shell_prefix = "root@starry:" +shell_init_cmd = "/usr/bin/nginx-runner.sh debug sendfile-on-debug" +success_regex = ["(?m)^NGINX_RUNNER_PASSED\\b"] +fail_regex = ["(?i)\\bpanic(?:ked)?\\b", "(?m)^NGINX_RUNNER_FAILED\\b"] +timeout = 3600 diff --git a/apps/starry/nginx/qemu/debug/qemu-x86_64-short-connection-debug.toml b/apps/starry/nginx/qemu/debug/qemu-x86_64-short-connection-debug.toml new file mode 100644 index 0000000000..17301c4c8e --- /dev/null +++ b/apps/starry/nginx/qemu/debug/qemu-x86_64-short-connection-debug.toml @@ -0,0 +1,20 @@ +args = [ + "-nographic", + "-m", + "1G", + "-device", + "virtio-blk-pci,drive=disk0", + "-drive", + "id=disk0,if=none,format=raw,file=${workspace}/tmp/axbuild/rootfs/rootfs-x86_64-alpine.img", + "-device", + "virtio-net-pci,netdev=net0", + "-netdev", + "user,id=net0", +] +uefi = false +to_bin = false +shell_prefix = "root@starry:" +shell_init_cmd = "NGINX_RUNNER_PHASE_TIMEOUT=1800 /usr/bin/nginx-runner.sh debug short-connection-debug" +success_regex = ["(?m)^NGINX_RUNNER_PASSED\\b"] +fail_regex = ["(?i)\\bpanic(?:ked)?\\b", "(?m)^NGINX_RUNNER_FAILED\\b"] +timeout = 3600 diff --git a/apps/starry/nginx/qemu/debug/qemu-x86_64-short-connection-direct-debug.toml b/apps/starry/nginx/qemu/debug/qemu-x86_64-short-connection-direct-debug.toml new file mode 100644 index 0000000000..ae3cadf390 --- /dev/null +++ b/apps/starry/nginx/qemu/debug/qemu-x86_64-short-connection-direct-debug.toml @@ -0,0 +1,20 @@ +args = [ + "-nographic", + "-m", + "1G", + "-device", + "virtio-blk-pci,drive=disk0", + "-drive", + "id=disk0,if=none,format=raw,file=${workspace}/tmp/axbuild/rootfs/rootfs-x86_64-alpine.img", + "-device", + "virtio-net-pci,netdev=net0", + "-netdev", + "user,id=net0", +] +uefi = false +to_bin = false +shell_prefix = "root@starry:" +shell_init_cmd = "NGINX_RUNNER_PHASE_TIMEOUT=1800 NGINX_SHORT_CONN_DEBUG_USE_TIMEOUT=0 /usr/bin/nginx-runner.sh debug short-connection-debug" +success_regex = ["(?m)^NGINX_RUNNER_PASSED\\b"] +fail_regex = ["(?i)\\bpanic(?:ked)?\\b", "(?m)^NGINX_RUNNER_FAILED\\b"] +timeout = 3600 diff --git a/apps/starry/nginx/qemu/debug/qemu-x86_64-timing-debug.toml b/apps/starry/nginx/qemu/debug/qemu-x86_64-timing-debug.toml new file mode 100644 index 0000000000..4509f87d7f --- /dev/null +++ b/apps/starry/nginx/qemu/debug/qemu-x86_64-timing-debug.toml @@ -0,0 +1,20 @@ +args = [ + "-nographic", + "-m", + "1G", + "-device", + "virtio-blk-pci,drive=disk0", + "-drive", + "id=disk0,if=none,format=raw,file=${workspace}/tmp/axbuild/rootfs/rootfs-x86_64-alpine.img", + "-device", + "virtio-net-pci,netdev=net0", + "-netdev", + "user,id=net0", +] +uefi = false +to_bin = false +shell_prefix = "root@starry:" +shell_init_cmd = "NGINX_RUNNER_PHASE_TIMEOUT=1800 /usr/bin/nginx-runner.sh debug x86-timing-debug" +success_regex = ["(?m)^NGINX_RUNNER_PASSED\\b"] +fail_regex = ["(?i)\\bpanic(?:ked)?\\b", "(?m)^NGINX_RUNNER_FAILED\\b"] +timeout = 3600 diff --git a/apps/starry/nginx/qemu/phase/qemu-aarch64-phase12.toml b/apps/starry/nginx/qemu/phase/qemu-aarch64-phase12.toml new file mode 100644 index 0000000000..8d90df2aeb --- /dev/null +++ b/apps/starry/nginx/qemu/phase/qemu-aarch64-phase12.toml @@ -0,0 +1,22 @@ +args = [ + "-nographic", + "-m", + "512M", + "-cpu", + "cortex-a53", + "-device", + "virtio-blk-pci,drive=disk0", + "-drive", + "id=disk0,if=none,format=raw,file=${workspace}/tmp/axbuild/rootfs/rootfs-aarch64-alpine.img", + "-device", + "virtio-net-pci,netdev=net0", + "-netdev", + "user,id=net0", +] +uefi = false +to_bin = true +shell_prefix = "root@starry:" +shell_init_cmd = "/usr/bin/nginx-runner.sh phase phase12" +success_regex = ["(?m)^NGINX_RUNNER_PASSED\\b"] +fail_regex = ["(?i)\\bpanic(?:ked)?\\b", "(?m)^NGINX_RUNNER_FAILED\\b"] +timeout = 3600 diff --git a/apps/starry/nginx/qemu/phase/qemu-aarch64-phase13.toml b/apps/starry/nginx/qemu/phase/qemu-aarch64-phase13.toml new file mode 100644 index 0000000000..732ed4e03d --- /dev/null +++ b/apps/starry/nginx/qemu/phase/qemu-aarch64-phase13.toml @@ -0,0 +1,22 @@ +args = [ + "-nographic", + "-m", + "512M", + "-cpu", + "cortex-a53", + "-device", + "virtio-blk-pci,drive=disk0", + "-drive", + "id=disk0,if=none,format=raw,file=${workspace}/tmp/axbuild/rootfs/rootfs-aarch64-alpine.img", + "-device", + "virtio-net-pci,netdev=net0", + "-netdev", + "user,id=net0", +] +uefi = false +to_bin = true +shell_prefix = "root@starry:" +shell_init_cmd = "/usr/bin/nginx-runner.sh phase phase13" +success_regex = ["(?m)^NGINX_RUNNER_PASSED\\b"] +fail_regex = ["(?i)\\bpanic(?:ked)?\\b", "(?m)^NGINX_RUNNER_FAILED\\b"] +timeout = 3600 diff --git a/apps/starry/nginx/qemu/phase/qemu-loongarch64-phase12.toml b/apps/starry/nginx/qemu/phase/qemu-loongarch64-phase12.toml new file mode 100644 index 0000000000..820019297d --- /dev/null +++ b/apps/starry/nginx/qemu/phase/qemu-loongarch64-phase12.toml @@ -0,0 +1,24 @@ +args = [ + "-machine", + "virt", + "-cpu", + "la464", + "-nographic", + "-m", + "2G", + "-device", + "virtio-blk-pci,drive=disk0", + "-drive", + "id=disk0,if=none,format=raw,file=${workspace}/tmp/axbuild/rootfs/rootfs-loongarch64-alpine.img", + "-device", + "virtio-net-pci,netdev=net0", + "-netdev", + "user,id=net0", +] +uefi = false +to_bin = true +shell_prefix = "root@starry:" +shell_init_cmd = "/usr/bin/nginx-runner.sh phase phase12" +success_regex = ["(?m)^NGINX_RUNNER_PASSED\\b"] +fail_regex = ["(?i)\\bpanic(?:ked)?\\b", "(?m)^NGINX_RUNNER_FAILED\\b"] +timeout = 3600 diff --git a/apps/starry/nginx/qemu/phase/qemu-riscv64-phase12.toml b/apps/starry/nginx/qemu/phase/qemu-riscv64-phase12.toml new file mode 100644 index 0000000000..c8455c77a9 --- /dev/null +++ b/apps/starry/nginx/qemu/phase/qemu-riscv64-phase12.toml @@ -0,0 +1,22 @@ +args = [ + "-nographic", + "-m", + "512M", + "-cpu", + "rv64", + "-device", + "virtio-blk-pci,drive=disk0", + "-drive", + "id=disk0,if=none,format=raw,file=${workspace}/tmp/axbuild/rootfs/rootfs-riscv64-alpine.img", + "-device", + "virtio-net-pci,netdev=net0", + "-netdev", + "user,id=net0", +] +uefi = false +to_bin = true +shell_prefix = "root@starry:" +shell_init_cmd = "/usr/bin/nginx-runner.sh phase phase12" +success_regex = ["(?m)^NGINX_RUNNER_PASSED\\b"] +fail_regex = ["(?i)\\bpanic(?:ked)?\\b", "(?m)^NGINX_RUNNER_FAILED\\b"] +timeout = 3600 diff --git a/apps/starry/nginx/qemu/phase/qemu-riscv64-phase13.toml b/apps/starry/nginx/qemu/phase/qemu-riscv64-phase13.toml new file mode 100644 index 0000000000..b979f32acc --- /dev/null +++ b/apps/starry/nginx/qemu/phase/qemu-riscv64-phase13.toml @@ -0,0 +1,22 @@ +args = [ + "-nographic", + "-m", + "512M", + "-cpu", + "rv64", + "-device", + "virtio-blk-pci,drive=disk0", + "-drive", + "id=disk0,if=none,format=raw,file=${workspace}/tmp/axbuild/rootfs/rootfs-riscv64-alpine.img", + "-device", + "virtio-net-pci,netdev=net0", + "-netdev", + "user,id=net0", +] +uefi = false +to_bin = true +shell_prefix = "root@starry:" +shell_init_cmd = "/usr/bin/nginx-runner.sh phase phase13" +success_regex = ["(?m)^NGINX_RUNNER_PASSED\\b"] +fail_regex = ["(?i)\\bpanic(?:ked)?\\b", "(?m)^NGINX_RUNNER_FAILED\\b"] +timeout = 3600 diff --git a/apps/starry/nginx/qemu/phase/qemu-riscv64-phase31.toml b/apps/starry/nginx/qemu/phase/qemu-riscv64-phase31.toml new file mode 100644 index 0000000000..dc72c7971b --- /dev/null +++ b/apps/starry/nginx/qemu/phase/qemu-riscv64-phase31.toml @@ -0,0 +1,22 @@ +args = [ + "-nographic", + "-m", + "512M", + "-cpu", + "rv64", + "-device", + "virtio-blk-pci,drive=disk0", + "-drive", + "id=disk0,if=none,format=raw,file=${workspace}/tmp/axbuild/rootfs/rootfs-riscv64-alpine.img", + "-device", + "virtio-net-pci,netdev=net0", + "-netdev", + "user,id=net0", +] +uefi = false +to_bin = true +shell_prefix = "root@starry:" +shell_init_cmd = "/usr/bin/nginx-runner.sh phase phase31" +success_regex = ["(?m)^NGINX_RUNNER_PASSED\\b"] +fail_regex = ["(?i)\\bpanic(?:ked)?\\b", "(?m)^NGINX_RUNNER_FAILED\\b"] +timeout = 3600 diff --git a/apps/starry/nginx/qemu/phase/qemu-riscv64-phase32.toml b/apps/starry/nginx/qemu/phase/qemu-riscv64-phase32.toml new file mode 100644 index 0000000000..7c3a1da06a --- /dev/null +++ b/apps/starry/nginx/qemu/phase/qemu-riscv64-phase32.toml @@ -0,0 +1,22 @@ +args = [ + "-nographic", + "-m", + "512M", + "-cpu", + "rv64", + "-device", + "virtio-blk-pci,drive=disk0", + "-drive", + "id=disk0,if=none,format=raw,file=${workspace}/tmp/axbuild/rootfs/rootfs-riscv64-alpine.img", + "-device", + "virtio-net-pci,netdev=net0", + "-netdev", + "user,id=net0", +] +uefi = false +to_bin = true +shell_prefix = "root@starry:" +shell_init_cmd = "/usr/bin/nginx-runner.sh phase phase32" +success_regex = ["(?m)^NGINX_RUNNER_PASSED\\b"] +fail_regex = ["(?i)\\bpanic(?:ked)?\\b", "(?m)^NGINX_RUNNER_FAILED\\b"] +timeout = 3600 diff --git a/apps/starry/nginx/qemu/phase/qemu-x86_64-phase00.toml b/apps/starry/nginx/qemu/phase/qemu-x86_64-phase00.toml new file mode 100644 index 0000000000..e4f064aaea --- /dev/null +++ b/apps/starry/nginx/qemu/phase/qemu-x86_64-phase00.toml @@ -0,0 +1,20 @@ +args = [ + "-nographic", + "-m", + "1G", + "-device", + "virtio-blk-pci,drive=disk0", + "-drive", + "id=disk0,if=none,format=raw,file=${workspace}/tmp/axbuild/rootfs/rootfs-x86_64-alpine.img", + "-device", + "virtio-net-pci,netdev=net0", + "-netdev", + "user,id=net0", +] +uefi = false +to_bin = false +shell_prefix = "root@starry:" +shell_init_cmd = "/usr/bin/nginx-runner.sh phase phase00" +success_regex = ["(?m)^NGINX_RUNNER_PASSED\\b"] +fail_regex = ["(?i)\\bpanic(?:ked)?\\b", "(?m)^NGINX_RUNNER_FAILED\\b"] +timeout = 3600 diff --git a/apps/starry/nginx/qemu/phase/qemu-x86_64-phase12.toml b/apps/starry/nginx/qemu/phase/qemu-x86_64-phase12.toml new file mode 100644 index 0000000000..95bd3b283d --- /dev/null +++ b/apps/starry/nginx/qemu/phase/qemu-x86_64-phase12.toml @@ -0,0 +1,20 @@ +args = [ + "-nographic", + "-m", + "1G", + "-device", + "virtio-blk-pci,drive=disk0", + "-drive", + "id=disk0,if=none,format=raw,file=${workspace}/tmp/axbuild/rootfs/rootfs-x86_64-alpine.img", + "-device", + "virtio-net-pci,netdev=net0", + "-netdev", + "user,id=net0", +] +uefi = false +to_bin = false +shell_prefix = "root@starry:" +shell_init_cmd = "/usr/bin/nginx-runner.sh phase phase12" +success_regex = ["(?m)^NGINX_RUNNER_PASSED\\b"] +fail_regex = ["(?i)\\bpanic(?:ked)?\\b", "(?m)^NGINX_RUNNER_FAILED\\b"] +timeout = 3600 diff --git a/apps/starry/nginx/qemu/phase/qemu-x86_64-phase13.toml b/apps/starry/nginx/qemu/phase/qemu-x86_64-phase13.toml new file mode 100644 index 0000000000..5aff7f7563 --- /dev/null +++ b/apps/starry/nginx/qemu/phase/qemu-x86_64-phase13.toml @@ -0,0 +1,20 @@ +args = [ + "-nographic", + "-m", + "1G", + "-device", + "virtio-blk-pci,drive=disk0", + "-drive", + "id=disk0,if=none,format=raw,file=${workspace}/tmp/axbuild/rootfs/rootfs-x86_64-alpine.img", + "-device", + "virtio-net-pci,netdev=net0", + "-netdev", + "user,id=net0", +] +uefi = false +to_bin = false +shell_prefix = "root@starry:" +shell_init_cmd = "/usr/bin/nginx-runner.sh phase phase13" +success_regex = ["(?m)^NGINX_RUNNER_PASSED\\b"] +fail_regex = ["(?i)\\bpanic(?:ked)?\\b", "(?m)^NGINX_RUNNER_FAILED\\b"] +timeout = 3600 diff --git a/apps/starry/nginx/qemu/phase/qemu-x86_64-phase20.toml b/apps/starry/nginx/qemu/phase/qemu-x86_64-phase20.toml new file mode 100644 index 0000000000..38755420cf --- /dev/null +++ b/apps/starry/nginx/qemu/phase/qemu-x86_64-phase20.toml @@ -0,0 +1,20 @@ +args = [ + "-nographic", + "-m", + "1G", + "-device", + "virtio-blk-pci,drive=disk0", + "-drive", + "id=disk0,if=none,format=raw,file=${workspace}/tmp/axbuild/rootfs/rootfs-x86_64-alpine.img", + "-device", + "virtio-net-pci,netdev=net0", + "-netdev", + "user,id=net0", +] +uefi = false +to_bin = false +shell_prefix = "root@starry:" +shell_init_cmd = "/usr/bin/nginx-runner.sh phase phase20" +success_regex = ["(?m)^NGINX_RUNNER_PASSED\\b"] +fail_regex = ["(?i)\\bpanic(?:ked)?\\b", "(?m)^NGINX_RUNNER_FAILED\\b"] +timeout = 3600 diff --git a/apps/starry/nginx/qemu/phase/qemu-x86_64-phase31.toml b/apps/starry/nginx/qemu/phase/qemu-x86_64-phase31.toml new file mode 100644 index 0000000000..cae5588139 --- /dev/null +++ b/apps/starry/nginx/qemu/phase/qemu-x86_64-phase31.toml @@ -0,0 +1,20 @@ +args = [ + "-nographic", + "-m", + "1G", + "-device", + "virtio-blk-pci,drive=disk0", + "-drive", + "id=disk0,if=none,format=raw,file=${workspace}/tmp/axbuild/rootfs/rootfs-x86_64-alpine.img", + "-device", + "virtio-net-pci,netdev=net0", + "-netdev", + "user,id=net0", +] +uefi = false +to_bin = false +shell_prefix = "root@starry:" +shell_init_cmd = "/usr/bin/nginx-runner.sh phase phase31" +success_regex = ["(?m)^NGINX_RUNNER_PASSED\\b"] +fail_regex = ["(?i)\\bpanic(?:ked)?\\b", "(?m)^NGINX_RUNNER_FAILED\\b"] +timeout = 3600 diff --git a/apps/starry/nginx/qemu/phase/qemu-x86_64-phase32.toml b/apps/starry/nginx/qemu/phase/qemu-x86_64-phase32.toml new file mode 100644 index 0000000000..3cc4728f39 --- /dev/null +++ b/apps/starry/nginx/qemu/phase/qemu-x86_64-phase32.toml @@ -0,0 +1,20 @@ +args = [ + "-nographic", + "-m", + "1G", + "-device", + "virtio-blk-pci,drive=disk0", + "-drive", + "id=disk0,if=none,format=raw,file=${workspace}/tmp/axbuild/rootfs/rootfs-x86_64-alpine.img", + "-device", + "virtio-net-pci,netdev=net0", + "-netdev", + "user,id=net0", +] +uefi = false +to_bin = false +shell_prefix = "root@starry:" +shell_init_cmd = "/usr/bin/nginx-runner.sh phase phase32" +success_regex = ["(?m)^NGINX_RUNNER_PASSED\\b"] +fail_regex = ["(?i)\\bpanic(?:ked)?\\b", "(?m)^NGINX_RUNNER_FAILED\\b"] +timeout = 3600 diff --git a/apps/starry/nginx/qemu/phase/qemu-x86_64-phase33.toml b/apps/starry/nginx/qemu/phase/qemu-x86_64-phase33.toml new file mode 100644 index 0000000000..a729505c5a --- /dev/null +++ b/apps/starry/nginx/qemu/phase/qemu-x86_64-phase33.toml @@ -0,0 +1,20 @@ +args = [ + "-nographic", + "-m", + "1G", + "-device", + "virtio-blk-pci,drive=disk0", + "-drive", + "id=disk0,if=none,format=raw,file=${workspace}/tmp/axbuild/rootfs/rootfs-x86_64-alpine.img", + "-device", + "virtio-net-pci,netdev=net0", + "-netdev", + "user,id=net0", +] +uefi = false +to_bin = false +shell_prefix = "root@starry:" +shell_init_cmd = "/usr/bin/nginx-runner.sh phase phase33" +success_regex = ["(?m)^NGINX_RUNNER_PASSED\\b"] +fail_regex = ["(?i)\\bpanic(?:ked)?\\b", "(?m)^NGINX_RUNNER_FAILED\\b"] +timeout = 3600 diff --git a/apps/starry/nginx/qemu/phase/qemu-x86_64-phase41.toml b/apps/starry/nginx/qemu/phase/qemu-x86_64-phase41.toml new file mode 100644 index 0000000000..c8a1f35071 --- /dev/null +++ b/apps/starry/nginx/qemu/phase/qemu-x86_64-phase41.toml @@ -0,0 +1,20 @@ +args = [ + "-nographic", + "-m", + "1G", + "-device", + "virtio-blk-pci,drive=disk0", + "-drive", + "id=disk0,if=none,format=raw,file=${workspace}/tmp/axbuild/rootfs/rootfs-x86_64-alpine.img", + "-device", + "virtio-net-pci,netdev=net0", + "-netdev", + "user,id=net0", +] +uefi = false +to_bin = false +shell_prefix = "root@starry:" +shell_init_cmd = "/usr/bin/nginx-runner.sh phase phase41" +success_regex = ["(?m)^NGINX_RUNNER_PASSED\\b"] +fail_regex = ["(?i)\\bpanic(?:ked)?\\b", "(?m)^NGINX_RUNNER_FAILED\\b"] +timeout = 3600 diff --git a/apps/starry/nginx/qemu/phase/qemu-x86_64-phase42.toml b/apps/starry/nginx/qemu/phase/qemu-x86_64-phase42.toml new file mode 100644 index 0000000000..4ed263cf46 --- /dev/null +++ b/apps/starry/nginx/qemu/phase/qemu-x86_64-phase42.toml @@ -0,0 +1,20 @@ +args = [ + "-nographic", + "-m", + "1G", + "-device", + "virtio-blk-pci,drive=disk0", + "-drive", + "id=disk0,if=none,format=raw,file=${workspace}/tmp/axbuild/rootfs/rootfs-x86_64-alpine.img", + "-device", + "virtio-net-pci,netdev=net0", + "-netdev", + "user,id=net0", +] +uefi = false +to_bin = false +shell_prefix = "root@starry:" +shell_init_cmd = "/usr/bin/nginx-runner.sh phase phase42" +success_regex = ["(?m)^NGINX_RUNNER_PASSED\\b"] +fail_regex = ["(?i)\\bpanic(?:ked)?\\b", "(?m)^NGINX_RUNNER_FAILED\\b"] +timeout = 3600 diff --git a/apps/starry/nginx/qemu/phase/qemu-x86_64-phase43.toml b/apps/starry/nginx/qemu/phase/qemu-x86_64-phase43.toml new file mode 100644 index 0000000000..2227bd44b7 --- /dev/null +++ b/apps/starry/nginx/qemu/phase/qemu-x86_64-phase43.toml @@ -0,0 +1,20 @@ +args = [ + "-nographic", + "-m", + "1G", + "-device", + "virtio-blk-pci,drive=disk0", + "-drive", + "id=disk0,if=none,format=raw,file=${workspace}/tmp/axbuild/rootfs/rootfs-x86_64-alpine.img", + "-device", + "virtio-net-pci,netdev=net0", + "-netdev", + "user,id=net0", +] +uefi = false +to_bin = false +shell_prefix = "root@starry:" +shell_init_cmd = "/usr/bin/nginx-runner.sh phase phase43" +success_regex = ["(?m)^NGINX_RUNNER_PASSED\\b"] +fail_regex = ["(?i)\\bpanic(?:ked)?\\b", "(?m)^NGINX_RUNNER_FAILED\\b"] +timeout = 3600 diff --git a/apps/starry/nginx/qemu/phase/qemu-x86_64-phase50.toml b/apps/starry/nginx/qemu/phase/qemu-x86_64-phase50.toml new file mode 100644 index 0000000000..dad81c2c65 --- /dev/null +++ b/apps/starry/nginx/qemu/phase/qemu-x86_64-phase50.toml @@ -0,0 +1,20 @@ +args = [ + "-nographic", + "-m", + "1G", + "-device", + "virtio-blk-pci,drive=disk0", + "-drive", + "id=disk0,if=none,format=raw,file=${workspace}/tmp/axbuild/rootfs/rootfs-x86_64-alpine.img", + "-device", + "virtio-net-pci,netdev=net0", + "-netdev", + "user,id=net0", +] +uefi = false +to_bin = false +shell_prefix = "root@starry:" +shell_init_cmd = "/usr/bin/nginx-runner.sh phase phase50" +success_regex = ["(?m)^NGINX_RUNNER_PASSED\\b"] +fail_regex = ["(?i)\\bpanic(?:ked)?\\b", "(?m)^NGINX_RUNNER_FAILED\\b"] +timeout = 3600 diff --git a/apps/starry/nginx/qemu/phase/qemu-x86_64-phase60.toml b/apps/starry/nginx/qemu/phase/qemu-x86_64-phase60.toml new file mode 100644 index 0000000000..7c855ee17f --- /dev/null +++ b/apps/starry/nginx/qemu/phase/qemu-x86_64-phase60.toml @@ -0,0 +1,20 @@ +args = [ + "-nographic", + "-m", + "1G", + "-device", + "virtio-blk-pci,drive=disk0", + "-drive", + "id=disk0,if=none,format=raw,file=${workspace}/tmp/axbuild/rootfs/rootfs-x86_64-alpine.img", + "-device", + "virtio-net-pci,netdev=net0", + "-netdev", + "user,id=net0", +] +uefi = false +to_bin = false +shell_prefix = "root@starry:" +shell_init_cmd = "/usr/bin/nginx-runner.sh phase phase60" +success_regex = ["(?m)^NGINX_RUNNER_PASSED\\b"] +fail_regex = ["(?i)\\bpanic(?:ked)?\\b", "(?m)^NGINX_RUNNER_FAILED\\b"] +timeout = 3600 diff --git a/apps/starry/nginx/qemu/phase/qemu-x86_64-phase70.toml b/apps/starry/nginx/qemu/phase/qemu-x86_64-phase70.toml new file mode 100644 index 0000000000..c879f4dd6e --- /dev/null +++ b/apps/starry/nginx/qemu/phase/qemu-x86_64-phase70.toml @@ -0,0 +1,20 @@ +args = [ + "-nographic", + "-m", + "1G", + "-device", + "virtio-blk-pci,drive=disk0", + "-drive", + "id=disk0,if=none,format=raw,file=${workspace}/tmp/axbuild/rootfs/rootfs-x86_64-alpine.img", + "-device", + "virtio-net-pci,netdev=net0", + "-netdev", + "user,id=net0", +] +uefi = false +to_bin = false +shell_prefix = "root@starry:" +shell_init_cmd = "/usr/bin/nginx-runner.sh phase phase70" +success_regex = ["(?m)^NGINX_RUNNER_PASSED\\b"] +fail_regex = ["(?i)\\bpanic(?:ked)?\\b", "(?m)^NGINX_RUNNER_FAILED\\b"] +timeout = 3600 diff --git a/apps/starry/nginx/qemu/phase/qemu-x86_64-phase90.toml b/apps/starry/nginx/qemu/phase/qemu-x86_64-phase90.toml new file mode 100644 index 0000000000..eef7548fb1 --- /dev/null +++ b/apps/starry/nginx/qemu/phase/qemu-x86_64-phase90.toml @@ -0,0 +1,20 @@ +args = [ + "-nographic", + "-m", + "1G", + "-device", + "virtio-blk-pci,drive=disk0", + "-drive", + "id=disk0,if=none,format=raw,file=${workspace}/tmp/axbuild/rootfs/rootfs-x86_64-alpine.img", + "-device", + "virtio-net-pci,netdev=net0", + "-netdev", + "user,id=net0", +] +uefi = false +to_bin = false +shell_prefix = "root@starry:" +shell_init_cmd = "/usr/bin/nginx-runner.sh phase phase90" +success_regex = ["(?m)^NGINX_RUNNER_PASSED\\b"] +fail_regex = ["(?i)\\bpanic(?:ked)?\\b", "(?m)^NGINX_RUNNER_FAILED\\b"] +timeout = 3600 diff --git a/apps/starry/nginx/runner/nginx-alpine-mirror.sh b/apps/starry/nginx/runner/nginx-alpine-mirror.sh new file mode 100644 index 0000000000..e8af472d17 --- /dev/null +++ b/apps/starry/nginx/runner/nginx-alpine-mirror.sh @@ -0,0 +1,85 @@ +#!/bin/sh + +set -eu + +NGINX_APK_MIRROR_TIMEOUT_SEC="${NGINX_APK_MIRROR_TIMEOUT_SEC:-120}" +NGINX_APK_MIRROR_RETRIES="${NGINX_APK_MIRROR_RETRIES:-2}" +NGINX_APK_REPO_FILE="/tmp/nginx-apk-repositories" +NGINX_APK_ATTEMPT_LOG="/tmp/nginx-apk-attempt.log" + +# Resolve the Alpine release branch that matches the running rootfs, e.g. "v3.23". +# Pulling packages from a fixed branch keeps the musl/ABI in sync with the +# rootfs base; the moving "latest-stable" alias can drift to a newer Alpine +# release whose packages need a newer musl (e.g. renameat2) and then fail to +# relocate or segfault on this rootfs. +nginx_apk_branch() { + if [ -n "${NGINX_APK_BRANCH:-}" ]; then + printf '%s\n' "$NGINX_APK_BRANCH" + return 0 + fi + release="" + if [ -r /etc/alpine-release ]; then + release=$(cat /etc/alpine-release 2>/dev/null) + fi + major=$(printf '%s' "$release" | cut -d. -f1) + minor=$(printf '%s' "$release" | cut -d. -f2) + if [ -n "$major" ] && [ -n "$minor" ]; then + printf 'v%s.%s\n' "$major" "$minor" + else + # Fall back to latest-stable only when the release cannot be read. + printf 'latest-stable\n' + fi +} + +write_repo_file() { + mirror="$1" + branch="$2" + cat >"$NGINX_APK_REPO_FILE" <"$NGINX_APK_ATTEMPT_LOG" 2>&1; then + echo "NGINX_APK_MIRROR_OK: $mirror/$branch" + return 0 + else + rc=$? + fi + + if [ "$rc" -eq 124 ] || [ "$rc" -eq 143 ]; then + echo "NGINX_APK_MIRROR_FAIL: $mirror/$branch (timeout ${NGINX_APK_MIRROR_TIMEOUT_SEC}s, rc=$rc)" + else + echo "NGINX_APK_MIRROR_FAIL: $mirror/$branch (apk rc=$rc)" + fi + sed -n '1,120p' "$NGINX_APK_ATTEMPT_LOG" || true + attempt=$((attempt + 1)) + done + return 1 +} + +nginx_apk_add_with_fallback() { + mirror_cn="https://mirrors.tuna.tsinghua.edu.cn/alpine" + mirror_global="https://dl-cdn.alpinelinux.org/alpine" + + if run_apk_add "$mirror_cn" "$@"; then + return 0 + fi + if run_apk_add "$mirror_global" "$@"; then + return 0 + fi + + echo "NGINX_APK_ALL_MIRRORS_FAILED" + return 1 +} diff --git a/apps/starry/nginx/runner/nginx-runner-lib.sh b/apps/starry/nginx/runner/nginx-runner-lib.sh new file mode 100644 index 0000000000..7dab13ce11 --- /dev/null +++ b/apps/starry/nginx/runner/nginx-runner-lib.sh @@ -0,0 +1,115 @@ +#!/bin/sh +# nginx-runner-lib.sh - shared helpers for the unified nginx test runner. +# +# Sourced by /usr/bin/nginx-runner.sh inside the guest. Provides: +# - structured marker logging +# - timeout command detection / wrapper +# - per-phase isolation (kill residual nginx, free ports, drop temp dirs) +# +# This library is the single place that owns cross-phase isolation, so the +# individual phase scripts no longer need their own watchdog (the old +# `( sleep N; kill -TERM $$ ) &` pattern leaked background jobs that woke up +# during later phases and killed an unrelated, PID-reused process). + +# Ports every phase/smoke config binds on 127.0.0.1. Kept in one place so the +# port-release wait stays in sync with the configs. +NGINX_RUNNER_PORTS="8080 8081 8082" + +runner_log() { printf 'NGINX_RUNNER_LOG: %s\n' "$*"; } + +# Detect a usable `timeout` implementation once and cache it in +# NGINX_RUNNER_TIMEOUT_CMD. Mirrors the probe the phase scripts used, but lives +# in the runner so the timeout responsibility sits at a single layer. +NGINX_RUNNER_TIMEOUT_CMD= +runner_init_timeout_cmd() { + if [ -n "$NGINX_RUNNER_TIMEOUT_CMD" ]; then + return 0 + fi + if command -v timeout >/dev/null 2>&1; then + NGINX_RUNNER_TIMEOUT_CMD='timeout' + return 0 + fi + if busybox timeout 2>&1 | grep -qi 'usage'; then + NGINX_RUNNER_TIMEOUT_CMD='busybox timeout' + return 0 + fi + runner_log "timeout command not available" + return 1 +} + +# runner_run_with_timeout +# Runs a command under the detected timeout. Returns the command's exit code, +# or 124 (timeout's convention) when the deadline is hit. +runner_run_with_timeout() { + sec=$1 + shift + # Word-split is intentional: NGINX_RUNNER_TIMEOUT_CMD may be "busybox timeout". + # shellcheck disable=SC2086 + $NGINX_RUNNER_TIMEOUT_CMD "$sec" "$@" +} + +# runner_port_listening -> 0 if something is LISTENing on the port. +# Best-effort: uses ss, then netstat; if neither exists we cannot tell and +# report "not listening" so the caller falls back to a fixed settle delay. +runner_port_listening() { + port=$1 + if command -v ss >/dev/null 2>&1; then + ss -ltn 2>/dev/null | grep -q ":${port}[[:space:]]" && return 0 + return 1 + fi + if command -v netstat >/dev/null 2>&1; then + netstat -ltn 2>/dev/null | grep -q ":${port}[[:space:]]" && return 0 + return 1 + fi + return 1 +} + +# runner_wait_ports_free [max_seconds] +# Waits until none of NGINX_RUNNER_PORTS is LISTENing, up to max_seconds. +# Prevents a residual nginx from one phase from making the next phase's bind +# fail (a false negative). Best-effort when no socket tool is available. +runner_wait_ports_free() { + max=${1:-10} + i=0 + while [ "$i" -lt "$max" ]; do + busy=0 + for port in $NGINX_RUNNER_PORTS; do + if runner_port_listening "$port"; then + busy=1 + break + fi + done + [ "$busy" -eq 0 ] && return 0 + sleep 1 + i=$((i + 1)) + done + runner_log "ports still busy after ${max}s: $NGINX_RUNNER_PORTS" + return 1 +} + +# runner_ensure_packages +# Installs the full package superset required by any phase/smoke script. +# Uses a sentinel file so apk add runs at most once per QEMU session, whether +# called from `all` (first stage installs, rest skip) or a standalone phase run. +NGINX_PKGS_SENTINEL="/tmp/nginx-pkgs-installed" +NGINX_PKGS="nginx curl busybox-extras procps netcat-openbsd" +runner_ensure_packages() { + [ -f "$NGINX_PKGS_SENTINEL" ] && return 0 + . /usr/bin/nginx-alpine-mirror.sh + nginx_apk_add_with_fallback $NGINX_PKGS || return 1 + touch "$NGINX_PKGS_SENTINEL" +} + +# runner_isolate_after_phase +# Unconditional per-phase cleanup, run after every phase regardless of result. +# This is the second line of defence behind each phase's own EXIT/TERM trap. +runner_isolate_after_phase() { + # 1. Reap any nginx the phase left behind (graceful, then forced). + killall -q nginx 2>/dev/null || true + sleep 1 + killall -q -9 nginx 2>/dev/null || true + # 2. Confirm the shared listen ports are released before the next phase. + runner_wait_ports_free 10 || true + # 3. Drop per-phase temp trees so disk state does not accumulate. + rm -rf /tmp/nginx-phase* /tmp/nginx-tests 2>/dev/null || true +} diff --git a/apps/starry/nginx/runner/nginx-runner.sh b/apps/starry/nginx/runner/nginx-runner.sh new file mode 100644 index 0000000000..d59803eb0f --- /dev/null +++ b/apps/starry/nginx/runner/nginx-runner.sh @@ -0,0 +1,180 @@ +#!/bin/sh +# nginx-runner.sh - unified entry point for the Starry nginx test suite. +# +# Usage (invoked as the QEMU shell_init_cmd): +# /usr/bin/nginx-runner.sh smoke +# /usr/bin/nginx-runner.sh phase +# /usr/bin/nginx-runner.sh all +# /usr/bin/nginx-runner.sh stress +# /usr/bin/nginx-runner.sh debug +# +# Terminal markers (the only thing QEMU success/fail regex must match): +# NGINX_RUNNER_PASSED mode= +# NGINX_RUNNER_FAILED phase= rc= +# Per-phase markers (informational, used by `all`): +# NGINX_RUNNER_PHASE_BEGIN phase= +# NGINX_RUNNER_PHASE_PASS phase= +# NGINX_RUNNER_PHASE_FAIL phase= rc= +# +# Pass/fail is decided by each stage script's EXIT CODE, not by its own marker +# text, so the historical marker-name drift (phase13 prints NGINX_PHASE1_*, +# phase20 prints NGINX_PHASE2_*) does not matter here. + +. /usr/bin/nginx-runner-lib.sh + +# Per-phase wall-clock budget. The runner is now the only watchdog (phase +# scripts no longer self-kill), so this must be generous enough to cover apk +# package install + the test body. The outer QEMU `timeout` is the hard bound. +NGINX_RUNNER_PHASE_TIMEOUT="${NGINX_RUNNER_PHASE_TIMEOUT:-600}" + +# Ordered stage list for `all`. Format: " ". +# smoke runs first, then phases in ascending id order. +NGINX_RUNNER_ALL_STAGES=' +smoke /usr/bin/nginx-smoke-tests.sh +phase00 /usr/bin/nginx-phase00-tests.sh +phase12 /usr/bin/nginx-phase12-tests.sh +phase13 /usr/bin/nginx-phase13-tests.sh +phase20 /usr/bin/nginx-phase20-tests.sh +phase31 /usr/bin/nginx-phase31-tests.sh +phase32 /usr/bin/nginx-phase32-tests.sh +phase33 /usr/bin/nginx-phase33-tests.sh +phase41 /usr/bin/nginx-phase41-tests.sh +phase42 /usr/bin/nginx-phase42-tests.sh +phase43 /usr/bin/nginx-phase43-tests.sh +phase50 /usr/bin/nginx-phase50-tests.sh +phase60 /usr/bin/nginx-phase60-tests.sh +phase70 /usr/bin/nginx-phase70-tests.sh +phase90 /usr/bin/nginx-phase90-tests.sh +' + +# Resolve a phase id to its guest script path. Echoes the path, or nothing +# when the id is unknown. +runner_phase_script() { + want=$1 + printf '%s\n' "$NGINX_RUNNER_ALL_STAGES" | while read -r id script; do + [ -n "$id" ] || continue + if [ "$id" = "$want" ]; then + printf '%s\n' "$script" + return 0 + fi + done +} + +# Resolve a debug name to its guest script path. +runner_debug_script() { + case "$1" in + bad-method-debug) printf '%s\n' /usr/bin/nginx-bad-method-debug.sh ;; + bad-method-matrix) printf '%s\n' /usr/bin/nginx-bad-method-matrix.sh ;; + short-connection-debug) printf '%s\n' /usr/bin/nginx-short-connection-debug.sh ;; + x86-timing-debug) printf '%s\n' /usr/bin/nginx-x86-timing-debug.sh ;; + sendfile-on-debug) printf '%s\n' /usr/bin/nginx-sendfile-on-debug.sh ;; + *) return 1 ;; + esac +} + +runner_pass() { + printf 'NGINX_RUNNER_PASSED mode=%s\n' "$1" + exit 0 +} + +runner_fail() { + printf 'NGINX_RUNNER_FAILED phase=%s rc=%s\n' "$1" "$2" + exit 1 +} + +# runner_run_stage +# Runs one stage in an isolated child process under a timeout, emits per-phase +# markers, and always runs post-phase isolation. Returns the stage exit code. +runner_run_stage() { + id=$1 + script=$2 + if [ ! -x "$script" ] && [ ! -f "$script" ]; then + printf 'NGINX_RUNNER_PHASE_FAIL phase=%s rc=127\n' "$id" + runner_log "stage script missing: $script" + return 127 + fi + printf 'NGINX_RUNNER_PHASE_BEGIN phase=%s\n' "$id" + # Independent child process (sh