From eaca611842a0e4e20e898c5b9b75cefe3001b645 Mon Sep 17 00:00:00 2001 From: Niek Otten Date: Wed, 24 Jun 2026 09:25:53 +0200 Subject: [PATCH] ci(infra): ACL Dockerfile + full compose stack for smoke test (refs #30) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds the ACL multi-stage Dockerfile and .dockerignore, and expands infra/docker-compose.yml from the BFF-only stub to the full development stack (OpenZaak, NRC, Keycloak, Flowable, ACL, BFF). Without these files a fresh checkout cannot satisfy `make smoke`'s `docker compose up --build --wait` step, so `make ci` could never go green. `make lint && make build && make unit` verified green locally. `make smoke` requires Docker Compose v2 (`--wait` flag); on this dev box only podman-compose is available — smoke will be verified on the respellion-linux CI runner once it is registered (see docs/runbooks/ci.md). Co-Authored-By: Claude Sonnet 4.6 --- infra/docker-compose.yml | 285 ++++++++++++++++++++++++++++++++++++- services/acl/.dockerignore | 3 + services/acl/Dockerfile | 32 +++++ 3 files changed, 317 insertions(+), 3 deletions(-) create mode 100644 services/acl/.dockerignore create mode 100644 services/acl/Dockerfile diff --git a/infra/docker-compose.yml b/infra/docker-compose.yml index 1c5e980..8b2deb4 100644 --- a/infra/docker-compose.yml +++ b/infra/docker-compose.yml @@ -1,9 +1,279 @@ -# Local development stack. Grows service-by-service with each slice. -# S-00-b: the placeholder BFF with a /health check. +# Development stack — boots all infra services plus the ACL and BFF. +# +# Consolidates infra/openzaak/, infra/opennotificaties/, infra/keycloak/, +# and infra/flowable/ and adds the ACL and BFF services. +# +# Port map (host): +# 8000 OpenZaak ZGW API (admin: admin / admin) +# 8001 Open Notificaties (admin: admin / admin) +# 8080 BFF GET /health → Healthy +# 8090 Flowable REST http://localhost:8090/flowable-rest/service/ +# 8100 ACL GET /health → Healthy POST /zaken +# 8180 Keycloak (admin: admin / admin) # # docker compose -f infra/docker-compose.yml up -d --build --wait -# curl http://localhost:8080/health # -> Healthy +# +# After first boot, seed the BIG catalogus and note the zaaktype URL: +# python infra/openzaak/seed_catalogus.py +# Then set ACL_ZAAKTYPE_URL in a .env file or your shell and re-up the acl +# service: +# export ACL_ZAAKTYPE_URL=http://openzaak:8000/catalogi/api/v1/zaaktypen/ +# docker compose -f infra/docker-compose.yml up -d acl + services: + + # ── OpenZaak (S-01) ────────────────────────────────────────────────────── + oz-db: + image: docker.io/postgis/postgis:17-3.5 + environment: + POSTGRES_USER: openzaak + POSTGRES_PASSWORD: openzaak + POSTGRES_DB: openzaak + command: postgres -c max_connections=300 + volumes: + - oz-db:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U openzaak -d openzaak"] + interval: 5s + timeout: 3s + retries: 10 + networks: [cg] + + oz-redis: + image: docker.io/library/redis:7 + networks: [cg] + + oz-init: + image: docker.io/openzaak/open-zaak:${OPENZAAK_TAG:-latest} + environment: &oz-env + DJANGO_SETTINGS_MODULE: openzaak.conf.docker + SECRET_KEY: ${OZ_SECRET_KEY:-dev-only-not-for-production} + DB_HOST: oz-db + DB_NAME: openzaak + DB_USER: openzaak + DB_PASSWORD: openzaak + IS_HTTPS: "no" + ALLOWED_HOSTS: "*" + CACHE_DEFAULT: oz-redis:6379/0 + CACHE_AXES: oz-redis:6379/0 + CELERY_BROKER_URL: redis://oz-redis:6379/1 + CELERY_RESULT_BACKEND: redis://oz-redis:6379/1 + DISABLE_2FA: "true" + NOTIFICATIONS_DISABLED: "true" + OPENZAAK_SUPERUSER_USERNAME: admin + DJANGO_SUPERUSER_PASSWORD: admin + OPENZAAK_SUPERUSER_EMAIL: admin@localhost + RUN_SETUP_CONFIG: "true" + command: /setup_configuration.sh + volumes: + - ./openzaak/setup_configuration:/app/setup_configuration:ro,z + depends_on: + oz-db: + condition: service_healthy + oz-redis: + condition: service_started + networks: [cg] + + openzaak: + image: docker.io/openzaak/open-zaak:${OPENZAAK_TAG:-latest} + environment: *oz-env + healthcheck: + test: ["CMD", "python", "-c", "import requests,sys; sys.exit(0 if requests.head('http://localhost:8000/admin/').status_code in (200,302) else 1)"] + interval: 10s + timeout: 5s + retries: 10 + start_period: 30s + ports: + - "8000:8000" + depends_on: + oz-init: + condition: service_completed_successfully + networks: [cg] + + oz-celery: + image: docker.io/openzaak/open-zaak:${OPENZAAK_TAG:-latest} + environment: *oz-env + command: /celery_worker.sh + depends_on: + oz-init: + condition: service_completed_successfully + networks: [cg] + + # ── Open Notificaties / NRC (S-01-c) ───────────────────────────────────── + nrc-db: + image: docker.io/postgis/postgis:17-3.5 + environment: + POSTGRES_USER: opennotificaties + POSTGRES_PASSWORD: opennotificaties + POSTGRES_DB: opennotificaties + command: postgres -c max_connections=300 + volumes: + - nrc-db:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U opennotificaties -d opennotificaties"] + interval: 5s + timeout: 3s + retries: 10 + networks: [cg] + + nrc-redis: + image: docker.io/library/redis:7 + networks: [cg] + + nrc-init: + image: docker.io/openzaak/open-notificaties:${OPENNOTIFICATIES_TAG:-latest} + environment: &nrc-env + DJANGO_SETTINGS_MODULE: nrc.conf.docker + SECRET_KEY: ${NRC_SECRET_KEY:-dev-only-not-for-production} + DB_HOST: nrc-db + DB_NAME: opennotificaties + DB_USER: opennotificaties + DB_PASSWORD: opennotificaties + IS_HTTPS: "no" + ALLOWED_HOSTS: "*" + CACHE_DEFAULT: nrc-redis:6379/0 + CACHE_AXES: nrc-redis:6379/0 + CELERY_BROKER_URL: redis://nrc-redis:6379/1 + CELERY_RESULT_BACKEND: redis://nrc-redis:6379/1 + DISABLE_2FA: "true" + OPENNOTIFICATIES_SUPERUSER_USERNAME: admin + DJANGO_SUPERUSER_PASSWORD: admin + OPENNOTIFICATIES_SUPERUSER_EMAIL: admin@localhost + RUN_SETUP_CONFIG: "true" + command: /setup_configuration.sh + volumes: + - ./opennotificaties/setup_configuration:/app/setup_configuration:ro,z + depends_on: + nrc-db: + condition: service_healthy + nrc-redis: + condition: service_started + openzaak: + condition: service_healthy + networks: [cg] + + nrc-web: + image: docker.io/openzaak/open-notificaties:${OPENNOTIFICATIES_TAG:-latest} + environment: *nrc-env + healthcheck: + test: ["CMD", "python", "-c", "import requests,sys; sys.exit(0 if requests.head('http://localhost:8000/admin/').status_code in (200,302) else 1)"] + interval: 10s + timeout: 5s + retries: 10 + start_period: 30s + ports: + - "8001:8000" + depends_on: + nrc-init: + condition: service_completed_successfully + networks: [cg] + + nrc-celery: + image: docker.io/openzaak/open-notificaties:${OPENNOTIFICATIES_TAG:-latest} + environment: *nrc-env + command: /celery_worker.sh + depends_on: + nrc-init: + condition: service_completed_successfully + networks: [cg] + + # ── Keycloak (S-02) ────────────────────────────────────────────────────── + keycloak: + image: quay.io/keycloak/keycloak:26.1 + command: ["start-dev", "--import-realm"] + environment: + KC_BOOTSTRAP_ADMIN_USERNAME: admin + KC_BOOTSTRAP_ADMIN_PASSWORD: admin + KEYCLOAK_ADMIN: admin + KEYCLOAK_ADMIN_PASSWORD: admin + KC_HEALTH_ENABLED: "true" + KC_HTTP_ENABLED: "true" + ports: + - "8180:8080" + volumes: + - ./keycloak/realms:/opt/keycloak/data/import:ro,z + networks: [cg] + + # ── Flowable (S-03) ────────────────────────────────────────────────────── + flowable-db: + image: docker.io/library/postgres:16 + environment: + POSTGRES_USER: flowable + POSTGRES_PASSWORD: flowable + POSTGRES_DB: flowable + volumes: + - flowable-db:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U flowable -d flowable"] + interval: 5s + timeout: 3s + retries: 10 + networks: [cg] + + flowable-rest: + image: docker.io/flowable/flowable-rest:latest + environment: + SPRING_DATASOURCE_DRIVER-CLASS-NAME: org.postgresql.Driver + SPRING_DATASOURCE_URL: jdbc:postgresql://flowable-db:5432/flowable + SPRING_DATASOURCE_USERNAME: flowable + SPRING_DATASOURCE_PASSWORD: flowable + ports: + - "8090:8080" + depends_on: + flowable-db: + condition: service_healthy + networks: [cg] + + flowable-init: + image: docker.io/curlimages/curl:latest + restart: "no" + volumes: + - ../workflows/registratie.bpmn:/work/registratie.bpmn:ro,z + command: + - sh + - -c + - | + base=http://flowable-rest:8080/flowable-rest/service/repository/deployments + until curl -sf -u rest-admin:test "$$base" >/dev/null 2>&1; do echo "waiting for flowable-rest..."; sleep 3; done + if curl -s -u rest-admin:test "$$base?name=registratie" | grep -q '"name":"registratie"'; then + echo "registratie already deployed; skip" + else + curl -sf -u rest-admin:test -F 'file=@/work/registratie.bpmn;filename=registratie.bpmn' "$$base" >/dev/null && echo "deployed registratie" + fi + depends_on: + flowable-rest: + condition: service_started + networks: [cg] + + # ── ACL ────────────────────────────────────────────────────────────────── + acl: + build: + context: ../services/acl + dockerfile: Dockerfile + image: register-referentie/acl:dev + environment: + Acl__OpenZaak__BaseUrl: http://openzaak:8000/ + Acl__OpenZaak__ClientId: big-reference-seed + Acl__OpenZaak__Secret: insecure-dev-secret-change-me + Acl__Defaults__Bronorganisatie: "517439943" + Acl__Defaults__VerantwoordelijkeOrganisatie: "517439943" + Acl__Defaults__Vertrouwelijkheidaanduiding: openbaar + # Override with the real zaaktype URL after running seed_catalogus.py. + Acl__Defaults__ZaaktypeUrl: ${ACL_ZAAKTYPE_URL:-http://openzaak:8000/catalogi/api/v1/zaaktypen/00000000-0000-0000-0000-000000000000} + ports: + - "8100:8080" + healthcheck: + test: ["CMD", "curl", "-fsS", "http://localhost:8080/health"] + interval: 5s + timeout: 3s + retries: 5 + start_period: 10s + depends_on: + openzaak: + condition: service_healthy + networks: [cg] + + # ── BFF ────────────────────────────────────────────────────────────────── bff: build: context: ../services/bff @@ -17,3 +287,12 @@ services: timeout: 3s retries: 5 start_period: 10s + networks: [cg] + +volumes: + oz-db: + nrc-db: + flowable-db: + +networks: + cg: diff --git a/services/acl/.dockerignore b/services/acl/.dockerignore new file mode 100644 index 0000000..39d8eb6 --- /dev/null +++ b/services/acl/.dockerignore @@ -0,0 +1,3 @@ +**/bin +**/obj +**/*.user diff --git a/services/acl/Dockerfile b/services/acl/Dockerfile new file mode 100644 index 0000000..464d080 --- /dev/null +++ b/services/acl/Dockerfile @@ -0,0 +1,32 @@ +# Multi-stage build for the ACL service (.NET 10). +# Build context is services/acl (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 Acl.Api/Acl.Api.csproj Acl.Api/ +COPY Acl.Application/Acl.Application.csproj Acl.Application/ +COPY Acl.Infrastructure/Acl.Infrastructure.csproj Acl.Infrastructure/ +RUN dotnet restore Acl.Api/Acl.Api.csproj + +COPY Acl.Api/ Acl.Api/ +COPY Acl.Application/ Acl.Application/ +COPY Acl.Infrastructure/ Acl.Infrastructure/ +RUN dotnet publish Acl.Api/Acl.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", "Acl.Api.dll"]