ci: probe runtime through Docker exec
CI / validate (pull_request) Successful in 1m51s

This commit is contained in:
Dennis Juhler Aagaard
2026-09-24 20:15:51 +02:00
parent b34d9b80fa
commit 4c9f9a5f4c
2 changed files with 31 additions and 23 deletions
+13 -3
View File
@@ -151,6 +151,15 @@ def test_runtime_copies_options_without_a_host_bind_mount() -> None:
assert "--mount" not in runtime_test
def test_runtime_probes_service_inside_container_network_namespace() -> None:
runtime_test = (ROOT / "tests/test_runtime.sh").read_text(encoding="utf-8")
assert 'probe_root "$container"' in runtime_test
assert 'post_invalid_worker "$first_container"' in runtime_test
assert 'assert_8080_loopback_mapping "$first_container"' in runtime_test
assert 'assert_8080_loopback_mapping "$second_container"' in runtime_test
assert 'f"http://127.0.0.1:{sys.argv[1]}/"' not in runtime_test
def test_ci_builds_and_loads_only_the_amd64_test_image() -> None:
workflow = (ROOT / ".gitea/workflows/ci.yml").read_text(encoding="utf-8")
parsed = yaml.safe_load(workflow)
@@ -253,9 +262,10 @@ def test_runtime_requires_process_baseline_after_cdp_close_and_zero_stopped_pid(
baseline = (
f'{prefix}_baseline="$(capture_process_baseline "{container}")"'
)
ready_index = runtime_test.index(
f'wait_ready "{container}" "${prefix}_port"'
mapping_index = runtime_test.index(
f'assert_8080_loopback_mapping "{container}"'
)
ready_index = runtime_test.index(f'wait_ready "{container}"')
baseline_index = runtime_test.index(baseline)
close_index = runtime_test.index(
f'probe_and_close_cdp "{container}"', baseline_index
@@ -264,7 +274,7 @@ def test_runtime_requires_process_baseline_after_cdp_close_and_zero_stopped_pid(
f'assert_processes_return_to_baseline "{container}" "${prefix}_baseline"',
close_index,
)
assert ready_index < baseline_index < close_index < return_index
assert mapping_index < ready_index < baseline_index < close_index < return_index
assert "{{.State.Pid}}" in runtime_test
assert '[ "$state" = "exited 0 0" ]' in runtime_test
+18 -20
View File
@@ -66,22 +66,23 @@ start_container() {
docker start "$container" >/dev/null
}
host_port() {
assert_8080_loopback_mapping() {
local container="$1"
local mapping
mapping="$(docker port "$container" 8080/tcp)"
[ -n "$mapping" ] || fail "container 8080 has no host mapping"
printf '%s\n' "${mapping##*:}"
case "$mapping" in
127.0.0.1:[0-9]*) ;;
*) fail "$container 8080 mapping is '$mapping', want 127.0.0.1:<port>" ;;
esac
}
probe_root() {
local port="$1"
python3 - "$port" <<'PY'
import sys
local container="$1"
docker exec -i "$container" python3 - <<'PY'
import urllib.request
opener = urllib.request.build_opener(urllib.request.ProxyHandler({}))
with opener.open(f"http://127.0.0.1:{sys.argv[1]}/", timeout=2) as response:
with opener.open("http://127.0.0.1:8080/", timeout=2) as response:
if response.status != 200:
raise SystemExit(f"root status {response.status}")
response.read()
@@ -90,7 +91,6 @@ PY
wait_ready() {
local container="$1"
local port="$2"
local deadline=$((SECONDS + 90))
while (( SECONDS < deadline )); do
if [ "$(docker inspect --format '{{.State.Running}}' "$container")" != "true" ]; then
@@ -98,7 +98,7 @@ wait_ready() {
fail "$container exited during readiness"
fi
if docker logs "$container" 2>&1 | grep -Fq "Stelloauth listening on 0.0.0.0:8080"; then
if probe_root "$port" >/dev/null 2>&1; then
if probe_root "$container" >/dev/null 2>&1; then
return
fi
fi
@@ -265,16 +265,14 @@ PY
}
post_invalid_worker() {
local port="$1"
local container="$1"
local response_artifact="$2"
python3 - "$port" "$response_artifact" <<'PY'
docker exec -i "$container" python3 - > "$response_artifact" <<'PY'
import json
import pathlib
import sys
import urllib.error
import urllib.request
port, artifact = sys.argv[1:]
body = {
"url": (
"https://example.invalid/am/oauth2/authorize"
@@ -292,7 +290,7 @@ body = {
"refresh_token": "SENTINEL_REFRESH_TOKEN_4e73",
}
request = urllib.request.Request(
f"http://127.0.0.1:{port}/worker",
"http://127.0.0.1:8080/worker",
data=json.dumps(body, separators=(",", ":")).encode(),
headers={"Content-Type": "application/json"},
method="POST",
@@ -307,7 +305,7 @@ except urllib.error.HTTPError as error:
response_body = error.read()
if status != 400:
raise SystemExit(f"invalid worker status {status}, want 400")
pathlib.Path(artifact).write_bytes(response_body)
sys.stdout.buffer.write(response_body)
PY
}
@@ -374,8 +372,8 @@ if [ "${SKIP_BUILD:-0}" != "1" ]; then
fi
start_container "$first_container"
first_port="$(host_port "$first_container")"
wait_ready "$first_container" "$first_port"
assert_8080_loopback_mapping "$first_container"
wait_ready "$first_container"
first_baseline="$(capture_process_baseline "$first_container")"
assert_no_9222_mapping "$first_container"
assert_loopback_cdp_listener \
@@ -384,14 +382,14 @@ assert_loopback_cdp_listener \
"${artifacts_dir}/runtime-proc-net-tcp6.txt"
probe_and_close_cdp "$first_container"
assert_processes_return_to_baseline "$first_container" "$first_baseline"
post_invalid_worker "$first_port" "${artifacts_dir}/runtime-invalid-worker-response.json"
post_invalid_worker "$first_container" "${artifacts_dir}/runtime-invalid-worker-response.json"
scan_logs "$first_container"
stop_and_assert "$first_container" "${artifacts_dir}/runtime-first-stop.txt"
scan_logs "$first_container"
start_container "$second_container"
second_port="$(host_port "$second_container")"
wait_ready "$second_container" "$second_port"
assert_8080_loopback_mapping "$second_container"
wait_ready "$second_container"
second_baseline="$(capture_process_baseline "$second_container")"
assert_no_9222_mapping "$second_container"
assert_loopback_cdp_listener \