145 lines
5.6 KiB
Bash
Executable File
145 lines
5.6 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Self-contained Docker deployment regression. Requires Linux Docker because
|
|
# the validator uses the host network to reach the ephemeral HTTPS RRDP server.
|
|
set -euo pipefail
|
|
|
|
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
work_root="$(mktemp -d)"
|
|
server_pid=""
|
|
daemon_container=""
|
|
cleanup() {
|
|
[[ -n "$daemon_container" ]] && docker stop --time 5 "$daemon_container" >/dev/null 2>&1 || true
|
|
[[ -n "$server_pid" ]] && kill "$server_pid" 2>/dev/null || true
|
|
[[ -n "$server_pid" ]] && wait "$server_pid" 2>/dev/null || true
|
|
rm -rf "$work_root"
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
port="$(python3 - <<'PY'
|
|
import socket
|
|
with socket.socket() as sock:
|
|
sock.bind(("127.0.0.1", 0))
|
|
print(sock.getsockname()[1])
|
|
PY
|
|
)"
|
|
fixture="$work_root/fixture"
|
|
python3 "$repo_root/tests/support/generate_repository.py" \
|
|
--rrdp-host 127.0.0.1 --rrdp-port "$port" --output "$fixture"
|
|
ln -s "cases/baseline-v1/http" "$fixture/active"
|
|
mkdir -p "$work_root/output-snapshot" "$work_root/output-delta" "$work_root/state"
|
|
|
|
PYTHONUNBUFFERED=1 python3 "$repo_root/tests/support/serve_repository.py" \
|
|
"$fixture/active" "$port" "$fixture/certs/rrdp-server.pem" "$fixture/certs/rrdp-server.key" \
|
|
>"$work_root/server.log" 2>&1 &
|
|
server_pid="$!"
|
|
for _ in $(seq 1 50); do
|
|
rg -qx 'ready' "$work_root/server.log" && break
|
|
sleep 0.1
|
|
done
|
|
rg -qx 'ready' "$work_root/server.log"
|
|
|
|
image="${PANDA_RPKI_TEST_IMAGE:-panda-rpki:synthetic-e2e}"
|
|
if [[ -z "${PANDA_RPKI_TEST_IMAGE:-}" ]]; then
|
|
docker build -f "$repo_root/docker/Dockerfile" -t "$image" "$repo_root"
|
|
else
|
|
docker image inspect "$image" >/dev/null
|
|
fi
|
|
run_validator() {
|
|
local out="$1"
|
|
local mode="$2"
|
|
docker run --rm --network host --user "$(id -u):$(id -g)" \
|
|
-v "$fixture:/fixture:ro" \
|
|
-v "$work_root/state:/state" \
|
|
-v "$out:/output" \
|
|
"$image" validate \
|
|
--tal /fixture/tal/custom.tal --ta /fixture/ta/custom-ta.cer \
|
|
--http-root-cert /fixture/certs/rrdp-ca.pem \
|
|
--http-timeout-secs 5 \
|
|
--parallel-max-repo-sync-workers-global 2 \
|
|
--parallel-phase2-object-workers 2 \
|
|
--rrdp-state-dir /state --rrdp-sync-mode "$mode" \
|
|
--ccr-out /output/result.ccr --out /output --log-level info --log-format json
|
|
}
|
|
|
|
run_validator "$work_root/output-snapshot" snapshot
|
|
rm "$fixture/active"
|
|
ln -s "cases/baseline-v2/http" "$fixture/active"
|
|
run_validator "$work_root/output-delta" delta
|
|
|
|
for output in "$work_root/output-snapshot" "$work_root/output-delta"; do
|
|
test -s "$output/summary.json"
|
|
test -s "$output/vrps.csv"
|
|
test -s "$output/result.ccr"
|
|
openssl asn1parse -inform DER -in "$output/result.ccr" -noout
|
|
done
|
|
test -s "$work_root/state/repository-db/CURRENT"
|
|
python3 - "$work_root/output-snapshot" "$work_root/output-delta" <<'PY'
|
|
import json
|
|
import pathlib
|
|
import sys
|
|
|
|
snapshot, delta = (pathlib.Path(item) for item in sys.argv[1:])
|
|
first = json.loads((snapshot / "summary.json").read_text(encoding="utf-8"))
|
|
second = json.loads((delta / "summary.json").read_text(encoding="utf-8"))
|
|
assert first["vrps"] == 1, first
|
|
assert second["vrps"] == 2, second
|
|
assert len((snapshot / "vaps.csv").read_text(encoding="utf-8").splitlines()) == 2
|
|
assert len((delta / "vaps.csv").read_text(encoding="utf-8").splitlines()) == 2
|
|
timing = json.loads((delta / "stage-timing.json").read_text(encoding="utf-8"))
|
|
assert timing["analysis_counts"]["rrdp_delta_ops_applied_total"] > 0, timing
|
|
PY
|
|
|
|
echo "Synthetic Docker snapshot+delta E2E passed"
|
|
|
|
# Exercise the actual public daemon in the image with a fresh protocol root.
|
|
mkdir "$work_root/daemon"
|
|
rm "$fixture/active"
|
|
ln -s "cases/baseline-v1/http" "$fixture/active"
|
|
daemon_container="panda-rpki-daemon-$(basename "$work_root")"
|
|
docker run --rm --name "$daemon_container" --network host --read-only --tmpfs /tmp \
|
|
--user "$(id -u):$(id -g)" \
|
|
-v "$fixture:/fixture:ro" -v "$work_root/daemon:/data" \
|
|
"$image" daemon \
|
|
--state-root /data --max-runs 3 --interval-secs 2 --retain-runs 3 -- \
|
|
--tal /fixture/tal/custom.tal --ta /fixture/ta/custom-ta.cer \
|
|
--http-root-cert /fixture/certs/rrdp-ca.pem --log-format json \
|
|
>"$work_root/daemon-container.log" 2>&1 &
|
|
daemon_job=$!
|
|
python3 - "$work_root/daemon" "$fixture" <<'PY'
|
|
import json
|
|
import pathlib
|
|
import sys
|
|
import time
|
|
root, fixture = map(pathlib.Path, sys.argv[1:])
|
|
for _ in range(400):
|
|
status = root / "daemon-status.json"
|
|
if status.exists() and json.loads(status.read_text())["state"] == "sleeping":
|
|
(fixture / "active").unlink()
|
|
(fixture / "active").symlink_to("cases/baseline-v2/http")
|
|
break
|
|
time.sleep(0.05)
|
|
else:
|
|
raise AssertionError("Docker daemon did not finish snapshot")
|
|
PY
|
|
wait "$daemon_job"
|
|
daemon_container=""
|
|
python3 - "$work_root/daemon" <<'PY'
|
|
import json
|
|
import pathlib
|
|
import subprocess
|
|
import sys
|
|
root = pathlib.Path(sys.argv[1])
|
|
assert json.loads((root / "daemon-status.json").read_text())["runs_completed"] == 3
|
|
for seq, mode, vrps in [(1, "snapshot", 1), (2, "delta", 2), (3, "delta", 2)]:
|
|
out = root / f"runs/run_{seq:06}"
|
|
run = json.loads((out / "run-summary.json").read_text())
|
|
assert run["status"] == "success" and run["sync_mode"] == mode, run
|
|
assert run["summary"]["vrps"] == vrps, run
|
|
assert run["process_metrics"]["maxRssKb"] > 0, run
|
|
assert len((out / "vrps.csv").read_text().splitlines()) - 1 == vrps
|
|
subprocess.run(["openssl", "asn1parse", "-inform", "DER", "-in", str(out / "result.ccr"), "-noout"], check=True)
|
|
timing = json.loads((root / "runs/run_000002/stage-timing.json").read_text())
|
|
assert timing["analysis_counts"]["rrdp_delta_ops_applied_total"] > 0
|
|
print("Synthetic Docker daemon 3-run E2E passed")
|
|
PY
|