Merge pull request 'feat(postgres) — PostGIS, posé par le playbook et non par une image custom' (#52) from arcodange/postgis-pour-kadans into main
This commit was merged in pull request #52.
This commit is contained in:
@@ -22,4 +22,45 @@ postgres:
|
|||||||
|
|
||||||
pgbouncer:
|
pgbouncer:
|
||||||
auth_user: &pgbouncer_auth pgbouncer_auth
|
auth_user: &pgbouncer_auth pgbouncer_auth
|
||||||
auth_user_password: *pgbouncer_auth
|
auth_user_password: *pgbouncer_auth
|
||||||
|
|
||||||
|
# PostGIS — spatial types for the databases that ask for it.
|
||||||
|
#
|
||||||
|
# Kadans stores neighbourhood ("zone") outlines as real geometry, so it needs
|
||||||
|
# geometry(MultiPolygon,4326), ST_Contains and a GiST index. The image stays
|
||||||
|
# `postgres:16.3-alpine`: the extension is installed INTO the running container
|
||||||
|
# by setup/postgres.yml, exactly like the pgbouncer role and the app databases
|
||||||
|
# are created there. No custom image (founder's call, 2026-08-08).
|
||||||
|
#
|
||||||
|
# WARNING — this is a per-CONTAINER install, not a per-VOLUME one. `apk add`
|
||||||
|
# writes to the container's writable layer, so recreating the container (image
|
||||||
|
# change, `docker compose up --force-recreate`) REMOVES PostGIS while the data
|
||||||
|
# keeps its geometry columns — every spatial query then fails until this
|
||||||
|
# playbook runs again. The install task is therefore idempotent and runs after
|
||||||
|
# every compose deploy, and it is the reason `postgis_verifier` exists below:
|
||||||
|
# a silent absence would look like an application bug.
|
||||||
|
postgis:
|
||||||
|
# Only these databases get the extension. Adding one here is the whole change.
|
||||||
|
databases:
|
||||||
|
- kadans
|
||||||
|
# The Alpine package. Pinned to a MAJOR line, not a patch: postgis 3.x
|
||||||
|
# upgrades within a major are ABI-compatible with a given PostgreSQL major.
|
||||||
|
paquet: postgis
|
||||||
|
# ⚠ WHY THE COPY STEP EXISTS — measured on pi2 (arm64), 2026-08-08.
|
||||||
|
# `apk add postgis` alone SUCCEEDS and `CREATE EXTENSION postgis` still fails:
|
||||||
|
#
|
||||||
|
# ERROR: extension "postgis" is not available
|
||||||
|
# DETAIL: Could not open extension control file
|
||||||
|
# "/usr/local/share/postgresql/extension/postgis.control"
|
||||||
|
#
|
||||||
|
# Alpine's package targets Alpine's own PostgreSQL layout
|
||||||
|
# (/usr/share/postgresql16, /usr/lib/postgresql16), while the official
|
||||||
|
# `postgres:16-alpine` image builds the server into /usr/local. The files are
|
||||||
|
# on disk, the server looks elsewhere. Relocating them makes it work — proven
|
||||||
|
# in a throwaway container: PostGIS 3.4 USE_GEOS=1 USE_PROJ=1, and a Lyon
|
||||||
|
# polygon round-tripping through ST_GeomFromText/ST_AsGeoJSON.
|
||||||
|
#
|
||||||
|
# Do NOT "simplify" this to a bare `apk add`. An `apk add --simulate` reports
|
||||||
|
# OK, the install reports OK, and the extension is still unusable.
|
||||||
|
source_partagee: /usr/share/postgresql16/extension
|
||||||
|
source_lib: /usr/lib/postgresql16
|
||||||
@@ -25,6 +25,63 @@
|
|||||||
applications_databases:
|
applications_databases:
|
||||||
gitea: "{{ gitea_database }}"
|
gitea: "{{ gitea_database }}"
|
||||||
|
|
||||||
|
# ── PostGIS ────────────────────────────────────────────────────────────
|
||||||
|
# Installed INTO the running container rather than baked into a custom
|
||||||
|
# image (founder's call, 2026-08-08). See group_vars/postgres/postgres.yml
|
||||||
|
# for why the copy step is not optional, and for the durability caveat.
|
||||||
|
#
|
||||||
|
# Runs after the compose deploy above ON PURPOSE: if that task recreated
|
||||||
|
# the container, the writable layer is fresh and PostGIS is gone with it.
|
||||||
|
# This is what makes the pair (deploy, install) safe to replay.
|
||||||
|
- name: Install PostGIS into the Postgres container
|
||||||
|
ansible.builtin.shell: |
|
||||||
|
set -eu
|
||||||
|
docker exec {{ postgres_container_name }} sh -c '
|
||||||
|
set -eu
|
||||||
|
apk add --no-cache {{ postgis.paquet }} >/dev/null
|
||||||
|
cp -r {{ postgis.source_partagee }}/* "$(pg_config --sharedir)/extension/"
|
||||||
|
cp -r {{ postgis.source_lib }}/*.so "$(pg_config --pkglibdir)/"
|
||||||
|
'
|
||||||
|
# `apk add` is idempotent and the copies overwrite identical files, so a
|
||||||
|
# replay changes nothing observable. We do not pretend to detect that:
|
||||||
|
# claiming `changed_when: false` outright would hide a REAL first install.
|
||||||
|
register: postgis_installation
|
||||||
|
changed_when: "'Installing' in postgis_installation.stdout"
|
||||||
|
|
||||||
|
- name: Enable PostGIS on the databases that need it
|
||||||
|
ansible.builtin.shell: |
|
||||||
|
docker exec {{ postgres_container_name }} \
|
||||||
|
psql -U postgres -d {{ item }} -tAc 'CREATE EXTENSION IF NOT EXISTS postgis;'
|
||||||
|
loop: "{{ postgis.databases }}"
|
||||||
|
register: postgis_activation
|
||||||
|
changed_when: "'CREATE EXTENSION' in postgis_activation.stdout"
|
||||||
|
|
||||||
|
# ⚠ THE HALF THAT MATTERS. Without it, a botched install leaves a green
|
||||||
|
# playbook and an application that fails at its first spatial query — the
|
||||||
|
# symptom would land in Kadans, days later, looking like an app bug.
|
||||||
|
# We ask the database itself, and we FAIL on anything unexpected.
|
||||||
|
- name: Verify PostGIS answers on every database
|
||||||
|
ansible.builtin.shell: |
|
||||||
|
docker exec {{ postgres_container_name }} psql -U postgres -d {{ item }} -tAc \
|
||||||
|
"SELECT postgis_version() || ' | ' || ST_AsGeoJSON(ST_SetSRID(ST_Point(4.83, 45.76), 4326));"
|
||||||
|
loop: "{{ postgis.databases }}"
|
||||||
|
register: postgis_verifier
|
||||||
|
changed_when: false
|
||||||
|
# Not just "the command exited 0": a real geometry must come back with
|
||||||
|
# the SRID applied. A stub that answered an empty string would pass a
|
||||||
|
# bare rc check — and prove nothing.
|
||||||
|
failed_when: >-
|
||||||
|
postgis_verifier.rc != 0
|
||||||
|
or 'USE_GEOS=1' not in postgis_verifier.stdout
|
||||||
|
or '"type":"Point"' not in postgis_verifier.stdout
|
||||||
|
|
||||||
|
- name: Report the PostGIS version in use
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg: "PostGIS on {{ item.item }} → {{ item.stdout | trim }}"
|
||||||
|
loop: "{{ postgis_verifier.results }}"
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item.item }}"
|
||||||
|
|
||||||
- name: Create auth_user for pgbouncer (connection pool component)
|
- name: Create auth_user for pgbouncer (connection pool component)
|
||||||
ansible.builtin.shell: |
|
ansible.builtin.shell: |
|
||||||
docker exec -it {{ postgres_container_name }} psql -U postgres -d {{ database }} -tc "{{ pg_instruction.replace('$','\$') }}"
|
docker exec -it {{ postgres_container_name }} psql -U postgres -d {{ database }} -tc "{{ pg_instruction.replace('$','\$') }}"
|
||||||
|
|||||||
Reference in New Issue
Block a user