docs(bindings): réconcilier la Phase 2 (bases) avec le code réel

Constat : le binding app→base existe déjà, côté base (consommateur/portee
dans bases-donnees.yml), résolu dans le rôle au déploiement (include_vars +
lookup('vars', secret)), sur 4 rôles. Délibérément conservé (le secret ne
quitte jamais le rôle) — ne pas dupliquer en app-side/instancier.

Note corrigée : §3.1 raffinée (app→app côté app, app→base côté base — deux
directions assumées) ; §5 réécrite ; §9 mise à jour. Reste (session Keycloak) :
factoriser le bloc de résolution copié-collé en include partagé (DRY).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Daniel Allaire 2026-07-03 08:49:04 -04:00
parent d95b26c243
commit 2755df44f1
2 changed files with 48 additions and 13 deletions

View file

@ -14,6 +14,13 @@
dur à des liens déclaratifs — `make instancier` donne **DIFF VIDE** (mêmes variables générées),
puis les group_vars sont retirés. La topologie mail devient déclarative et portable.
Cf. `docs/bindings-conception.md`. Suite : bases (Phase 2), exposition/domaines (Phase 3), GUI (Phase 4).
- **Bindings — Phase 2 (bases) : constat + réconciliation de la note (`docs/bindings-conception.md` §5/§9).**
Inspection du code réel : le binding app→base **existe déjà****côté base** (`consommateur`/`portee`
dans `bases-donnees.yml`), résolu **dans le rôle** au déploiement (`include_vars` + filtre +
`lookup('vars', secret)`), sur 4 rôles (postgresql, forgejo, keycloak, icinga). **Délibérément
conservé** (le secret ne quitte jamais le rôle) — ne PAS dupliquer en app-side/instancier. Deux
directions assumées : app→app côté app (instancier), app→base côté base (registre). Reste (reporté
à l'épreuve de Keycloak) : factoriser le bloc de résolution copié-collé en include partagé (DRY).
## 2026-07-02

View file

@ -141,17 +141,41 @@ applications:
`domaines.yml` reste le **registre** des domaines publics (autorité, DNSSEC, secondaires, edge) ;
le lien app ↔ domaine relie enfin app + domaine + edge, aujourd'hui séparés.
## 5. Réconcilier l'existant (bases)
## 5. Les bases : binding **côté base**, déjà en place (constat 2026-07-03)
Le binding base existe aujourd'hui **côté base** (`consommateur` pointe l'app). On l'aligne sur la
direction retenue :
> **Correction du modèle après inspection du code réel.** La §3.1 tranchait « tout lien côté
> app ». Mais le binding app→base **existe déjà et fonctionne**, par un mécanisme *différent* de
> celui des liens app→app. Il **ne faut pas le dupliquer** dans `instancier`.
- `bases-donnees.yml` garde la **définition** de la base (type, hôte, port, `secret`,
`proprietaire`) — la base reste une entité à part entière ;
- le **lien de consommation** passe côté app (`role: base`) ; la résolution injecte chez le
consommateur la chaîne de connexion + le **nom** de la variable Vault (jamais le secret) ;
- `consommateur` / `portee` sur la base deviennent une **rétro-référence calculée** (ou sont
dépréciés). Migration douce.
Réalité : quatre rôles (`serveur_postgresql`, `serveur_forgejo`, `serveur_keycloak`,
`serveur_icinga`) résolvent leur base **au déploiement, dans le rôle**, depuis le registre
`plan/bases-donnees.yml` :
```yaml
- include_vars: bases-donnees.yml # charge le registre
- set_fact: entree = bases_donnees | selectattr(consommateur == mon_groupe) # filtre côté base
- assert: entree + secret présents
- set_fact: db_password = "{{ lookup('vars', entree.secret) }}" # déréférence le secret par NOM
- set_fact: db_host = <hôte du serveur_bd, depuis le registre>
```
Le binding est donc **côté base** (`consommateur` + `portee`), **résolu à l'exécution dans le
rôle**. C'est **délibérément conservé**, pour deux raisons :
1. **le secret ne quitte jamais le rôle**`lookup('vars', entree.secret)` déréférence la
variable Vault *par son nom* au moment du déploiement ; instancier n'a jamais à toucher un
secret ni à l'écrire dans l'inventaire ;
2. c'est **déjà utilisé et cohérent** sur 4 rôles ; le refaire en app-side/instancier serait de
la duplication à risque, sans gain.
**Conséquence sur la §3.1** : la direction « côté app » vaut pour les liens **app→app** (mail,
annuaire, milter…) résolus par instancier. Les liens **app→base** restent **côté base**, résolus
dans le rôle. Deux directions, **assumées**, chacune selon la nature de la cible et la sensibilité
du secret. La §3.1 est donc raffinée, pas contredite.
Reste comme valeur réelle (non bloquant) : **factoriser** le bloc de résolution copié-collé dans
les 4 rôles en un include partagé (ex. `roles/_resoudre_base/`). À faire **avec l'épreuve de
Keycloak** (qui utilise ce mécanisme), pour valider le DRY en le déployant. Cf. §9.
## 6. Règles de validation
@ -179,11 +203,15 @@ Premier cas concret, à faire en régression (les mêmes variables doivent être
## 9. Phasage
- **Phase 0** : cette note + décision. ✅ (direction : liens côté app)
- **Phase 0** : cette note + décision. ✅ (direction : liens côté app pour app→app)
- **Phase 1** : résolveur de liens dans `instancier.py` + `meta/liens.yml` des rôles mail ;
migrer les 3 liens mail ; prouver que le déploiement reste identique (régression).
- **Phase 2** : réconcilier les bases (consommateur → liens app ; chaîne de connexion).
- **Phase 3** : exposition / domaines (vhosts nginx dérivés des liens).
migrer les liens mail ; régression DIFF VIDE. ✅ (2026-07-03 : `mailstore` + `milter` migrés)
- **Phase 2** : bases. ✅ **déjà en place** — binding **côté base** (registre + résolution en
rôle, 4 rôles), cf. §5. Ne PAS dupliquer dans instancier. **Reste (reporté à la session
Keycloak)** : factoriser le bloc de résolution copié-collé en un include partagé (DRY),
validé en déployant Keycloak.
- **Phase 3** : exposition / domaines (vhosts nginx dérivés des liens ; machinerie `expose` /
`expositions_des_applications` déjà présente).
- **Phase 4** : vue GUI des liens + graphe.
## 10. Décisions ouvertes