diff --git a/.gitea/workflows/ci.yaml b/.gitea/workflows/ci.yaml index 8d4a012..7eabeb2 100644 --- a/.gitea/workflows/ci.yaml +++ b/.gitea/workflows/ci.yaml @@ -280,7 +280,7 @@ jobs: # 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-db flowable-rest flowable-init keycloak acl bff domain projection-db event-subscriber projection-api self-service openbaar behandel beheer objecttypen-db objecttypen-redis objecttypen-init objecttypen tempo prometheus grafana 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 self-service openbaar behandel beheer objecttypen-db objecttypen-redis objecttypen-init objecttypen objecten-db objecten-redis objecten-init objecten tempo prometheus grafana 2>&1 || true - name: Tear down if: always() run: make down diff --git a/Makefile b/Makefile index 7006158..a399cbf 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 domain event-subscriber projection-api self-service openbaar behandel beheer objecttypen +WAIT_SVCS := openzaak nrc-web acl bff domain event-subscriber projection-api self-service openbaar behandel beheer objecttypen objecten # 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 @@ -18,7 +18,7 @@ WAIT_SVCS := openzaak nrc-web acl bff domain event-subscriber projection-api se # volumes are `external`, so compose won't remove them — CFG_VOLS lists them for # explicit teardown. See docs/runbooks/gitea-actions-gotchas.md. SEED := bash infra/seed-config.sh -CFG_VOLS := rr-oz-config rr-nrc-config rr-kc-realms rr-fl-bpmn rr-objecttypen-config +CFG_VOLS := rr-oz-config rr-nrc-config rr-kc-realms rr-fl-bpmn rr-objecttypen-config rr-objecten-config # 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 # engine. This is the no-make / Windows-friendly path. See that file's header. @@ -94,14 +94,14 @@ mutation: # 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. smoke: - $(SEED) oz nrc kc fl objecttypen + $(SEED) oz nrc kc fl objecttypen objecten 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' ## 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) up: - $(SEED) oz nrc kc fl objecttypen + $(SEED) oz nrc kc fl objecttypen objecten docker compose -f $(COMPOSE) up -d --build ## down: stop and remove the local stack (incl. the external config volumes) @@ -139,7 +139,7 @@ changelog: ## verify-up: bring the FULL stack up and wait for health (CI verify-stack step 1; ## subsumes the old compose-smoke health gate — the DoD "up reaches green" check). verify-up: - $(SEED) oz nrc kc fl objecttypen + $(SEED) oz nrc kc fl objecttypen objecten docker compose -f $(COMPOSE) up -d --build WAIT_TIMEOUT=420 bash infra/wait-healthy.sh $(WAIT_SVCS) @@ -200,7 +200,7 @@ verify-objecten: ## tear down (always). For fast single-concern local iteration use `integration` ## (oz-only) or `verify-notifications` (oz+nrc) instead. verify: - $(SEED) oz nrc kc fl objecttypen + $(SEED) oz nrc kc fl objecttypen objecten docker compose -f $(COMPOSE) up -d --build @bash -c 'set -e; rc=0; \ WAIT_TIMEOUT=420 bash infra/wait-healthy.sh $(WAIT_SVCS) \ diff --git a/infra/docker-compose.local.yml b/infra/docker-compose.local.yml index fd1d1c1..a03cacd 100644 --- a/infra/docker-compose.local.yml +++ b/infra/docker-compose.local.yml @@ -621,12 +621,76 @@ services: condition: service_completed_successfully networks: [cg] + # ── Objecten API (S-18b) — bind-mounted config (local variant) ───────────── + objecten-db: + image: docker.io/postgis/postgis:17-3.5 + environment: + POSTGRES_USER: objects + POSTGRES_PASSWORD: objects + POSTGRES_DB: objects + volumes: + - objecten-db:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U objects"] + interval: 5s + timeout: 3s + retries: 10 + networks: [cg] + + objecten-redis: + image: docker.io/library/redis:7 + networks: [cg] + + objecten-init: + image: docker.io/maykinmedia/objects-api:${OBJECTS_TAG:-3.4.0} + environment: &objecten-env-local + DJANGO_SETTINGS_MODULE: objects.conf.docker + SECRET_KEY: ${OBJECTS_SECRET_KEY:-dev-only-not-for-production} + DB_HOST: objecten-db + DB_NAME: objects + DB_USER: objects + DB_PASSWORD: objects + ALLOWED_HOSTS: "*" + CACHE_DEFAULT: objecten-redis:6379/0 + CACHE_AXES: objecten-redis:6379/0 + DISABLE_2FA: "true" + OTEL_SDK_DISABLED: "true" + RUN_SETUP_CONFIG: "true" + command: /setup_configuration.sh + volumes: + - ./objecten/setup_configuration:/app/setup_configuration:ro,z + depends_on: + objecten-db: + condition: service_healthy + objecten-redis: + condition: service_started + objecttypen: + condition: service_healthy + networks: [cg] + + objecten: + image: docker.io/maykinmedia/objects-api:${OBJECTS_TAG:-3.4.0} + environment: *objecten-env-local + 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: + - "8021:8000" + depends_on: + objecten-init: + condition: service_completed_successfully + networks: [cg] + volumes: oz-db: nrc-db: flowable-db: projection-db: objecttypen-db: + objecten-db: # Carries the seed-generated acl.env (server-assigned zaaktype URLs) from local-seed to the ACL. seed-env: diff --git a/infra/docker-compose.yml b/infra/docker-compose.yml index 468e57c..2d0b2a0 100644 --- a/infra/docker-compose.yml +++ b/infra/docker-compose.yml @@ -635,6 +635,76 @@ services: condition: service_completed_successfully networks: [cg] + # ── Objecten API (S-18b) — upstream Maykin image, verbatim ───────────────── + # The authoritative object store. Same shape as Objecttypen (own DB + redis, an `-init` that runs + # setup_configuration from the external config volume, a health-checked web). Two differences: the + # DB is PostGIS (objects carry geometry), and setup_configuration registers the Objecttypen API + # (S-18a) as a trusted service so an object can reference its objecttype. + objecten-db: + image: docker.io/postgis/postgis:17-3.5 + environment: + POSTGRES_USER: objects + POSTGRES_PASSWORD: objects + POSTGRES_DB: objects + volumes: + - objecten-db:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U objects"] + interval: 5s + timeout: 3s + retries: 10 + networks: [cg] + + objecten-redis: + image: docker.io/library/redis:7 + networks: [cg] + + objecten-init: + image: docker.io/maykinmedia/objects-api:${OBJECTS_TAG:-3.4.0} + environment: &objecten-env + DJANGO_SETTINGS_MODULE: objects.conf.docker + SECRET_KEY: ${OBJECTS_SECRET_KEY:-dev-only-not-for-production} + DB_HOST: objecten-db + DB_NAME: objects + DB_USER: objects + DB_PASSWORD: objects + ALLOWED_HOSTS: "*" + CACHE_DEFAULT: objecten-redis:6379/0 + CACHE_AXES: objecten-redis:6379/0 + DISABLE_2FA: "true" + OTEL_SDK_DISABLED: "true" + RUN_SETUP_CONFIG: "true" + command: /setup_configuration.sh + # data.yaml is streamed into this external volume by infra/seed-config.sh before start. + volumes: + - objecten-config:/app/setup_configuration:ro + depends_on: + objecten-db: + condition: service_healthy + objecten-redis: + condition: service_started + # Objecten's setup_configuration registers the Objecttypen service; that service only needs to + # exist as config, but wait for Objecttypen to be up so the register is meaningful end to end. + objecttypen: + condition: service_healthy + networks: [cg] + + objecten: + image: docker.io/maykinmedia/objects-api:${OBJECTS_TAG:-3.4.0} + environment: *objecten-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: + - "8021:8000" + depends_on: + objecten-init: + condition: service_completed_successfully + networks: [cg] + # ── Observability backplane (S-16a, ADR-0023) ────────────────────────────── # Grafana-native stack: Tempo ingests OTLP traces (the .NET services export # straight to it — no collector hop, S-16b), Prometheus scrapes service @@ -685,6 +755,7 @@ volumes: flowable-db: projection-db: objecttypen-db: + objecten-db: # Config volumes — created and populated out-of-band by infra/seed-config.sh # (docker cp), because bind mounts don't reach sibling containers on the CI # runner. `external` keeps the names deterministic; the seed step manages them. @@ -703,6 +774,9 @@ volumes: objecttypen-config: external: true name: rr-objecttypen-config + objecten-config: + external: true + name: rr-objecten-config networks: cg: diff --git a/infra/objecten/setup_configuration/data.yaml b/infra/objecten/setup_configuration/data.yaml new file mode 100644 index 0000000..1a5b2a3 --- /dev/null +++ b/infra/objecten/setup_configuration/data.yaml @@ -0,0 +1,31 @@ +# Objecten API setup_configuration (S-18b). Streamed into the external rr-objecten-config volume by +# infra/seed-config.sh and applied by objecten-init (RUN_SETUP_CONFIG). Declarative + idempotent. +# +# Two things: (1) register the Objecttypen API (S-18a) as a trusted service so an object can +# reference its objecttype — authenticating with the dev static token Objecttypen provisioned; and +# (2) a dev static token so peers (the ACL, S-19) can write objects here. Dev-only, not for prod. + +# (1) Trust the Objecttypen API. `orc` = overige RESTful component (how zgw_consumers classifies the +# Objecttypen API). The RegisterRecord objecttype (S-18c) will reference an objecttype under this +# service by uuid. +zgw_consumers_config_enable: true +zgw_consumers: + services: + - identifier: objecttypen + label: Objecttypen API + api_type: orc + api_root: http://objecttypen:8000/api/v2/ + auth_type: api_key + header_key: Authorization + header_value: Token 0123456789abcdef0123456789abcdef01234567 + +# (2) Static API token peers use to write/read objects. +tokenauth_config_enable: true +tokenauth: + items: + - identifier: register-referentie + token: 1234567890abcdef1234567890abcdef12345678 + contact_person: Register Referentie + email: admin@localhost + organization: Respellion + is_superuser: true diff --git a/infra/seed-config.sh b/infra/seed-config.sh index 6ee5907..6895d6c 100755 --- a/infra/seed-config.sh +++ b/infra/seed-config.sh @@ -13,7 +13,7 @@ # subcommand. Fixed-name `external` volumes keep the names deterministic across # both runtimes. See docs/runbooks/gitea-actions-gotchas.md. # -# Usage: seed-config.sh [ ...] where key ∈ { oz, kc, fl } +# Usage: seed-config.sh [ ...] where key ∈ { oz, nrc, kc, fl, objecttypen, objecten } set -euo pipefail here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" @@ -33,7 +33,7 @@ populate() { # volume source(file or dir/.) echo " seeded $vol" } -[ "$#" -gt 0 ] || { echo "usage: seed-config.sh ..." >&2; exit 2; } +[ "$#" -gt 0 ] || { echo "usage: seed-config.sh ..." >&2; exit 2; } # The registratie process (BPMN) and its diploma-eligibility DMN are deployed as SEPARATE Flowable # deployments — the process engine and the DMN engine each own theirs (S-13, ADR-0016). flowable-rest @@ -50,6 +50,7 @@ for key in "$@"; do nrc) populate rr-nrc-config "$here/opennotificaties/setup_configuration/." ;; kc) populate rr-kc-realms "$here/keycloak/realms/." ;; objecttypen) populate rr-objecttypen-config "$here/objecttypen/setup_configuration/." ;; + objecten) populate rr-objecten-config "$here/objecten/setup_configuration/." ;; fl) d="$(mktemp -d)"; stage_flowable_workflows "$d"; populate rr-fl-bpmn "$d/." ;; *) echo "unknown seed key: $key" >&2; exit 2 ;; esac