diff --git a/.gitea/workflows/ci.yaml b/.gitea/workflows/ci.yaml index 9e3c95b..16901c6 100644 --- a/.gitea/workflows/ci.yaml +++ b/.gitea/workflows/ci.yaml @@ -94,10 +94,12 @@ jobs: run: make verify-nrc - name: OpenZaak → NRC → Event Subscriber → projection-api run: make verify-projection + - name: Domain → Flowable → ACL → OpenZaak + run: make verify-domain # Log dump must precede teardown (which removes the containers). - name: Dump container logs on failure if: failure() - run: docker compose -f infra/docker-compose.yml logs --no-color --tail=100 oz-init openzaak nrc-init nrc-web nrc-celery nrc-beat flowable-init keycloak acl bff projection-db event-subscriber projection-api 2>&1 || true + run: docker compose -f infra/docker-compose.yml logs --no-color --tail=100 oz-init openzaak nrc-init nrc-web nrc-celery nrc-beat flowable-db flowable-rest flowable-init keycloak acl bff domain projection-db event-subscriber projection-api 2>&1 || true - name: Tear down if: always() run: make down diff --git a/Makefile b/Makefile index 0f65935..b6fba46 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ COMPOSE := infra/docker-compose.yml # Long-running services with a healthcheck — the smoke polls these for readiness # (infra/wait-healthy.sh). One-shot init jobs (oz-init, nrc-init, flowable-init) # are not polled; they only need to have run. See docs/runbooks/gitea-actions-gotchas.md. -WAIT_SVCS := openzaak nrc-web acl bff event-subscriber projection-api +WAIT_SVCS := openzaak nrc-web acl bff domain event-subscriber projection-api # Config files (OpenZaak data.yaml, Keycloak realms, Flowable BPMN) are streamed # into external named volumes via `docker cp` (infra/seed-config.sh) instead of # bind-mounted, because bind mounts don't reach sibling containers on the @@ -138,6 +138,11 @@ verify-nrc: verify-projection: bash infra/run-projection-check.sh +## verify-domain: domain → Flowable → ACL → OpenZaak end-to-end (S-05), against the +## already-running stack. Recreates the acl service to inject the seeded zaaktype URL. +verify-domain: + bash infra/run-domain-check.sh + ## verify: local mirror of the CI verify-stack job — full stack up once, all checks, ## tear down (always). For fast single-concern local iteration use `integration` ## (oz-only) or `verify-notifications` (oz+nrc) instead. @@ -148,7 +153,8 @@ verify: WAIT_TIMEOUT=420 bash infra/wait-healthy.sh $(WAIT_SVCS) \ && bash infra/run-acl-integration.sh \ && bash infra/run-notification-check.sh \ - && bash infra/run-projection-check.sh || rc=$$?; \ + && bash infra/run-projection-check.sh \ + && bash infra/run-domain-check.sh || rc=$$?; \ docker compose -f $(COMPOSE) down --volumes >/dev/null 2>&1; \ docker volume rm -f $(CFG_VOLS) >/dev/null 2>&1; \ exit $$rc' diff --git a/infra/docker-compose.yml b/infra/docker-compose.yml index ecbe7bd..02a4e90 100644 --- a/infra/docker-compose.yml +++ b/infra/docker-compose.yml @@ -285,7 +285,9 @@ services: dockerfile: Dockerfile image: register-referentie/acl:dev environment: - Acl__OpenZaak__BaseUrl: http://openzaak:8000/ + # Overridable so verify-domain can point the ACL at the same OpenZaak host that + # owns the seeded zaaktype URL (host-consistent zaak creation, ADR-0009). + Acl__OpenZaak__BaseUrl: ${ACL_OPENZAAK_BASEURL:-http://openzaak:8000/} Acl__OpenZaak__ClientId: big-reference-seed Acl__OpenZaak__Secret: insecure-dev-secret-change-me Acl__Defaults__Bronorganisatie: "517439943" @@ -306,6 +308,36 @@ services: condition: service_healthy networks: [cg] + # ── BIG Domain Service (S-05) ────────────────────────────────────────────── + # Orchestrates a registration: POST /registrations creates the aggregate and + # starts the registratie Flowable process; a hosted worker acquires the + # OpenZaakAanmaken job, opens a zaak via the ACL and completes it (ADR-0009). + # Talks only to Flowable (Workflow Client, §8.2) and the ACL (§8.1). + domain: + build: + context: ../services/domain + dockerfile: Dockerfile + image: register-referentie/domain:dev + environment: + Flowable__BaseUrl: http://flowable-rest:8080/flowable-rest/ + Flowable__Username: rest-admin + Flowable__Password: test + Acl__BaseUrl: http://acl:8080/ + ports: + - "8130:8080" + healthcheck: + test: ["CMD", "curl", "-fsS", "http://localhost:8080/health"] + interval: 5s + timeout: 3s + retries: 5 + start_period: 10s + depends_on: + acl: + condition: service_healthy + flowable-init: + condition: service_completed_successfully + networks: [cg] + # ── BFF ────────────────────────────────────────────────────────────────── bff: build: diff --git a/infra/openzaak/seed_catalogus.py b/infra/openzaak/seed_catalogus.py index a9aa4c6..5dc15a8 100644 --- a/infra/openzaak/seed_catalogus.py +++ b/infra/openzaak/seed_catalogus.py @@ -210,6 +210,10 @@ def main(): print(f"zaaktypen in BIG: {names}") assert "BIG-REGISTRATIE" in names, "BIG-REGISTRATIE not listed" state = "published" if PUBLISH else "concept" + # Machine-readable line so callers (e.g. infra/run-domain-check.sh) can capture the + # zaaktype URL to configure the ACL's default-fill (ADR-0003/0009). + zt_url = next(z["url"] for z in zaaktypen if z.get("identificatie") == "BIG-REGISTRATIE") + print(f"ZAAKTYPE_URL {zt_url}") print(f"OK — BIG catalogus seeded (BIG-REGISTRATIE {state} + bsn eigenschap)") diff --git a/infra/run-domain-check.sh b/infra/run-domain-check.sh new file mode 100755 index 0000000..33d7d9e --- /dev/null +++ b/infra/run-domain-check.sh @@ -0,0 +1,68 @@ +#!/usr/bin/env bash +# +# Verify the BIG Domain Service end-to-end (S-05) against an ALREADY-RUNNING full stack: +# domain → Flowable (start the registratie process + external-task worker) → ACL → OpenZaak. +# Submits a registration to the domain and asserts the worker opens a zaak in OpenZaak and +# records its URL on the aggregate (ADR-0009). +# +# The seeded zaaktype URL is server-assigned, so it isn't knowable at initial bring-up. This +# script therefore seeds a published BIG zaaktype and recreates the `acl` service configured to +# default-fill it — pointing the ACL at the SAME OpenZaak host that owns the URL, so zaak creation +# is host-consistent (exactly the configuration the ACL integration test proves, ADR-0006). That +# one recreate aside, the caller owns stack bring-up + teardown. +# +# All in-network, reaching services by container IP (a single-label host isn't URL-valid; the +# runner can't reach published ports — gitea-actions-gotchas.md §5/§6). Plain docker primitives. +set -euo pipefail + +here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +root="$(cd "$here/.." && pwd)" +compose="$root/infra/docker-compose.yml" + +ip() { docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' "$1"; } + +oz="$(docker ps -q --filter 'name=[-_]openzaak[-_]' | head -1)" +dom="$(docker ps -q --filter 'name=domain' | head -1)" +[ -n "$oz" ] || { echo "ERROR: no running OpenZaak container — bring the stack up first" >&2; exit 1; } +[ -n "$dom" ] || { echo "ERROR: no running domain container — bring the stack up first" >&2; exit 1; } +net="$(docker inspect -f '{{range $k,$_ := .NetworkSettings.Networks}}{{$k}}{{"\n"}}{{end}}' "$oz" | head -1)" +oz_ip="$(ip "$oz")"; dom_ip="$(ip "$dom")" +oz_base="http://$oz_ip:8000" +echo ">> openzaak=$oz_ip domain=$dom_ip network=$net" + +echo ">> seeding a published BIG zaaktype (idempotent) and capturing its URL" +sid="$(docker create --network "$net" -e "OZ_BASE=$oz_base" -e OZ_PUBLISH=1 python:3-slim python /seed.py)" +docker cp "$here/openzaak/seed_catalogus.py" "$sid:/seed.py" >/dev/null +zt_url="$(docker start -a "$sid" | sed -n 's/^ZAAKTYPE_URL //p' | head -1)" +docker rm -f "$sid" >/dev/null +[ -n "$zt_url" ] || { echo "ERROR: seed did not report a ZAAKTYPE_URL" >&2; exit 1; } +echo ">> zaaktype: $zt_url" + +echo ">> recreating the acl service pointed at the seeded zaaktype (host-consistent)" +ACL_ZAAKTYPE_URL="$zt_url" ACL_OPENZAAK_BASEURL="$oz_base/" docker compose -f "$compose" up -d acl +WAIT_TIMEOUT="${WAIT_TIMEOUT:-120}" bash "$here/wait-healthy.sh" acl + +echo ">> submitting a registration to the domain" +loc="$(docker run --rm --network "$net" curlimages/curl:latest \ + -fsS -D - -o /dev/null -X POST "http://$dom_ip:8080/registrations" \ + -H 'Content-Type: application/json' -d '{"bsn":"123456782"}' \ + | sed -n 's/\r$//; s/^[Ll]ocation: //p' | head -1)" +[ -n "$loc" ] || { echo "ERROR: POST /registrations returned no Location" >&2; exit 1; } +echo ">> registration accepted at $loc" + +echo ">> polling the domain until the worker records the opened zaak" +for _ in $(seq 1 30); do + body="$(docker run --rm --network "$net" curlimages/curl:latest \ + -fsS "http://$dom_ip:8080$loc" 2>/dev/null || true)" + if echo "$body" | grep -q '/zaken/api/v1/zaken/'; then + echo "OK — the domain opened a zaak and recorded it on the registration:" + echo "$body" | cut -c1-300 + exit 0 + fi + sleep 2 +done +echo "FAIL — the registration never received a zaak URL" >&2 +echo "--- domain log ---" >&2; docker logs "$dom" 2>&1 | tail -15 >&2 +acl="$(docker ps -q --filter 'name=[-_]acl[-_]' | head -1)" +[ -n "$acl" ] && { echo "--- acl log ---" >&2; docker logs "$acl" 2>&1 | tail -15 >&2; } +exit 1 diff --git a/services/domain/.dockerignore b/services/domain/.dockerignore new file mode 100644 index 0000000..173dc20 --- /dev/null +++ b/services/domain/.dockerignore @@ -0,0 +1,4 @@ +**/bin +**/obj +**/*.user +StrykerOutput diff --git a/services/domain/Dockerfile b/services/domain/Dockerfile new file mode 100644 index 0000000..600799b --- /dev/null +++ b/services/domain/Dockerfile @@ -0,0 +1,34 @@ +# Multi-stage build for the BIG Domain Service (.NET 10). +# Build context is services/domain (see infra/docker-compose.yml). +FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build +WORKDIR /src + +# Restore first (cached unless .csproj files change). +COPY Big.Api/Big.Api.csproj Big.Api/ +COPY Big.Application/Big.Application.csproj Big.Application/ +COPY Big.Infrastructure/Big.Infrastructure.csproj Big.Infrastructure/ +COPY Big.Domain/Big.Domain.csproj Big.Domain/ +RUN dotnet restore Big.Api/Big.Api.csproj + +COPY Big.Api/ Big.Api/ +COPY Big.Application/ Big.Application/ +COPY Big.Infrastructure/ Big.Infrastructure/ +COPY Big.Domain/ Big.Domain/ +RUN dotnet publish Big.Api/Big.Api.csproj -c Release -o /app/publish /p:UseAppHost=false + +FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS runtime +WORKDIR /app + +RUN apt-get update \ + && apt-get install -y --no-install-recommends curl \ + && rm -rf /var/lib/apt/lists/* + +COPY --from=build /app/publish . + +ENV ASPNETCORE_URLS=http://+:8080 +EXPOSE 8080 + +HEALTHCHECK --interval=5s --timeout=3s --start-period=10s --retries=5 \ + CMD curl -fsS http://localhost:8080/health || exit 1 + +ENTRYPOINT ["dotnet", "Big.Api.dll"]