CI / build (pull_request) Successful in 1m14s
CI / lint (pull_request) Successful in 1m30s
CI / unit (pull_request) Successful in 1m34s
CI / frontend (pull_request) Successful in 3m14s
CI / mutation (pull_request) Successful in 6m21s
CI / verify-stack (pull_request) Successful in 8m48s
The publish chain works — the sink received it:
{"kanaal": "objecten", "resource": "object", "kenmerken": {"objectType": "…"},
"hoofdObject": "http://objecten.local:8000/api/v2/objects/a68d4c46-…", …}
The check just looked for the wrong thing. An NRC notification carries hoofdObject /
resourceUrl and kenmerken — never the record data — so the `reference` inside the
RegisterRecord it wrote was never going to appear in the delivered message. Grep the sink
for the object URL instead, which is what identifies the write.
84 lines
4.3 KiB
Bash
Executable File
84 lines
4.3 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
#
|
|
# S-19b-1 (#152): verify the Objecten → NRC notification path against an ALREADY-RUNNING full
|
|
# stack. Registers an abonnement on the `objecten` kanaal pointing at a throwaway webhook sink,
|
|
# writes a RegisterRecord object (exactly as the ACL does on approval, S-19a), and asserts the sink
|
|
# receives the notification.
|
|
#
|
|
# This is the whole publish chain in one assertion: Objecten → its celery worker → NRC → nrc-beat →
|
|
# the subscriber callback. S-19a deliberately left it disconnected (ADR-0028); this proves it is
|
|
# connected for real, rather than merely configured.
|
|
#
|
|
# All in-network, reaching services by container IP (a single-label host isn't URL-valid for NRC's
|
|
# callbackUrl validator; the runner can't reach published ports — gitea-actions-gotchas.md §5/§6).
|
|
# EXCEPT Objecttypen, which must be reached by SERVICE NAME: it echoes the request Host into the
|
|
# objecttype `url` and Objecten only accepts the one matching its configured api_root (ADR-0028);
|
|
# and Objecten, reached by its `objecten.local` alias because it reflects the request Host into the
|
|
# notification's hoofdObject/resourceUrl, which NRC validates as a URL (ADR-0029).
|
|
#
|
|
# Does NOT manage the stack lifecycle, but cleans up the sink/driver it creates.
|
|
set -euo pipefail
|
|
|
|
here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
SINK_AUTH="Bearer objecten-notification-sink-token"
|
|
|
|
cleanup() { docker rm -f rr-osink rr-overify >/dev/null 2>&1 || true; }
|
|
trap cleanup EXIT
|
|
|
|
ip() { docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' "$1"; }
|
|
|
|
# Anchored on the compose replica suffix so they don't also match objecten-db / objecten-redis.
|
|
obj="$(docker ps -q --filter 'name=objecten[-_][0-9]+$' | head -1)"
|
|
nrc="$(docker ps -q --filter 'name=nrc-web' | head -1)"
|
|
[ -n "$obj" ] || { echo "ERROR: no running objecten container — bring the stack up first" >&2; exit 1; }
|
|
[ -n "$nrc" ] || { echo "ERROR: no running nrc-web container — bring the stack up first" >&2; exit 1; }
|
|
net="$(docker inspect -f '{{range $k,$_ := .NetworkSettings.Networks}}{{$k}}{{"\n"}}{{end}}' "$obj" | head -1)"
|
|
nrc_ip="$(ip "$nrc")"
|
|
echo ">> network=$net nrc=$nrc_ip"
|
|
|
|
echo ">> starting the webhook sink"
|
|
docker rm -f rr-osink >/dev/null 2>&1 || true
|
|
sink="$(docker create --network "$net" --name rr-osink -e "EXPECTED_AUTH=$SINK_AUTH" \
|
|
python:3-slim python /sink.py)"
|
|
docker cp "$here/notification-sink.py" "$sink:/sink.py" >/dev/null
|
|
docker start "$sink" >/dev/null
|
|
sleep 1
|
|
sink_ip="$(ip rr-osink)"
|
|
echo ">> sink at $sink_ip:9000"
|
|
|
|
echo ">> registering the abonnement + writing a RegisterRecord"
|
|
docker rm -f rr-overify >/dev/null 2>&1 || true
|
|
drv="$(docker create --network "$net" --name rr-overify \
|
|
-e "OBJECTEN=http://objecten.local:8000" \
|
|
-e "OBJECTEN_TOKEN=${OBJECTEN_TOKEN:-1234567890abcdef1234567890abcdef12345678}" \
|
|
-e "OBJECTTYPEN=http://objecttypen:8000" \
|
|
-e "OBJECTTYPEN_TOKEN=${OBJECTTYPEN_TOKEN:-0123456789abcdef0123456789abcdef01234567}" \
|
|
-e "NRC_BASE=http://$nrc_ip:8000" \
|
|
-e "SINK_CALLBACK=http://$sink_ip:9000/" -e "SINK_AUTH=$SINK_AUTH" \
|
|
python:3-slim python /driver.py)"
|
|
docker cp "$here/objecten-notifications-check.py" "$drv:/driver.py" >/dev/null
|
|
docker start -a "$drv"
|
|
object_url="$(docker logs rr-overify 2>/dev/null | sed -n 's/^OBJECT_URL //p' | head -1)"
|
|
docker rm -f rr-overify >/dev/null
|
|
[ -n "$object_url" ] || { echo "FAIL — the driver did not write a RegisterRecord" >&2; exit 1; }
|
|
echo ">> wrote $object_url"
|
|
|
|
# Correlate on the object URL: a notification carries hoofdObject/resourceUrl, never the record
|
|
# data, so the reference inside the record is not in the delivered message.
|
|
echo ">> waiting for the notification to reach the sink"
|
|
for _ in $(seq 1 "${NOTIFICATION_TRIES:-40}"); do
|
|
if docker logs rr-osink 2>&1 | grep -qF "$object_url"; then
|
|
echo "OK — Objecten published to NRC and the abonnement delivered it:"
|
|
docker logs rr-osink 2>&1 | grep -F "$object_url" | tail -1 | cut -c1-500
|
|
exit 0
|
|
fi
|
|
sleep 2
|
|
done
|
|
|
|
echo "FAIL — no 'objecten' notification for $object_url reached the sink." >&2
|
|
echo " Objecten accepted the write, so the gap is downstream: the celery broker/worker," >&2
|
|
echo " the kanaal registration, or Objecten's notifications_config." >&2
|
|
echo "--- sink log ---" >&2; docker logs rr-osink 2>&1 | tail -8 >&2
|
|
echo "--- objecten log ---" >&2; docker logs "$obj" 2>&1 | tail -15 >&2
|
|
exit 1
|