amduat-api/scripts/test_fed_smoke.sh

389 lines
9.4 KiB
Bash
Raw Permalink Normal View History

#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
HTTP_HELPER="${ROOT_DIR}/build/amduatd_http_unix"
USE_HTTP_HELPER=0
TMPDIR="${TMPDIR:-/tmp}"
mkdir -p "${TMPDIR}"
if ! command -v grep >/dev/null 2>&1; then
echo "skip: grep not found" >&2
exit 77
fi
if ! command -v awk >/dev/null 2>&1; then
echo "skip: awk not found" >&2
exit 77
fi
if command -v curl >/dev/null 2>&1; then
if curl --help 2>/dev/null | grep -q -- '--unix-socket'; then
USE_HTTP_HELPER=0
else
USE_HTTP_HELPER=1
fi
else
USE_HTTP_HELPER=1
fi
if [[ "${USE_HTTP_HELPER}" -eq 1 && ! -x "${HTTP_HELPER}" ]]; then
echo "skip: curl lacks --unix-socket support and helper missing" >&2
exit 77
fi
AMDUATD_BIN="${ROOT_DIR}/build/amduatd"
ASL_BIN="${ROOT_DIR}/vendor/amduat/build/amduat-asl"
if [[ ! -x "${AMDUATD_BIN}" || ! -x "${ASL_BIN}" ]]; then
echo "missing binaries; build amduatd and amduat-asl first" >&2
exit 1
fi
tmp_root="$(mktemp -d -p "${TMPDIR}" amduatd-fed-smoke-XXXXXX)"
root_a="${tmp_root}/a"
root_b="${tmp_root}/b"
sock_a="${tmp_root}/amduatd-a.sock"
sock_b="${tmp_root}/amduatd-b.sock"
space_id="smoke"
log_a="${tmp_root}/amduatd-a.log"
log_b="${tmp_root}/amduatd-b.log"
cleanup() {
if [[ -n "${pid_a:-}" ]]; then
kill "${pid_a}" >/dev/null 2>&1 || true
fi
if [[ -n "${pid_b:-}" ]]; then
kill "${pid_b}" >/dev/null 2>&1 || true
fi
rm -rf "${tmp_root}"
}
trap cleanup EXIT
mkdir -p "${root_a}" "${root_b}"
"${ASL_BIN}" init --root "${root_a}"
"${ASL_BIN}" init --root "${root_b}"
"${AMDUATD_BIN}" --root "${root_a}" --sock "${sock_a}" \
--store-backend index --space "${space_id}" \
--fed-enable --fed-transport unix \
--fed-unix-sock "${sock_b}" --fed-domain-id 1 \
>"${log_a}" 2>&1 &
pid_a=$!
"${AMDUATD_BIN}" --root "${root_b}" --sock "${sock_b}" \
--store-backend index --space "${space_id}" \
--fed-enable --fed-transport unix \
--fed-unix-sock "${sock_a}" --fed-domain-id 2 \
>"${log_b}" 2>&1 &
pid_b=$!
http_get() {
local sock="$1"
local path="$2"
shift 2
if [[ "${USE_HTTP_HELPER}" -eq 1 ]]; then
"${HTTP_HELPER}" --sock "${sock}" --method GET --path "${path}" "$@"
else
curl --silent --show-error --fail \
--unix-socket "${sock}" \
"$@" \
"http://localhost${path}"
fi
}
http_get_allow() {
local sock="$1"
local path="$2"
shift 2
if [[ "${USE_HTTP_HELPER}" -eq 1 ]]; then
"${HTTP_HELPER}" --sock "${sock}" --method GET --path "${path}" \
--allow-status "$@"
else
curl --silent --show-error \
--unix-socket "${sock}" \
"$@" \
"http://localhost${path}"
fi
}
http_post() {
local sock="$1"
local path="$2"
local data="$3"
shift 3
if [[ "${USE_HTTP_HELPER}" -eq 1 ]]; then
"${HTTP_HELPER}" --sock "${sock}" --method POST --path "${path}" \
--data "${data}" \
"$@"
else
curl --silent --show-error --fail \
--unix-socket "${sock}" \
"$@" \
--data-binary "${data}" \
"http://localhost${path}"
fi
}
http_post_allow() {
local sock="$1"
local path="$2"
local data="$3"
shift 3
if [[ "${USE_HTTP_HELPER}" -eq 1 ]]; then
"${HTTP_HELPER}" --sock "${sock}" --method POST --path "${path}" \
--data "${data}" --allow-status \
"$@"
else
curl --silent --show-error \
--unix-socket "${sock}" \
"$@" \
--data-binary "${data}" \
"http://localhost${path}"
fi
}
wait_for_ready() {
local sock="$1"
local pid="$2"
local log_path="$3"
local i
for i in $(seq 1 100); do
if ! kill -0 "${pid}" >/dev/null 2>&1; then
if [[ -f "${log_path}" ]] && grep -q "bind: Operation not permitted" "${log_path}"; then
echo "skip: bind not permitted for unix socket" >&2
exit 77
fi
if [[ -f "${log_path}" ]]; then
cat "${log_path}" >&2
fi
return 1
fi
if [[ -S "${sock}" ]]; then
if http_get "${sock}" "/v1/meta" >/dev/null 2>&1; then
return 0
fi
fi
sleep 0.1
done
return 1
}
if ! wait_for_ready "${sock_a}" "${pid_a}" "${log_a}"; then
echo "daemon A not ready" >&2
exit 1
fi
if ! wait_for_ready "${sock_b}" "${pid_b}" "${log_b}"; then
echo "daemon B not ready" >&2
exit 1
fi
payload="fed-smoke"
artifact_resp="$(
http_post "${sock_a}" "/v1/artifacts" "${payload}" \
--header "Content-Type: application/octet-stream" \
--header "X-Amduat-Space: ${space_id}"
)" || {
echo "artifact POST failed" >&2
if [[ -f "${log_a}" ]]; then
cat "${log_a}" >&2
fi
exit 1
}
ref="$(
printf '%s' "${artifact_resp}" \
| tr -d '\r\n' \
| awk 'match($0, /"ref":"[^"]+"/) {print substr($0, RSTART+7, RLENGTH-8)}'
)"
if [[ -z "${ref}" ]]; then
echo "failed to parse ref from daemon A" >&2
echo "artifact response: ${artifact_resp}" >&2
if [[ -f "${log_a}" ]]; then
cat "${log_a}" >&2
fi
exit 1
fi
plan_resp="$(
http_get_allow "${sock_b}" "/v1/fed/pull/plan?peer=1&limit=8" \
--header "X-Amduat-Space: ${space_id}"
)" || {
echo "pull plan failed" >&2
if [[ -f "${log_b}" ]]; then
cat "${log_b}" >&2
fi
exit 1
}
if ! echo "${plan_resp}" | grep -q "\"record_count\":"; then
echo "pull plan malformed" >&2
echo "plan response: ${plan_resp}" >&2
exit 1
fi
if echo "${plan_resp}" | grep -q "\"record_count\":0"; then
echo "pull plan empty" >&2
echo "plan response: ${plan_resp}" >&2
exit 1
fi
pull_resp="$(
http_post "${sock_b}" "/v1/fed/pull?peer=1&limit=8" "" \
--header "X-Amduat-Space: ${space_id}"
)" || {
echo "pull apply failed" >&2
if [[ -f "${log_b}" ]]; then
cat "${log_b}" >&2
fi
exit 1
}
if ! echo "${pull_resp}" | grep -q "\"advanced\":true"; then
echo "pull did not advance cursor" >&2
echo "pull response: ${pull_resp}" >&2
exit 1
fi
cursor_json="$(
http_get_allow "${sock_b}" "/v1/fed/cursor?peer=1" \
--header "X-Amduat-Space: ${space_id}"
)" || {
echo "cursor fetch failed" >&2
if [[ -f "${log_a}" ]]; then
cat "${log_a}" >&2
fi
if [[ -f "${log_b}" ]]; then
cat "${log_b}" >&2
fi
exit 1
}
echo "${cursor_json}" | grep -q "\"last_logseq\":" || {
echo "cursor missing last_logseq" >&2
echo "cursor response: ${cursor_json}" >&2
exit 1
}
payload_b="$(
http_get "${sock_b}" "/v1/artifacts/${ref}" \
--header "X-Amduat-Space: ${space_id}"
)" || {
echo "artifact fetch failed" >&2
if [[ -f "${log_b}" ]]; then
cat "${log_b}" >&2
fi
exit 1
}
if [[ "${payload_b}" != "${payload}" ]]; then
echo "payload mismatch after pull" >&2
exit 1
fi
payload_push="fed-smoke-push"
artifact_resp_push="$(
http_post "${sock_b}" "/v1/artifacts" "${payload_push}" \
--header "Content-Type: application/octet-stream" \
--header "X-Amduat-Space: ${space_id}"
)" || {
echo "artifact POST failed on B" >&2
if [[ -f "${log_b}" ]]; then
cat "${log_b}" >&2
fi
exit 1
}
ref_push="$(
printf '%s' "${artifact_resp_push}" \
| tr -d '\r\n' \
| awk 'match($0, /"ref":"[^"]+"/) {print substr($0, RSTART+7, RLENGTH-8)}'
)"
if [[ -z "${ref_push}" ]]; then
echo "failed to parse ref from daemon B" >&2
echo "artifact response: ${artifact_resp_push}" >&2
if [[ -f "${log_b}" ]]; then
cat "${log_b}" >&2
fi
exit 1
fi
push_plan_resp="$(
http_get_allow "${sock_b}" "/v1/fed/push/plan?peer=1&limit=8" \
--header "X-Amduat-Space: ${space_id}"
)" || {
echo "push plan failed" >&2
if [[ -f "${log_b}" ]]; then
cat "${log_b}" >&2
fi
exit 1
}
if ! echo "${push_plan_resp}" | grep -q "\"record_count\":"; then
echo "push plan malformed (missing endpoint?)" >&2
echo "push plan response: ${push_plan_resp}" >&2
exit 1
fi
if echo "${push_plan_resp}" | grep -q "\"record_count\":0"; then
echo "push plan empty" >&2
echo "push plan response: ${push_plan_resp}" >&2
exit 1
fi
push_cursor_before="$(
printf '%s' "${push_plan_resp}" \
| tr -d '\r\n' \
| awk 'match($0, /"cursor":\{[^}]*\}/) {seg=substr($0, RSTART, RLENGTH); if (match(seg, /"last_logseq":[0-9]+/)) {print substr(seg, RSTART+14, RLENGTH-14)}}'
)"
push_resp="$(
http_post_allow "${sock_b}" "/v1/fed/push?peer=1&limit=8" "" \
--header "X-Amduat-Space: ${space_id}"
)" || {
echo "push apply failed" >&2
if [[ -f "${log_b}" ]]; then
cat "${log_b}" >&2
fi
exit 1
}
if ! echo "${push_resp}" | grep -q "\"advanced\":true"; then
echo "push did not advance cursor" >&2
echo "push response: ${push_resp}" >&2
exit 1
fi
payload_a="$(
http_get "${sock_a}" "/v1/artifacts/${ref_push}" \
--header "X-Amduat-Space: ${space_id}"
)" || {
echo "artifact fetch failed on A" >&2
if [[ -f "${log_a}" ]]; then
cat "${log_a}" >&2
fi
exit 1
}
if [[ "${payload_a}" != "${payload_push}" ]]; then
echo "payload mismatch after push" >&2
exit 1
fi
push_plan_after="$(
http_get_allow "${sock_b}" "/v1/fed/push/plan?peer=1&limit=1" \
--header "X-Amduat-Space: ${space_id}"
)" || {
echo "push plan after failed" >&2
if [[ -f "${log_b}" ]]; then
cat "${log_b}" >&2
fi
exit 1
}
push_cursor_after="$(
printf '%s' "${push_plan_after}" \
| tr -d '\r\n' \
| awk 'match($0, /"cursor":\{[^}]*\}/) {seg=substr($0, RSTART, RLENGTH); if (match(seg, /"last_logseq":[0-9]+/)) {print substr(seg, RSTART+14, RLENGTH-14)}}'
)"
if [[ -n "${push_cursor_before}" && -n "${push_cursor_after}" ]]; then
if [[ "${push_cursor_after}" -lt "${push_cursor_before}" ]]; then
echo "push cursor did not advance" >&2
echo "cursor before: ${push_cursor_before}" >&2
echo "cursor after: ${push_cursor_after}" >&2
exit 1
fi
fi
echo "fed smoke ok"