#!/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