Compare commits
3 Commits
86cc65f4d9
...
a256db1a23
| Author | SHA1 | Date | |
|---|---|---|---|
| a256db1a23 | |||
| 4d07285dcd | |||
| f3e9db7147 |
@@ -80,6 +80,22 @@ jobs:
|
|||||||
if: failure()
|
if: failure()
|
||||||
run: docker compose -f infra/openzaak/docker-compose.yml down --volumes 2>&1 || true
|
run: docker compose -f infra/openzaak/docker-compose.yml down --volumes 2>&1 || true
|
||||||
|
|
||||||
|
notifications:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: https://github.com/actions/checkout@v4
|
||||||
|
# `make verify-notifications` brings up OpenZaak + NRC, seeds, and asserts a
|
||||||
|
# zaak-create notification is delivered to a subscriber — all via containers on
|
||||||
|
# the compose network (the runner can't reach published ports; §5). Needs only
|
||||||
|
# Docker + egress (base images, selectielijst.openzaak.nl).
|
||||||
|
- run: make verify-notifications
|
||||||
|
- name: dump OpenZaak/NRC logs on failure
|
||||||
|
if: failure()
|
||||||
|
run: docker compose -f infra/openzaak/docker-compose.yml -f infra/opennotificaties/docker-compose.yml logs --no-color --tail=100 oz-init openzaak nrc-init nrc-web nrc-celery nrc-beat 2>&1 || true
|
||||||
|
- name: tear down on failure
|
||||||
|
if: failure()
|
||||||
|
run: docker compose -f infra/openzaak/docker-compose.yml -f infra/opennotificaties/docker-compose.yml down --volumes 2>&1 || true
|
||||||
|
|
||||||
compose-smoke:
|
compose-smoke:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
|||||||
24
Makefile
24
Makefile
@@ -18,7 +18,7 @@ WAIT_SVCS := openzaak nrc-web acl bff
|
|||||||
# volumes are `external`, so compose won't remove them — CFG_VOLS lists them for
|
# volumes are `external`, so compose won't remove them — CFG_VOLS lists them for
|
||||||
# explicit teardown. See docs/runbooks/gitea-actions-gotchas.md.
|
# explicit teardown. See docs/runbooks/gitea-actions-gotchas.md.
|
||||||
SEED := bash infra/seed-config.sh
|
SEED := bash infra/seed-config.sh
|
||||||
CFG_VOLS := rr-oz-config rr-kc-realms rr-fl-bpmn
|
CFG_VOLS := rr-oz-config rr-nrc-config rr-kc-realms rr-fl-bpmn
|
||||||
# Local-only stack: same services but config is bind-mounted (no seed step), so a
|
# Local-only stack: same services but config is bind-mounted (no seed step), so a
|
||||||
# plain `docker compose -f infra/docker-compose.local.yml up` works on any local
|
# plain `docker compose -f infra/docker-compose.local.yml up` works on any local
|
||||||
# engine. This is the no-make / Windows-friendly path. See that file's header.
|
# engine. This is the no-make / Windows-friendly path. See that file's header.
|
||||||
@@ -43,7 +43,7 @@ export DOCKER_HOST := unix://$(PODMAN_SOCK)
|
|||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
.PHONY: ci lint build unit mutation integration smoke up down local local-down changelog openzaak-up openzaak-smoke openzaak-seed openzaak-down stack-up stack-smoke stack-down keycloak-up keycloak-smoke keycloak-down flowable-up flowable-smoke flowable-down help
|
.PHONY: ci lint build unit mutation integration verify-notifications smoke up down local local-down changelog openzaak-up openzaak-smoke openzaak-seed openzaak-down stack-up stack-smoke stack-down keycloak-up keycloak-smoke keycloak-down flowable-up flowable-smoke flowable-down help
|
||||||
|
|
||||||
## ci: run the full pipeline — lint, build, unit, mutation, smoke (mirrors Gitea Actions)
|
## ci: run the full pipeline — lint, build, unit, mutation, smoke (mirrors Gitea Actions)
|
||||||
ci: lint build unit mutation smoke
|
ci: lint build unit mutation smoke
|
||||||
@@ -77,14 +77,14 @@ mutation:
|
|||||||
# podman-compose, and needing no `--wait` flag or host port access. The one-shots
|
# podman-compose, and needing no `--wait` flag or host port access. The one-shots
|
||||||
# (oz-init, flowable-init) aren't polled; they just need to have run.
|
# (oz-init, flowable-init) aren't polled; they just need to have run.
|
||||||
smoke:
|
smoke:
|
||||||
$(SEED) oz kc fl
|
$(SEED) oz nrc kc fl
|
||||||
docker compose -f $(COMPOSE) up -d --build
|
docker compose -f $(COMPOSE) up -d --build
|
||||||
bash -c 'WAIT_TIMEOUT=420 bash infra/wait-healthy.sh $(WAIT_SVCS); rc=$$?; docker compose -f $(COMPOSE) down --volumes; docker volume rm -f $(CFG_VOLS) >/dev/null 2>&1; exit $$rc'
|
bash -c 'WAIT_TIMEOUT=420 bash infra/wait-healthy.sh $(WAIT_SVCS); rc=$$?; docker compose -f $(COMPOSE) down --volumes; docker volume rm -f $(CFG_VOLS) >/dev/null 2>&1; exit $$rc'
|
||||||
|
|
||||||
## up: seed config volumes and start the full stack (use instead of bare
|
## up: seed config volumes and start the full stack (use instead of bare
|
||||||
## `docker compose up`, which can't self-seed the external config volumes)
|
## `docker compose up`, which can't self-seed the external config volumes)
|
||||||
up:
|
up:
|
||||||
$(SEED) oz kc fl
|
$(SEED) oz nrc kc fl
|
||||||
docker compose -f $(COMPOSE) up -d --build
|
docker compose -f $(COMPOSE) up -d --build
|
||||||
|
|
||||||
## down: stop and remove the local stack (incl. the external config volumes)
|
## down: stop and remove the local stack (incl. the external config volumes)
|
||||||
@@ -147,10 +147,18 @@ openzaak-down:
|
|||||||
docker compose -f $(OZ_COMPOSE) down --volumes
|
docker compose -f $(OZ_COMPOSE) down --volumes
|
||||||
-docker volume rm -f rr-oz-config
|
-docker volume rm -f rr-oz-config
|
||||||
|
|
||||||
## stack-up: start OpenZaak + Open Notificaties together (shared network)
|
## verify-notifications: end-to-end check that a zaak created in OpenZaak is
|
||||||
|
## published to NRC and delivered to a subscriber (S-01-c). Brings the stack up,
|
||||||
|
## seeds, drives a zaak + sink abonnement inside the network, asserts delivery,
|
||||||
|
## tears down. Runner-safe (no host-port access). See infra/verify-notifications.sh.
|
||||||
|
verify-notifications:
|
||||||
|
bash infra/verify-notifications.sh
|
||||||
|
|
||||||
|
## stack-up: start OpenZaak + Open Notificaties together (shared network), with
|
||||||
|
## OpenZaak publishing notifications to NRC (S-01-c).
|
||||||
stack-up:
|
stack-up:
|
||||||
$(SEED) oz
|
$(SEED) oz nrc
|
||||||
docker compose $(STACK_FILES) up -d
|
OZ_NOTIFICATIONS_DISABLED=false docker compose $(STACK_FILES) up -d
|
||||||
|
|
||||||
## stack-smoke: start both, assert OpenZaak (403/302/200) and NRC (302) are reachable
|
## stack-smoke: start both, assert OpenZaak (403/302/200) and NRC (302) are reachable
|
||||||
stack-smoke: stack-up
|
stack-smoke: stack-up
|
||||||
@@ -169,7 +177,7 @@ stack-smoke: stack-up
|
|||||||
## stack-down: stop and remove both stacks (wipes data)
|
## stack-down: stop and remove both stacks (wipes data)
|
||||||
stack-down:
|
stack-down:
|
||||||
docker compose $(STACK_FILES) down --volumes
|
docker compose $(STACK_FILES) down --volumes
|
||||||
-docker volume rm -f rr-oz-config
|
-docker volume rm -f rr-oz-config rr-nrc-config
|
||||||
|
|
||||||
## keycloak-up: start Keycloak with the four imported realms
|
## keycloak-up: start Keycloak with the four imported realms
|
||||||
keycloak-up:
|
keycloak-up:
|
||||||
|
|||||||
76
docs/architecture/adr-0007-notification-wiring.md
Normal file
76
docs/architecture/adr-0007-notification-wiring.md
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
# ADR-0007: Wiring OpenZaak → Open Notificaties (NRC) for notifications
|
||||||
|
|
||||||
|
- **Status:** Accepted
|
||||||
|
- **Date:** 2026-06-29
|
||||||
|
- **Deciders:** Respellion engineering
|
||||||
|
- **Relates to:** S-01-c (#56); completes S-01 (#2); unblocks the Event Subscriber (#7); builds on ADR-0002 (catalogus/seed) and ADR-0006 (runner-safe container harnesses)
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
S-01 brought OpenZaak + Open Notificaties (NRC) up in compose but **deferred the
|
||||||
|
notification wiring**: OpenZaak ran with `NOTIFICATIONS_DISABLED=true` and NRC's
|
||||||
|
`setup_configuration` was empty. The walking skeleton (PRD §12) needs the upstream
|
||||||
|
event path — a zaak created in OpenZaak must publish a notification NRC fans out to
|
||||||
|
subscribers — before the Event Subscriber (#7) can consume it.
|
||||||
|
|
||||||
|
The OpenZaak↔NRC handshake is intricate and several details are non-obvious; they
|
||||||
|
were nailed down by iterating `setup_configuration` against the running stack.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
**Provision both sides declaratively via `setup_configuration`, authenticate with the
|
||||||
|
existing `big-reference-seed` client, and run NRC's celery-beat so deliveries happen.**
|
||||||
|
|
||||||
|
- **OpenZaak** (`infra/openzaak/setup_configuration/data.yaml`): a `zgw_consumers`
|
||||||
|
service `nrc` (api_type `nrc`, the NRC API root) plus `notifications_config` naming
|
||||||
|
it. `NOTIFICATIONS_DISABLED` is flipped to `false` **only when NRC is present** —
|
||||||
|
the full stack and the local twin set it; OpenZaak-only bring-ups (`openzaak-up`,
|
||||||
|
the ACL integration test) default it back to `true` via `OZ_NOTIFICATIONS_DISABLED`
|
||||||
|
so they don't 500 publishing to an absent NRC.
|
||||||
|
- **NRC** (`infra/opennotificaties/setup_configuration/data.yaml`): the
|
||||||
|
`big-reference-seed` JWT credential (to verify OpenZaak's token), a `zgw_consumers`
|
||||||
|
`ac` service pointing at **OpenZaak's Autorisaties API**, the `autorisaties_api`
|
||||||
|
step delegating authorization to that AC, and the `zaken` kanaal. NRC's init
|
||||||
|
container switches from `migrate` to `/setup_configuration.sh`; its data.yaml is
|
||||||
|
delivered through the `rr-nrc-config` external volume by `infra/seed-config.sh`
|
||||||
|
(the same `docker cp` pattern as OpenZaak — bind mounts don't reach the CI runner's
|
||||||
|
daemon).
|
||||||
|
- **celery-beat is required.** NRC accepts a notification and writes a
|
||||||
|
`ScheduledNotification`; a periodic `execute_notifications` task (celery-beat,
|
||||||
|
every `NOTIFICATION_SEC_INTERVAL`s) drains it to the worker for delivery. The lean
|
||||||
|
S-01 stack dropped beat — so notifications were accepted but never delivered. An
|
||||||
|
`nrc-beat` service is added to every compose; the interval is lowered to 5s.
|
||||||
|
|
||||||
|
Verification is a runner-safe smoke (`make verify-notifications`,
|
||||||
|
`infra/verify-notifications.sh` + a `notifications` CI job): it brings the stack up,
|
||||||
|
seeds a published BIG zaaktype, registers an abonnement to a webhook sink, creates a
|
||||||
|
zaak, and asserts the sink receives the `zaken`/`create` notification — all from
|
||||||
|
containers **inside** the compose network (ADR-0006).
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
- **Positive:** the walking-skeleton event path works end to end; #7 can consume real
|
||||||
|
notifications; the wiring is declarative and reproducible from a fresh `make`.
|
||||||
|
- **Gotchas captured (see gitea-actions-gotchas.md):**
|
||||||
|
- **Single-label hosts aren't URL-valid.** OpenZaak/NRC reject `http://openzaak…`
|
||||||
|
/`http://nrc-web…` in URLs they validate (Django `URLValidator`); the verify
|
||||||
|
harness reaches services and registers the sink callback **by container IP**.
|
||||||
|
- **Abonnement callbacks must enforce auth.** NRC probes the callback during
|
||||||
|
registration and refuses it (`no-auth-on-callback-url`) unless it returns 401
|
||||||
|
without the configured `Authorization`; the sink enforces a bearer token.
|
||||||
|
- **Cost:** an extra long-running service (`nrc-beat`) per stack, and the verify job
|
||||||
|
needs egress (base images + `selectielijst.openzaak.nl`, since the published
|
||||||
|
zaaktype the check creates a zaak against depends on it — ADR-0006).
|
||||||
|
- **Dev-only credentials** reused (`big-reference-seed` / its secret) across publish,
|
||||||
|
AC lookup, and seeding — acceptable for the reference app, not production.
|
||||||
|
|
||||||
|
## Alternatives considered
|
||||||
|
|
||||||
|
- **NRC with its own (non-AC) authorization** — rejected: delegating to OpenZaak's
|
||||||
|
Autorisaties API is the upstream-intended model and reuses the applicatie that
|
||||||
|
already grants `heeft_alle_autorisaties`.
|
||||||
|
- **Keep beat out, deliver synchronously** — not an option: Open Notificaties 1.16
|
||||||
|
delivers via scheduled notifications drained by beat; there is no sync path.
|
||||||
|
- **A persistent abonnement in `setup_configuration`** instead of registering one in
|
||||||
|
the verify harness — deferred: the real subscriber is #7; the harness's sink
|
||||||
|
abonnement is throwaway and IP-specific.
|
||||||
@@ -18,6 +18,7 @@ and CI cannot drift:
|
|||||||
| `unit` | `make unit` → `dotnet test … -c Release --filter "Category!=Integration"` | .NET 10 SDK |
|
| `unit` | `make unit` → `dotnet test … -c Release --filter "Category!=Integration"` | .NET 10 SDK |
|
||||||
| `mutation` | `make mutation` → `dotnet tool restore` → `dotnet stryker` (ACL); uploads the HTML report as an artifact | .NET 10 SDK |
|
| `mutation` | `make mutation` → `dotnet tool restore` → `dotnet stryker` (ACL); uploads the HTML report as an artifact | .NET 10 SDK |
|
||||||
| `integration` | `make integration` → `infra/run-integration.sh`: OpenZaak up → seed a **published** BIG zaaktype + run `Acl.IntegrationTests` **as containers inside the compose network** → tear down | container engine + egress (base images, nuget, `selectielijst.openzaak.nl`) |
|
| `integration` | `make integration` → `infra/run-integration.sh`: OpenZaak up → seed a **published** BIG zaaktype + run `Acl.IntegrationTests` **as containers inside the compose network** → tear down | container engine + egress (base images, nuget, `selectielijst.openzaak.nl`) |
|
||||||
|
| `notifications` | `make verify-notifications` → bring up OpenZaak + NRC → seed → assert a zaak-create notification reaches a subscriber (containers on the network) → tear down | container engine + egress (base images, `selectielijst.openzaak.nl`) |
|
||||||
| `compose-smoke` | `make smoke` → seed config volumes → `up -d` (full stack) → `up --wait` durable services → `down` | container engine + compose v2 |
|
| `compose-smoke` | `make smoke` → seed config volumes → `up -d` (full stack) → `up --wait` durable services → `down` | container engine + compose v2 |
|
||||||
|
|
||||||
> **The `integration` job needs no `setup-dotnet`.** dotnet runs inside the test
|
> **The `integration` job needs no `setup-dotnet`.** dotnet runs inside the test
|
||||||
|
|||||||
@@ -152,8 +152,30 @@ service by name (`http://openzaak:8000`). For a test/seed that needs the repo's
|
|||||||
code, deliver it via a **built image** (not a bind mount — §1), then
|
code, deliver it via a **built image** (not a bind mount — §1), then
|
||||||
`docker run --network <stack>_cg …`.
|
`docker run --network <stack>_cg …`.
|
||||||
|
|
||||||
**Applied** — `make integration` (the ACL ↔ real-OpenZaak test, ADR-0006) does
|
**Applied** — `make integration` (ADR-0006) and `make verify-notifications` (ADR-0007)
|
||||||
exactly this: `infra/run-integration.sh` runs the seed and the test as containers on
|
do exactly this: they run the seed/test/driver as containers on the stack network and
|
||||||
the OpenZaak network and reaches it by **container IP** (a single-label service name
|
reach services by **container IP** (see §6).
|
||||||
like `openzaak` isn't URL-valid — OpenZaak echoes the request host into the URLs it
|
|
||||||
returns and then rejects them with Django's `URLValidator`; an IPv4 literal passes).
|
---
|
||||||
|
|
||||||
|
## 6. OpenZaak / NRC reject single-label hosts in URLs
|
||||||
|
|
||||||
|
**Symptom** — talking to OpenZaak or NRC by compose **service name** fails where a URL
|
||||||
|
is validated: catalogus/zaaktype filters, the zaak `zaaktype` URL, and abonnement
|
||||||
|
`callbackUrl` come back `400 "Voer een geldige URL in."` — even though the host
|
||||||
|
resolves and is reachable.
|
||||||
|
|
||||||
|
**Why** — these apps validate URLs with Django's `URLValidator`, which rejects a
|
||||||
|
**single-label** host like `openzaak` or `nrc-web` (no dot, and not `localhost`).
|
||||||
|
`localhost` passes (so it's invisible in host-port-based local runs); in-network the
|
||||||
|
reality is a service name or an IPv4 literal — and only the IP passes.
|
||||||
|
|
||||||
|
**Fix** — in-network tooling reaches OpenZaak/NRC by **container IP**
|
||||||
|
(`docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'`), not
|
||||||
|
service name; the notif verify harness also registers the sink callback by IP.
|
||||||
|
(`infra/run-integration.sh`, `infra/verify-notifications.sh`.)
|
||||||
|
|
||||||
|
**Related — abonnement callbacks must enforce auth.** NRC probes a callback when an
|
||||||
|
abonnement is registered and refuses it (`no-auth-on-callback-url`) unless it returns
|
||||||
|
**401** without the configured `Authorization`. The verify sink
|
||||||
|
(`infra/notification-sink.py`) enforces a bearer token for exactly this reason.
|
||||||
|
|||||||
@@ -59,7 +59,8 @@ services:
|
|||||||
CELERY_BROKER_URL: redis://oz-redis:6379/1
|
CELERY_BROKER_URL: redis://oz-redis:6379/1
|
||||||
CELERY_RESULT_BACKEND: redis://oz-redis:6379/1
|
CELERY_RESULT_BACKEND: redis://oz-redis:6379/1
|
||||||
DISABLE_2FA: "true"
|
DISABLE_2FA: "true"
|
||||||
NOTIFICATIONS_DISABLED: "true"
|
# Publish notifications to NRC (always present in this twin). See ADR-0007.
|
||||||
|
NOTIFICATIONS_DISABLED: "false"
|
||||||
OPENZAAK_SUPERUSER_USERNAME: admin
|
OPENZAAK_SUPERUSER_USERNAME: admin
|
||||||
DJANGO_SUPERUSER_PASSWORD: admin
|
DJANGO_SUPERUSER_PASSWORD: admin
|
||||||
OPENZAAK_SUPERUSER_EMAIL: admin@localhost
|
OPENZAAK_SUPERUSER_EMAIL: admin@localhost
|
||||||
@@ -122,7 +123,9 @@ services:
|
|||||||
networks: [cg]
|
networks: [cg]
|
||||||
|
|
||||||
nrc-init:
|
nrc-init:
|
||||||
# Migrations only (see the canonical compose / ADR-0002); no config needed.
|
# Migrations + setup_configuration (S-01-c): the JWT credential, Autorisaties-API
|
||||||
|
# delegation, and the `zaken` kanaal that let OpenZaak publish. Config is
|
||||||
|
# bind-mounted here (this twin is the local/no-make path). See ADR-0007.
|
||||||
image: docker.io/openzaak/open-notificaties:${OPENNOTIFICATIES_TAG:-1.16.1}
|
image: docker.io/openzaak/open-notificaties:${OPENNOTIFICATIES_TAG:-1.16.1}
|
||||||
environment: &nrc-env
|
environment: &nrc-env
|
||||||
DJANGO_SETTINGS_MODULE: nrc.conf.docker
|
DJANGO_SETTINGS_MODULE: nrc.conf.docker
|
||||||
@@ -141,7 +144,11 @@ services:
|
|||||||
OPENNOTIFICATIES_SUPERUSER_USERNAME: admin
|
OPENNOTIFICATIES_SUPERUSER_USERNAME: admin
|
||||||
DJANGO_SUPERUSER_PASSWORD: admin
|
DJANGO_SUPERUSER_PASSWORD: admin
|
||||||
OPENNOTIFICATIES_SUPERUSER_EMAIL: admin@localhost
|
OPENNOTIFICATIES_SUPERUSER_EMAIL: admin@localhost
|
||||||
command: ["sh", "-c", "/wait_for_db.sh && OTEL_SDK_DISABLED=True python src/manage.py migrate"]
|
RUN_SETUP_CONFIG: "true"
|
||||||
|
NOTIFICATION_SEC_INTERVAL: "5"
|
||||||
|
command: /setup_configuration.sh
|
||||||
|
volumes:
|
||||||
|
- ./opennotificaties/setup_configuration:/app/setup_configuration:ro,z
|
||||||
depends_on:
|
depends_on:
|
||||||
nrc-db:
|
nrc-db:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
@@ -176,6 +183,17 @@ services:
|
|||||||
condition: service_completed_successfully
|
condition: service_completed_successfully
|
||||||
networks: [cg]
|
networks: [cg]
|
||||||
|
|
||||||
|
# Celery beat drains scheduled notifications to subscribers — required for
|
||||||
|
# delivery, not optional. See ADR-0007.
|
||||||
|
nrc-beat:
|
||||||
|
image: docker.io/openzaak/open-notificaties:${OPENNOTIFICATIES_TAG:-1.16.1}
|
||||||
|
environment: *nrc-env
|
||||||
|
command: /celery_beat.sh
|
||||||
|
depends_on:
|
||||||
|
nrc-init:
|
||||||
|
condition: service_completed_successfully
|
||||||
|
networks: [cg]
|
||||||
|
|
||||||
# ── Keycloak (S-02) ──────────────────────────────────────────────────────
|
# ── Keycloak (S-02) ──────────────────────────────────────────────────────
|
||||||
keycloak:
|
keycloak:
|
||||||
image: quay.io/keycloak/keycloak:26.1
|
image: quay.io/keycloak/keycloak:26.1
|
||||||
|
|||||||
@@ -62,7 +62,10 @@ services:
|
|||||||
CELERY_BROKER_URL: redis://oz-redis:6379/1
|
CELERY_BROKER_URL: redis://oz-redis:6379/1
|
||||||
CELERY_RESULT_BACKEND: redis://oz-redis:6379/1
|
CELERY_RESULT_BACKEND: redis://oz-redis:6379/1
|
||||||
DISABLE_2FA: "true"
|
DISABLE_2FA: "true"
|
||||||
NOTIFICATIONS_DISABLED: "true"
|
# Publish notifications to NRC (always present in this full stack). The NRC
|
||||||
|
# service + notifications_config are provisioned by setup_configuration
|
||||||
|
# (infra/openzaak/setup_configuration/data.yaml). See ADR-0007 / S-01-c.
|
||||||
|
NOTIFICATIONS_DISABLED: "false"
|
||||||
OPENZAAK_SUPERUSER_USERNAME: admin
|
OPENZAAK_SUPERUSER_USERNAME: admin
|
||||||
DJANGO_SUPERUSER_PASSWORD: admin
|
DJANGO_SUPERUSER_PASSWORD: admin
|
||||||
OPENZAAK_SUPERUSER_EMAIL: admin@localhost
|
OPENZAAK_SUPERUSER_EMAIL: admin@localhost
|
||||||
@@ -146,11 +149,17 @@ services:
|
|||||||
OPENNOTIFICATIES_SUPERUSER_USERNAME: admin
|
OPENNOTIFICATIES_SUPERUSER_USERNAME: admin
|
||||||
DJANGO_SUPERUSER_PASSWORD: admin
|
DJANGO_SUPERUSER_PASSWORD: admin
|
||||||
OPENNOTIFICATIES_SUPERUSER_EMAIL: admin@localhost
|
OPENNOTIFICATIES_SUPERUSER_EMAIL: admin@localhost
|
||||||
# Migrations only for now. No setup_configuration steps are enabled yet (the
|
RUN_SETUP_CONFIG: "true"
|
||||||
# OZ<->NRC notification wiring lands in S-06), and NRC's `setup_configuration`
|
# nrc-beat fires `execute_notifications` this often to drain scheduled
|
||||||
# aborts with "No steps enabled" on the empty data.yaml — so we run `migrate`
|
# notifications to subscribers (upstream default 20s). See ADR-0007.
|
||||||
# directly instead of /setup_configuration.sh. See data.yaml and ADR-0002.
|
NOTIFICATION_SEC_INTERVAL: "5"
|
||||||
command: ["sh", "-c", "/wait_for_db.sh && OTEL_SDK_DISABLED=True python src/manage.py migrate"]
|
# Runs migrations + setup_configuration (S-01-c): the JWT credential, the
|
||||||
|
# Autorisaties-API delegation, and the `zaken` kanaal that let OpenZaak publish.
|
||||||
|
# data.yaml is streamed into rr-nrc-config by infra/seed-config.sh (bind mounts
|
||||||
|
# don't reach sibling containers on the CI runner). See data.yaml + ADR-0007.
|
||||||
|
command: /setup_configuration.sh
|
||||||
|
volumes:
|
||||||
|
- nrc-config:/app/setup_configuration:ro
|
||||||
depends_on:
|
depends_on:
|
||||||
nrc-db:
|
nrc-db:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
@@ -185,6 +194,18 @@ services:
|
|||||||
condition: service_completed_successfully
|
condition: service_completed_successfully
|
||||||
networks: [cg]
|
networks: [cg]
|
||||||
|
|
||||||
|
# Celery beat drains the ScheduledNotification rows the API creates on publish
|
||||||
|
# and hands them to the worker. Without it, notifications are accepted but never
|
||||||
|
# delivered to subscribers — required, not optional. See ADR-0007.
|
||||||
|
nrc-beat:
|
||||||
|
image: docker.io/openzaak/open-notificaties:${OPENNOTIFICATIES_TAG:-1.16.1}
|
||||||
|
environment: *nrc-env
|
||||||
|
command: /celery_beat.sh
|
||||||
|
depends_on:
|
||||||
|
nrc-init:
|
||||||
|
condition: service_completed_successfully
|
||||||
|
networks: [cg]
|
||||||
|
|
||||||
# ── Keycloak (S-02) ──────────────────────────────────────────────────────
|
# ── Keycloak (S-02) ──────────────────────────────────────────────────────
|
||||||
keycloak:
|
keycloak:
|
||||||
image: quay.io/keycloak/keycloak:26.1
|
image: quay.io/keycloak/keycloak:26.1
|
||||||
@@ -309,6 +330,9 @@ volumes:
|
|||||||
oz-config:
|
oz-config:
|
||||||
external: true
|
external: true
|
||||||
name: rr-oz-config
|
name: rr-oz-config
|
||||||
|
nrc-config:
|
||||||
|
external: true
|
||||||
|
name: rr-nrc-config
|
||||||
kc-realms:
|
kc-realms:
|
||||||
external: true
|
external: true
|
||||||
name: rr-kc-realms
|
name: rr-kc-realms
|
||||||
|
|||||||
39
infra/notification-sink.py
Executable file
39
infra/notification-sink.py
Executable file
@@ -0,0 +1,39 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""A throwaway webhook sink for verifying the OpenZaak → NRC notification path.
|
||||||
|
|
||||||
|
NRC delivers abonnement callbacks here as POSTs; each body is printed to stdout
|
||||||
|
(prefixed `NOTIFICATION `) so the verify harness can assert on `docker logs`.
|
||||||
|
|
||||||
|
NRC refuses to register an abonnement whose callback is unauthenticated
|
||||||
|
(`no-auth-on-callback`): when validating it sends a probe and expects the callback
|
||||||
|
to reject a request without the configured `Authorization` value. So this sink
|
||||||
|
enforces that header (EXPECTED_AUTH env) — 401 without it, 204 with it.
|
||||||
|
|
||||||
|
Stdlib only. Listens on :9000. See infra/verify-notifications.sh / S-01-c (#56).
|
||||||
|
"""
|
||||||
|
import http.server
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
EXPECTED_AUTH = os.environ.get("EXPECTED_AUTH", "Bearer notification-sink-token")
|
||||||
|
|
||||||
|
|
||||||
|
class Handler(http.server.BaseHTTPRequestHandler):
|
||||||
|
def do_POST(self):
|
||||||
|
length = int(self.headers.get("content-length", 0))
|
||||||
|
body = self.rfile.read(length).decode("utf-8", "replace")
|
||||||
|
if self.headers.get("Authorization") != EXPECTED_AUTH:
|
||||||
|
self.send_response(401)
|
||||||
|
self.end_headers()
|
||||||
|
return
|
||||||
|
print("NOTIFICATION " + body, flush=True)
|
||||||
|
self.send_response(204)
|
||||||
|
self.end_headers()
|
||||||
|
|
||||||
|
def log_message(self, *args): # silence default request logging
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
http.server.HTTPServer(("0.0.0.0", 9000), Handler).serve_forever()
|
||||||
|
sys.exit(0)
|
||||||
@@ -52,11 +52,18 @@ services:
|
|||||||
OPENNOTIFICATIES_SUPERUSER_USERNAME: admin
|
OPENNOTIFICATIES_SUPERUSER_USERNAME: admin
|
||||||
DJANGO_SUPERUSER_PASSWORD: admin
|
DJANGO_SUPERUSER_PASSWORD: admin
|
||||||
OPENNOTIFICATIES_SUPERUSER_EMAIL: admin@localhost
|
OPENNOTIFICATIES_SUPERUSER_EMAIL: admin@localhost
|
||||||
# Migrations only for now. No setup_configuration steps are enabled yet (the
|
RUN_SETUP_CONFIG: "true"
|
||||||
# OZ<->NRC notification wiring lands in S-06), and NRC's `setup_configuration`
|
# Delivery cadence: nrc-beat fires `execute_notifications` this often to drain
|
||||||
# aborts with "No steps enabled" on the empty data.yaml — so we run `migrate`
|
# scheduled notifications to subscribers. Upstream default is 20s; 5s keeps the
|
||||||
# directly instead of /setup_configuration.sh. See data.yaml and ADR-0002.
|
# walking-skeleton + the verify smoke responsive.
|
||||||
command: ["sh", "-c", "/wait_for_db.sh && OTEL_SDK_DISABLED=True python src/manage.py migrate"]
|
NOTIFICATION_SEC_INTERVAL: "5"
|
||||||
|
# Runs migrations + setup_configuration (S-01-c): the JWT credential, the
|
||||||
|
# Autorisaties-API delegation, and the `zaken` kanaal that let OpenZaak publish
|
||||||
|
# notifications. data.yaml is streamed into this external volume by
|
||||||
|
# infra/seed-config.sh (same pattern as oz-init). See data.yaml + ADR-0006.
|
||||||
|
command: /setup_configuration.sh
|
||||||
|
volumes:
|
||||||
|
- nrc-config:/app/setup_configuration:ro
|
||||||
depends_on:
|
depends_on:
|
||||||
nrc-db:
|
nrc-db:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
@@ -89,8 +96,25 @@ services:
|
|||||||
condition: service_completed_successfully
|
condition: service_completed_successfully
|
||||||
networks: [cg]
|
networks: [cg]
|
||||||
|
|
||||||
|
# Celery beat: periodically fires `execute_notifications`, which drains the
|
||||||
|
# ScheduledNotification rows the API creates on publish and hands them to the
|
||||||
|
# worker for delivery. Without beat, notifications are accepted but never
|
||||||
|
# delivered to subscribers — so it is required, not optional. See ADR-0007.
|
||||||
|
nrc-beat:
|
||||||
|
image: docker.io/openzaak/open-notificaties:${OPENNOTIFICATIES_TAG:-1.16.1}
|
||||||
|
environment: *nrc-env
|
||||||
|
command: /celery_beat.sh
|
||||||
|
depends_on:
|
||||||
|
nrc-init:
|
||||||
|
condition: service_completed_successfully
|
||||||
|
networks: [cg]
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
nrc-db:
|
nrc-db:
|
||||||
|
# populated out-of-band by infra/seed-config.sh (docker cp) — see that script.
|
||||||
|
nrc-config:
|
||||||
|
external: true
|
||||||
|
name: rr-nrc-config
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
cg:
|
cg:
|
||||||
|
|||||||
@@ -1,5 +1,41 @@
|
|||||||
# Open Notificaties setup_configuration.
|
# Open Notificaties (NRC) setup_configuration (S-01-c, #56).
|
||||||
# Stage 1 (this commit): intentionally minimal — the init container runs
|
# Wires NRC so OpenZaak can publish notifications:
|
||||||
# migrations; no steps enabled yet. The OpenZaak<->NRC notification wiring
|
# - the JWT credential OpenZaak authenticates with,
|
||||||
# (Services, Authorization, JWT, Kanalen) is added next. See ADR-0002 / S-01-c.
|
# - delegation of authorization checks to OpenZaak's Autorisaties API (AC),
|
||||||
{}
|
# - the `zaken` kanaal OpenZaak publishes zaak events on.
|
||||||
|
# Dev-only credentials — not for production. Steps from nrc.setup_configuration.
|
||||||
|
|
||||||
|
# 1. JWT credential NRC uses to verify the token OpenZaak presents.
|
||||||
|
vng_api_common_credentials_config_enable: true
|
||||||
|
vng_api_common_credentials:
|
||||||
|
items:
|
||||||
|
- identifier: big-reference-seed
|
||||||
|
secret: insecure-dev-secret-change-me
|
||||||
|
|
||||||
|
# 2. The Autorisaties API (OpenZaak's AC) NRC consults to authorize publishers.
|
||||||
|
zgw_consumers_config_enable: true
|
||||||
|
zgw_consumers:
|
||||||
|
services:
|
||||||
|
- identifier: openzaak-ac
|
||||||
|
label: OpenZaak Autorisaties API
|
||||||
|
api_type: ac
|
||||||
|
api_root: http://openzaak:8000/autorisaties/api/v1/
|
||||||
|
auth_type: zgw
|
||||||
|
client_id: big-reference-seed
|
||||||
|
secret: insecure-dev-secret-change-me
|
||||||
|
|
||||||
|
# 3. Delegate authorization to that AC.
|
||||||
|
autorisaties_api_config_enable: true
|
||||||
|
autorisaties_api:
|
||||||
|
authorizations_api_service_identifier: openzaak-ac
|
||||||
|
|
||||||
|
# 4. The kanaal OpenZaak publishes zaak events on.
|
||||||
|
notifications_kanalen_config_enable: true
|
||||||
|
notifications_kanalen_config:
|
||||||
|
items:
|
||||||
|
- naam: zaken
|
||||||
|
documentatie_link: https://github.com/VNG-Realisatie/gemma-zaken
|
||||||
|
filters:
|
||||||
|
- bronorganisatie
|
||||||
|
- zaaktype
|
||||||
|
- vertrouwelijkheidaanduiding
|
||||||
|
|||||||
@@ -47,9 +47,12 @@ services:
|
|||||||
CELERY_BROKER_URL: redis://oz-redis:6379/1
|
CELERY_BROKER_URL: redis://oz-redis:6379/1
|
||||||
CELERY_RESULT_BACKEND: redis://oz-redis:6379/1
|
CELERY_RESULT_BACKEND: redis://oz-redis:6379/1
|
||||||
DISABLE_2FA: "true"
|
DISABLE_2FA: "true"
|
||||||
# Notifications go to Open Notificaties (NRC), which arrives in S-01-c.
|
# Notifications are OFF by default so OpenZaak-only bring-ups (openzaak-up,
|
||||||
# Until then, disable outbound notifications so writes don't 500.
|
# the ACL integration test) don't 500 trying to reach an absent NRC. When
|
||||||
NOTIFICATIONS_DISABLED: "true"
|
# OpenZaak runs together with the NRC stack, set OZ_NOTIFICATIONS_DISABLED=false
|
||||||
|
# (make stack-up does) to publish; the NRC service + notifications_config that
|
||||||
|
# name it are provisioned by setup_configuration (data.yaml, S-01-c).
|
||||||
|
NOTIFICATIONS_DISABLED: "${OZ_NOTIFICATIONS_DISABLED:-true}"
|
||||||
OPENZAAK_SUPERUSER_USERNAME: admin
|
OPENZAAK_SUPERUSER_USERNAME: admin
|
||||||
DJANGO_SUPERUSER_PASSWORD: admin
|
DJANGO_SUPERUSER_PASSWORD: admin
|
||||||
OPENZAAK_SUPERUSER_EMAIL: admin@localhost
|
OPENZAAK_SUPERUSER_EMAIL: admin@localhost
|
||||||
|
|||||||
@@ -20,3 +20,22 @@ vng_api_common_applicaties:
|
|||||||
- big-reference-seed
|
- big-reference-seed
|
||||||
label: BIG reference seed client
|
label: BIG reference seed client
|
||||||
heeft_alle_autorisaties: true
|
heeft_alle_autorisaties: true
|
||||||
|
|
||||||
|
# ── OpenZaak → Open Notificaties (NRC) publishing (S-01-c, #56) ─────────────
|
||||||
|
# The NRC service OpenZaak posts notifications to, authenticating with the same
|
||||||
|
# big-reference-seed client (NRC verifies the JWT and authorizes it via the AC).
|
||||||
|
zgw_consumers_config_enable: true
|
||||||
|
zgw_consumers:
|
||||||
|
services:
|
||||||
|
- identifier: nrc
|
||||||
|
label: Open Notificaties
|
||||||
|
api_type: nrc
|
||||||
|
api_root: http://nrc-web:8000/api/v1/
|
||||||
|
auth_type: zgw
|
||||||
|
client_id: big-reference-seed
|
||||||
|
secret: insecure-dev-secret-change-me
|
||||||
|
|
||||||
|
# Point OpenZaak's notifications at that service. Requires NOTIFICATIONS_DISABLED=false.
|
||||||
|
notifications_config_enable: true
|
||||||
|
notifications_config:
|
||||||
|
notifications_api_service_identifier: nrc
|
||||||
|
|||||||
@@ -33,11 +33,12 @@ populate() { # volume source(file or dir/.)
|
|||||||
echo " seeded $vol"
|
echo " seeded $vol"
|
||||||
}
|
}
|
||||||
|
|
||||||
[ "$#" -gt 0 ] || { echo "usage: seed-config.sh <oz|kc|fl> ..." >&2; exit 2; }
|
[ "$#" -gt 0 ] || { echo "usage: seed-config.sh <oz|nrc|kc|fl> ..." >&2; exit 2; }
|
||||||
|
|
||||||
for key in "$@"; do
|
for key in "$@"; do
|
||||||
case "$key" in
|
case "$key" in
|
||||||
oz) populate rr-oz-config "$here/openzaak/setup_configuration/." ;;
|
oz) populate rr-oz-config "$here/openzaak/setup_configuration/." ;;
|
||||||
|
nrc) populate rr-nrc-config "$here/opennotificaties/setup_configuration/." ;;
|
||||||
kc) populate rr-kc-realms "$here/keycloak/realms/." ;;
|
kc) populate rr-kc-realms "$here/keycloak/realms/." ;;
|
||||||
fl) populate rr-fl-bpmn "$here/../workflows/registratie.bpmn" ;;
|
fl) populate rr-fl-bpmn "$here/../workflows/registratie.bpmn" ;;
|
||||||
*) echo "unknown seed key: $key" >&2; exit 2 ;;
|
*) echo "unknown seed key: $key" >&2; exit 2 ;;
|
||||||
|
|||||||
90
infra/verify-notification-driver.py
Executable file
90
infra/verify-notification-driver.py
Executable file
@@ -0,0 +1,90 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Drive the OpenZaak → NRC notification check from *inside* the compose network.
|
||||||
|
|
||||||
|
Registers an abonnement on the `zaken` kanaal pointing at a webhook sink, then
|
||||||
|
creates a zaak against the published BIG zaaktype. OpenZaak publishes a
|
||||||
|
`zaken`/`create` notification; NRC delivers it to the sink. The host harness
|
||||||
|
(infra/verify-notifications.sh) then asserts the sink received it.
|
||||||
|
|
||||||
|
Reached by container IP, not service name: OpenZaak/NRC validate URLs with Django's
|
||||||
|
URLValidator, which rejects a single-label host like `openzaak`. Stdlib only.
|
||||||
|
Env: OZ_BASE, NRC_BASE, SINK_CALLBACK, SINK_AUTH, OZ_CLIENT_ID, OZ_SECRET.
|
||||||
|
"""
|
||||||
|
import base64
|
||||||
|
import hashlib
|
||||||
|
import hmac
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import urllib.error
|
||||||
|
import urllib.request
|
||||||
|
|
||||||
|
OZ = os.environ["OZ_BASE"].rstrip("/")
|
||||||
|
NRC = os.environ["NRC_BASE"].rstrip("/")
|
||||||
|
SINK_CALLBACK = os.environ["SINK_CALLBACK"]
|
||||||
|
SINK_AUTH = os.environ.get("SINK_AUTH", "Bearer notification-sink-token")
|
||||||
|
CID = os.environ.get("OZ_CLIENT_ID", "big-reference-seed")
|
||||||
|
SECRET = os.environ.get("OZ_SECRET", "insecure-dev-secret-change-me")
|
||||||
|
RSIN = "517439943"
|
||||||
|
|
||||||
|
|
||||||
|
def token():
|
||||||
|
b64 = lambda b: base64.urlsafe_b64encode(b).rstrip(b"=")
|
||||||
|
seg = (
|
||||||
|
b64(json.dumps({"alg": "HS256", "typ": "JWT"}, separators=(",", ":")).encode())
|
||||||
|
+ b"."
|
||||||
|
+ b64(json.dumps(
|
||||||
|
{"iss": CID, "iat": int(time.time()), "client_id": CID,
|
||||||
|
"user_id": "verify", "user_representation": "verify"},
|
||||||
|
separators=(",", ":")).encode())
|
||||||
|
)
|
||||||
|
return (seg + b"." + b64(hmac.new(SECRET.encode(), seg, hashlib.sha256).digest())).decode()
|
||||||
|
|
||||||
|
|
||||||
|
def call(method, url, body=None, crs=False):
|
||||||
|
headers = {"Authorization": "Bearer " + token(),
|
||||||
|
"Content-Type": "application/json", "Accept": "application/json"}
|
||||||
|
if crs:
|
||||||
|
headers["Accept-Crs"] = "EPSG:4326"
|
||||||
|
headers["Content-Crs"] = "EPSG:4326"
|
||||||
|
data = json.dumps(body).encode() if body is not None else None
|
||||||
|
try:
|
||||||
|
with urllib.request.urlopen(
|
||||||
|
urllib.request.Request(url, data=data, method=method, headers=headers), timeout=30
|
||||||
|
) as r:
|
||||||
|
return r.status, json.loads(r.read() or "null")
|
||||||
|
except urllib.error.HTTPError as e:
|
||||||
|
return e.code, json.loads(e.read() or "null")
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
status, ab = call("POST", f"{NRC}/api/v1/abonnement", {
|
||||||
|
"callbackUrl": SINK_CALLBACK,
|
||||||
|
"auth": SINK_AUTH,
|
||||||
|
"kanalen": [{"naam": "zaken", "filters": {}}],
|
||||||
|
})
|
||||||
|
if status != 201:
|
||||||
|
sys.exit(f"create abonnement -> {status}: {json.dumps(ab)}")
|
||||||
|
print(f"abonnement: {ab['url']}")
|
||||||
|
|
||||||
|
status, body = call(
|
||||||
|
"GET", f"{OZ}/catalogi/api/v1/zaaktypen?identificatie=BIG-REGISTRATIE&status=definitief")
|
||||||
|
results = body.get("results", []) if status == 200 else []
|
||||||
|
if not results:
|
||||||
|
sys.exit("no published BIG-REGISTRATIE zaaktype — seed with OZ_PUBLISH=1 first")
|
||||||
|
zaaktype = results[0]["url"]
|
||||||
|
|
||||||
|
status, zaak = call("POST", f"{OZ}/zaken/api/v1/zaken", {
|
||||||
|
"bronorganisatie": RSIN, "verantwoordelijkeOrganisatie": RSIN,
|
||||||
|
"zaaktype": zaaktype, "startdatum": time.strftime("%Y-%m-%d"),
|
||||||
|
"vertrouwelijkheidaanduiding": "openbaar",
|
||||||
|
}, crs=True)
|
||||||
|
if status != 201:
|
||||||
|
sys.exit(f"create zaak -> {status}: {json.dumps(zaak)}")
|
||||||
|
# The harness greps the sink for this exact URL.
|
||||||
|
print(f"ZAAK_CREATED {zaak['url']}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
93
infra/verify-notifications.sh
Executable file
93
infra/verify-notifications.sh
Executable file
@@ -0,0 +1,93 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# Verify the OpenZaak → Open Notificaties (NRC) notification path end to end (S-01-c,
|
||||||
|
# #56): a zaak created in OpenZaak is published to NRC and delivered to a subscriber.
|
||||||
|
#
|
||||||
|
# Everything that talks to OpenZaak/NRC runs *inside* the compose network and reaches
|
||||||
|
# them by container IP — the hosted CI runner can't reach published ports (sibling
|
||||||
|
# containers, gitea-actions-gotchas.md §5) and OpenZaak/NRC reject a single-label host
|
||||||
|
# in URLs (Django URLValidator). Plain docker primitives only (docker/podman-portable),
|
||||||
|
# like infra/seed-config.sh and infra/run-integration.sh. See ADR-0007.
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
OZ_COMPOSE="$here/openzaak/docker-compose.yml"
|
||||||
|
NRC_COMPOSE="$here/opennotificaties/docker-compose.yml"
|
||||||
|
SINK_AUTH="Bearer notification-sink-token"
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
docker rm -f rr-nsink rr-nverify >/dev/null 2>&1 || true
|
||||||
|
docker compose -f "$OZ_COMPOSE" -f "$NRC_COMPOSE" down --volumes >/dev/null 2>&1 || true
|
||||||
|
docker volume rm -f rr-oz-config rr-nrc-config >/dev/null 2>&1 || true
|
||||||
|
}
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
ip() { docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' "$1"; }
|
||||||
|
|
||||||
|
wait_healthy() { # name-regex -> echoes container id
|
||||||
|
local re="$1" cid status
|
||||||
|
for _ in $(seq 1 140); do
|
||||||
|
cid="$(docker ps -q --filter "name=$re" | head -1)"
|
||||||
|
if [ -n "$cid" ]; then
|
||||||
|
status="$(docker inspect -f '{{if .State.Health}}{{.State.Health.Status}}{{end}}' "$cid" 2>/dev/null || true)"
|
||||||
|
[ "$status" = healthy ] && { echo "$cid"; return 0; }
|
||||||
|
fi
|
||||||
|
sleep 3
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
echo ">> bringing up OpenZaak + Open Notificaties (notifications enabled)"
|
||||||
|
bash "$here/seed-config.sh" oz nrc
|
||||||
|
OZ_NOTIFICATIONS_DISABLED=false docker compose -f "$OZ_COMPOSE" -f "$NRC_COMPOSE" up -d
|
||||||
|
|
||||||
|
echo ">> waiting for OpenZaak + NRC to be healthy"
|
||||||
|
oz="$(wait_healthy 'openzaak[-_]openzaak')" || { echo "ERROR: OpenZaak not healthy" >&2; exit 1; }
|
||||||
|
nrc="$(wait_healthy 'nrc-web')" || { echo "ERROR: NRC not healthy" >&2; exit 1; }
|
||||||
|
net="$(docker inspect -f '{{range $k,$_ := .NetworkSettings.Networks}}{{$k}}{{"\n"}}{{end}}' "$oz" | head -1)"
|
||||||
|
oz_ip="$(ip "$oz")"; nrc_ip="$(ip "$nrc")"
|
||||||
|
echo ">> network=$net openzaak=$oz_ip nrc=$nrc_ip"
|
||||||
|
|
||||||
|
echo ">> seeding a published BIG zaaktype"
|
||||||
|
sid="$(docker create --network "$net" -e "OZ_BASE=http://$oz_ip:8000" -e OZ_PUBLISH=1 \
|
||||||
|
python:3-slim python /seed.py)"
|
||||||
|
docker cp "$here/openzaak/seed_catalogus.py" "$sid:/seed.py" >/dev/null
|
||||||
|
docker start -a "$sid"
|
||||||
|
docker rm -f "$sid" >/dev/null
|
||||||
|
|
||||||
|
echo ">> starting the webhook sink"
|
||||||
|
docker rm -f rr-nsink >/dev/null 2>&1 || true
|
||||||
|
sink="$(docker create --network "$net" --name rr-nsink -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-nsink)"
|
||||||
|
echo ">> sink at $sink_ip:9000"
|
||||||
|
|
||||||
|
echo ">> registering abonnement + creating a zaak"
|
||||||
|
docker rm -f rr-nverify >/dev/null 2>&1 || true
|
||||||
|
drv="$(docker create --network "$net" --name rr-nverify \
|
||||||
|
-e "OZ_BASE=http://$oz_ip:8000" -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/verify-notification-driver.py" "$drv:/driver.py" >/dev/null
|
||||||
|
docker start -a "$drv"
|
||||||
|
zaak_url="$(docker logs rr-nverify 2>/dev/null | sed -n 's/^ZAAK_CREATED //p' | head -1)"
|
||||||
|
docker rm -f rr-nverify >/dev/null
|
||||||
|
[ -n "$zaak_url" ] || { echo "ERROR: driver did not create a zaak" >&2; exit 1; }
|
||||||
|
zaak_uuid="${zaak_url##*/}"
|
||||||
|
echo ">> zaak created: $zaak_url"
|
||||||
|
|
||||||
|
echo ">> waiting for the notification to reach the sink"
|
||||||
|
for _ in $(seq 1 30); do
|
||||||
|
if docker logs rr-nsink 2>&1 | grep -q "$zaak_uuid"; then
|
||||||
|
echo "OK — NRC delivered the zaken notification for zaak $zaak_uuid to the sink"
|
||||||
|
docker logs rr-nsink 2>&1 | grep "$zaak_uuid" | tail -1 | cut -c1-300
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
sleep 2
|
||||||
|
done
|
||||||
|
echo "FAIL — the sink never received a notification for zaak $zaak_uuid" >&2
|
||||||
|
echo "--- sink log ---" >&2; docker logs rr-nsink 2>&1 | tail -8 >&2
|
||||||
|
exit 1
|
||||||
@@ -28,6 +28,7 @@ nav:
|
|||||||
- "ADR-0004: BDD framework": architecture/adr-0004-bdd-framework.md
|
- "ADR-0004: BDD framework": architecture/adr-0004-bdd-framework.md
|
||||||
- "ADR-0005: Mutation testing": architecture/adr-0005-mutation-testing.md
|
- "ADR-0005: Mutation testing": architecture/adr-0005-mutation-testing.md
|
||||||
- "ADR-0006: ACL integration test provisioning": architecture/adr-0006-integration-test-provisioning.md
|
- "ADR-0006: ACL integration test provisioning": architecture/adr-0006-integration-test-provisioning.md
|
||||||
|
- "ADR-0007: OpenZaak → NRC notification wiring": architecture/adr-0007-notification-wiring.md
|
||||||
- Working in Gitea: gitea-workflow.md
|
- Working in Gitea: gitea-workflow.md
|
||||||
- Runbooks:
|
- Runbooks:
|
||||||
- CI: runbooks/ci.md
|
- CI: runbooks/ci.md
|
||||||
|
|||||||
Reference in New Issue
Block a user