Provisionner le rôle applicatif à chaque docker compose up
Le rôle planflow_app n etait créé qu à la première initialisation du volume : une base existante au rôle absent (ou au mot de passe changé) bloquait l app en échec P1000 sans remède automatique. Ajouter un service db-init, idempotent, qui rejoue docker/init-app-role.sh à chaque up — branche ELSE re-synchronisant le mot de passe — avant que l app ne démarre.
This commit is contained in:
1 parent
f4e3608d72
commit
2c3c67b949
3 files changed
+44
-4
No files matched your search
@@ -148,7 +148,9 @@ pnpm test:e2e # build, serveur standalone, tests de bout en bout
|
|||||||
|
|
||||||
**L'application ne doit pas se connecter en superutilisateur PostgreSQL.**
|
**L'application ne doit pas se connecter en superutilisateur PostgreSQL.**
|
||||||
|
|
||||||
En docker-compose c'est déjà réglé : `docker/init-app-role.sh` crée au premier démarrage un rôle `planflow_app`, `NOSUPERUSER NOBYPASSRLS`, propriétaire de la base — il lui faut ce droit pour appliquer les migrations, et les politiques sont déclarées en `FORCE` précisément pour s'appliquer aussi au propriétaire.
|
En docker-compose c'est déjà réglé : d'abord `docker/init-app-role.sh` au premier démarrage, puis le service `db-init` qui le rejoue **à chaque `docker compose up`** — et seulement ensuite l'application. Le rôle `planflow_app`, `NOSUPERUSER NOBYPASSRLS`, propriétaire de la base — il lui faut ce droit pour appliquer les migrations, et les politiques sont déclarées en `FORCE` précisément pour s'appliquer aussi au propriétaire.
|
||||||
|
|
||||||
|
L'idempotence n'est pas un luxe : une base déjà en place dont le rôle manque (ou dont le mot de passe a changé) ne doit pas exiger de SQL à la main. Le service `db-init` corrige les deux cas à chaque relance, et l'application n'est démarrée qu'une fois sa tâche terminée.
|
||||||
|
|
||||||
Le script ne s'exécute qu'à la **première** initialisation du volume. Sur une installation déjà en place, jouer le même SQL à la main puis basculer `DATABASE_URL` sur ce rôle.
|
Le script ne s'exécute qu'à la **première** initialisation du volume. Sur une installation déjà en place, jouer le même SQL à la main puis basculer `DATABASE_URL` sur ce rôle.
|
||||||
|
|
||||||
|
|||||||
@@ -41,6 +41,30 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- '${POSTGRES_PORT:-55432}:5432'
|
- '${POSTGRES_PORT:-55432}:5432'
|
||||||
|
|
||||||
|
# Provisionne le rôle applicatif à chaude occurrence de `up`, pas seulement à
|
||||||
|
# la première initialisation du volume : inversement, une base existante dont
|
||||||
|
# le rôle est absent (ou dont le mot de passe a changé) laisserait l'app en
|
||||||
|
# échec d'authentification P1000 sans recours autre que du SQL à la main.
|
||||||
|
# Idempotent : rejouable autant de fois que nécessaire.
|
||||||
|
db-init:
|
||||||
|
image: postgres:16-alpine
|
||||||
|
restart: 'no'
|
||||||
|
depends_on:
|
||||||
|
db:
|
||||||
|
condition: service_healthy
|
||||||
|
environment:
|
||||||
|
POSTGRES_USER: ${POSTGRES_USER:-planflow}
|
||||||
|
PGPASSWORD: ${POSTGRES_PASSWORD:-planflow-interne}
|
||||||
|
POSTGRES_DB: ${POSTGRES_DB:-planflow}
|
||||||
|
APP_DB_USER: ${APP_DB_USER:-planflow_app}
|
||||||
|
APP_DB_PASSWORD: ${APP_DB_PASSWORD:-planflow-app-interne}
|
||||||
|
PGHOST: db
|
||||||
|
volumes:
|
||||||
|
- ./docker/init-app-role.sh:/docker-entrypoint-initdb.d/10-init-app-role.sh:ro
|
||||||
|
command: ['sh', '/docker-entrypoint-initdb.d/10-init-app-role.sh']
|
||||||
|
networks:
|
||||||
|
- interne
|
||||||
|
|
||||||
app:
|
app:
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
@@ -48,6 +72,8 @@ services:
|
|||||||
depends_on:
|
depends_on:
|
||||||
db:
|
db:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
db-init:
|
||||||
|
condition: service_completed_successfully
|
||||||
environment:
|
environment:
|
||||||
NODE_ENV: production
|
NODE_ENV: production
|
||||||
# Le compte applicatif, **pas** le superutilisateur d'amorçage : un
|
# Le compte applicatif, **pas** le superutilisateur d'amorçage : un
|
||||||
|
|||||||
+15
-3
@@ -14,18 +14,30 @@ set -eu
|
|||||||
# ni superutilisateur ni BYPASSRLS. C'est précisément pourquoi les politiques
|
# ni superutilisateur ni BYPASSRLS. C'est précisément pourquoi les politiques
|
||||||
# sont déclarées en FORCE : elles s'appliquent aussi au propriétaire.
|
# sont déclarées en FORCE : elles s'appliquent aussi au propriétaire.
|
||||||
#
|
#
|
||||||
# Ce script ne s'exécute qu'à la **première** initialisation du volume. Pour une
|
# Playé par le conteneur db à la **première** initialisation du volume, puis par
|
||||||
# installation déjà en place, jouer le même SQL à la main (voir README).
|
# le service `db-init` à chaque `docker compose up`. Idempotent : la branche
|
||||||
|
# `ELSE` re-synchronise le mot de passe d'une base déjà en place.
|
||||||
|
|
||||||
APP_ROLE="${APP_DB_USER:-planflow_app}"
|
APP_ROLE="${APP_DB_USER:-planflow_app}"
|
||||||
APP_PASSWORD="${APP_DB_PASSWORD:-planflow-app-interne}"
|
APP_PASSWORD="${APP_DB_PASSWORD:-planflow-app-interne}"
|
||||||
|
|
||||||
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<SQL
|
# En officiation dans le conteneur db, l'hôte est local ; depuis le service
|
||||||
|
# db-init, il est `db`. L'un et l'autre doivent aboutir au même SQL.
|
||||||
|
if [ -n "${PGHOST:-}" ]; then
|
||||||
|
set -- -h "$PGHOST" -p "${PGPORT:-5432}" -U "$POSTGRES_USER" -d "$POSTGRES_DB" -v ON_ERROR_STOP=1
|
||||||
|
else
|
||||||
|
set -- -U "$POSTGRES_USER" -d "$POSTGRES_DB" -v ON_ERROR_STOP=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
psql "$@" <<SQL
|
||||||
DO \$\$
|
DO \$\$
|
||||||
BEGIN
|
BEGIN
|
||||||
IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = '${APP_ROLE}') THEN
|
IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = '${APP_ROLE}') THEN
|
||||||
CREATE ROLE ${APP_ROLE} LOGIN PASSWORD '${APP_PASSWORD}'
|
CREATE ROLE ${APP_ROLE} LOGIN PASSWORD '${APP_PASSWORD}'
|
||||||
NOSUPERUSER NOCREATEDB NOCREATEROLE NOBYPASSRLS;
|
NOSUPERUSER NOCREATEDB NOCREATEROLE NOBYPASSRLS;
|
||||||
|
ELSE
|
||||||
|
ALTER ROLE ${APP_ROLE} WITH LOGIN PASSWORD '${APP_PASSWORD}'
|
||||||
|
NOSUPERUSER NOCREATEDB NOCREATEROLE NOBYPASSRLS;
|
||||||
END IF;
|
END IF;
|
||||||
END
|
END
|
||||||
\$\$;
|
\$\$;
|
||||||
|
|||||||
Reference in new issue
Block a user