depot de binaires : le site tient ce que les runners allaient chercher
Le cache du site couvrait apt ; quatre artefacts arrivaient autrement, parce qu ils ne vivent dans aucun depot apt. Le controleur les tire puis les pousse par SSH. Mesure du 2026-09-12 : le cache du runner du site est ABSENT. Un second locataire monte depuis lui sortait chercher 570 Mo sur codeberg.org, github.com et download.nextcloud.com, alors que le meme ecosysteme ne demandait plus un seul paquet a Debian. Le poste du mainteneur les a depuis toujours : personne ne l avait vu. Pas de relais transparent, et la mesure tranche : github.com redirige vers une URL signee valable une heure, differente a chaque requete. Un cache qui la prend pour cle ne fait jamais mouche. Le relais marcherait pour deux amonts sur quatre. Donc un vrai depot, dans le service qui existe deja. LocalDirs d apt-cacher-ng publie un repertoire du disque sous un prefixe, eprouve AVANT d ecrire le role. Aucun service, aucun port, aucun certificat, aucun flux nouveaux : l ingress 3142 pair flotte couvre exactement ce chemin. Les versions ne sont pas recopiees : le role lit les defauts des quatre consommateurs. Les quatre roles recoivent une tache AJOUTEE, placee avant leur stat de cache — si le depot sert, le stat le voit et la tache amont se saute d elle-meme. Aucune tache existante n a change. P70 exige que tout dest ecrit sous un cache_local figure au depot. Une liste qui suit une autre prend du retard ; celle-ci est nee avec sa garde. Deux marches payees en chemin : - failed_when: false REECRIT le verdict, donc la premiere garde de signature ne gardait rien. Elles mesurent le fichier desormais. - file: state=directory cree les parents en 0750 : apt-cacher-ng, qui ne tourne pas en root, rendait 403 sur chaque fichier. Un chemin se traverse en entier. Verifie sur l infrastructure : 6/6 artefacts servis (200/206) depuis le runner du site ET depuis une machine du locataire a travers la frontiere ; les 6 empreintes SHA-256 sont identiques a celles qui ont construit Chezlepro ; second passage changed=0. make prouver : 69 OK, 0 echec, 1 saute. ansible-lint : 0 failure, profil production. Inclut aussi force: true sur cinq telechargements de cles : une reprise conditionnelle ne reprend rien (304 Not Modified, size 0, attempts 5). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
This commit is contained in:
parent
56d202a8bb
commit
5812e0c6ca
19 changed files with 565 additions and 8 deletions
|
|
@ -188,7 +188,7 @@ Si `ansible-lint` n’est pas disponible, le signaler clairement. Ne pas invente
|
|||
## Écrire, puis relire (D-68)
|
||||
|
||||
`--syntax-check` et `ansible-lint` prouvent que le dépôt est cohérent **avec lui-même**.
|
||||
C'est aussi ce que font les 69 preuves de `make prouver` : elles lisent le dépôt, sans le
|
||||
C'est aussi ce que font les 70 preuves de `make prouver` : elles lisent le dépôt, sans le
|
||||
moindre appel réseau. **Aucune ne demande au système déployé s'il ressemble à ce que le
|
||||
dépôt annonce.**
|
||||
|
||||
|
|
|
|||
95
CHANGELOG.md
95
CHANGELOG.md
|
|
@ -1,5 +1,100 @@
|
|||
# CHANGELOG — Set-OPS
|
||||
|
||||
## 2026-09-12 (6) — Le site tient aussi les binaires, pas seulement les paquets
|
||||
|
||||
### Ce qui manquait
|
||||
|
||||
Le cache du site couvre `apt` depuis longtemps : 1,34 Go tires de l'amont, 6,13 Go servis
|
||||
a la flotte pendant la reconstruction d'un locataire — un rapport de 4,58. Mais quatre
|
||||
artefacts arrivent autrement, parce qu'ils ne vivent dans aucun depot apt : Forgejo,
|
||||
Keycloak, Nextcloud, oauth2-proxy. Le CONTROLEUR les tire (`delegate_to: localhost`) puis
|
||||
les pousse par SSH.
|
||||
|
||||
**Mesure du 2026-09-12** : `/opt/setops/.cache/setops` est ABSENT sur le runner du site.
|
||||
Un second locataire monte depuis ce runner sortait donc chercher **570 Mo** sur
|
||||
codeberg.org, github.com (deux fois) et download.nextcloud.com — alors que le meme
|
||||
ecosysteme ne demandait plus un seul paquet a Debian. Le poste du mainteneur, lui, a ces
|
||||
1,6 Go depuis toujours : c'est pourquoi personne ne l'avait vu.
|
||||
|
||||
### Pourquoi pas un relais transparent
|
||||
|
||||
Les `Remap-*` du cache font deja passer quatre fournisseurs HTTPS. Mesure des quatre
|
||||
amonts avant de decider :
|
||||
|
||||
| amont | reponse |
|
||||
|---|---|
|
||||
| `codeberg.org` | 200, aucune redirection |
|
||||
| `download.nextcloud.com` | 200, aucune redirection |
|
||||
| `github.com` (x2) | 302 vers `release-assets.githubusercontent.com`, **URL signee valable une heure** |
|
||||
|
||||
L'URL finale de GitHub change a chaque requete. Un cache qui la prend pour cle ne fait
|
||||
jamais mouche, et ce qu'il garderait serait perime avant d'etre relu. Le relais marche
|
||||
pour deux amonts sur quatre : ce n'est pas un mecanisme, c'est une coincidence.
|
||||
|
||||
### Ce qui a ete fait
|
||||
|
||||
**Un vrai depot de fichiers, dans le service qui existe deja.** `LocalDirs` d'apt-cacher-ng
|
||||
publie un repertoire du disque sous un prefixe — eprouve sur `site-cache-01` AVANT d'ecrire
|
||||
le role : 200, 90 158 octets, identiques a l'octet. Donc **aucun service, aucun port,
|
||||
aucun certificat, aucun flux nouveaux** : l'`ingress 3142 pair: flotte` deja declare couvre
|
||||
exactement ce chemin.
|
||||
|
||||
- `serveur_artefacts` publie `/var/lib/setops/artefacts-directs` sous `setops-binaires`,
|
||||
et le remplit depuis les amonts — une seule machine sort, pour tout le site et pour les
|
||||
ecosystemes qui naitront demain.
|
||||
- **Les versions ne sont pas recopiees.** Le role lit les defauts des quatre roles
|
||||
consommateurs (`include_vars` SANS `name:` — enfermees dans un dictionnaire, les valeurs
|
||||
qui se citent entre elles ne se resolvent plus ; le devis des certificats avait deja paye
|
||||
cette marche).
|
||||
- Les quatre roles recoivent **une tache ajoutee, placee avant leur `stat` de cache**. Si
|
||||
le depot sert le fichier, le `stat` le voit et la tache de telechargement amont se saute
|
||||
d'elle-meme : aucune tache existante n'a change.
|
||||
- Les deux signatures detachees passent aussi par le depot. Elles pesent 228 octets, mais
|
||||
elles sont demandees a chaque passage : une sortie reste une sortie.
|
||||
- `setops_depot_binaires` est **derive**, jamais ecrit deux fois — de `artefacts_amorcage`
|
||||
chez le locataire, du plan du site dans `site_inventaire.py`.
|
||||
|
||||
### La garde, ecrite le meme jour que la liste
|
||||
|
||||
`P70` exige que tout `dest:` ecrit sous un `<role>_cache_local` figure au depot. Sans elle,
|
||||
une version montee dans un role sans l'etre dans le depot produirait exactement le defaut
|
||||
que ce depot traque : le runner ne trouve pas le fichier, retombe sur Internet, **et tout
|
||||
fonctionne** — sans que rien ne le dise.
|
||||
|
||||
Une liste qui suit une autre prend du retard. Celle-ci est nee avec son garde-fou.
|
||||
|
||||
### Une erreur corrigee en cours de route
|
||||
|
||||
La premiere version des gardes de signature s'appuyait sur `failed_when: false` puis testait
|
||||
`is not succeeded`. `failed_when: false` REECRIT le verdict : la tache n'est plus jamais
|
||||
`failed`, donc la garde etait toujours vraie et ne gardait rien — la faute exacte que
|
||||
`client_artefacts` documente depuis le 2026-08-31. Les gardes **mesurent le fichier**
|
||||
desormais, pas le verdict de la tache.
|
||||
|
||||
### Ce que ca ne couvre pas encore
|
||||
|
||||
Le cache du contrôleur reste per-runner : un runner tout neuf demande le depot, et si le
|
||||
depot ne l'a pas encore, il sort. Le depot se remplit au deploiement du site — donc un
|
||||
locataire monte avant que le site n'ait ete redeploye sortira une derniere fois.
|
||||
|
||||
|
||||
## 2026-09-12 (5) — Une reprise conditionnelle ne reprend rien
|
||||
|
||||
Cinq roles telechargeaient une cle de signature avec une boucle `until` exigeant une taille
|
||||
non nulle. Des que le fichier existe — meme a zero octet — `get_url` emet une requete
|
||||
CONDITIONNELLE ; l'amont repond `304 Not Modified` ; la tache reussit sans rien ecrire ; et
|
||||
la boucle retente cinq fois contre un serveur qui repondra toujours 304 :
|
||||
|
||||
```
|
||||
HTTP Error 304: Not Modified size: 0 attempts: 5
|
||||
```
|
||||
|
||||
La boucle de reprise se battait contre elle-meme. `force: true` sur les cinq
|
||||
(`client_pki`, `client_journal`, `serveur_collabora`, `serveur_grafana`, `serveur_loki`) —
|
||||
le `when:` qui precede garantit deja qu'on ne retelecharge pas une cle valide, donc `force`
|
||||
ne concerne que le cas ou l'on a DECIDE d'aller chercher.
|
||||
|
||||
|
||||
## 2026-09-12 (4) — Pools nommes comme les depots, et les cles sortent du poste
|
||||
|
||||
### Le nom d'un pool est celui de son depot
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
> [`docs/audit/affirmations.md`](affirmations.md).
|
||||
|
||||
- **Instance** : `/home/danallaire/Espace Chezlepro/DépôtsSurForge/Set-OPS-public/instance` — inventaire `/home/danallaire/Espace Chezlepro/DépôtsSurForge/Set-OPS-public/instance/inventories/principal/hosts.yml`
|
||||
- **Verdict** : ✅ CONFORME (68 OK · 0 echec · 1 saute)
|
||||
- **Verdict** : ✅ CONFORME (69 OK · 0 echec · 1 saute)
|
||||
|
||||
## Preuves
|
||||
|
||||
|
|
@ -44,7 +44,7 @@
|
|||
| P29 | Authentification : chaque role declare sa position | AFF-111 | ✅ OK | 33 role(s) serveur declares (interne-sans-auth 2, ldap-direct 2, sans-auth-humaine 22, socle-identite 2, web-sso 5) ; 2 lacune(s) nommee(s) : serveur_loki, serv |
|
||||
| P30 | SDN EVPN : zones, VNets et sous-reseaux derives | AFF-112 | ✅ OK | CONFORME : SDN EVPN, 3 zone(s), 15 VNet(s), 15 sous-reseau(x), aucune collision. |
|
||||
| P31 | Documentation : tout ce que le depot FAIT est nomme | — | ✅ OK | 62 scripts expliques et atteignables, 119 cibles make documentees, 68 roles avec README. |
|
||||
| P32 | Intrants exiges par les roles : tous fournis | — | ✅ OK | CONFORME : 38 exigence(s) de role, toutes satisfaites (138 cle(s) declaree(s) par l'instance). |
|
||||
| P32 | Intrants exiges par les roles : tous fournis | — | ✅ OK | CONFORME : 38 exigence(s) de role, toutes satisfaites (139 cle(s) declaree(s) par l'instance). |
|
||||
| P33 | Aucune collision de port entre roles co-localises | — | ✅ OK | CONFORME : 34 revendication(s) de port, aucune collision entre roles co-localises (36 groupes). |
|
||||
| P34 | Chaque document declare son lecteur | — | ✅ OK | 44 document(s) declarent leur lecteur (38 genere(s) exempte(s)). |
|
||||
| P35 | Toute application exigeant une base en a une au plan | — | ✅ OK | 5 application(s) exigeant une base l'ont toutes (4 entree(s) au registre). |
|
||||
|
|
@ -69,7 +69,7 @@
|
|||
| P54 | L'insemination ne reclame aucun secret du tenant | — | ✅ OK | 2 couche(s) d'insemination (serveur_debian, serveur_ops), 9 role(s) applique(s), aucun secret de tenant reclame. |
|
||||
| P55 | La cle du SITE ne nait que sur le runner d'un tenant | — | ✅ OK | 14 hote(s) : la cle du SITE ne nait que sur 1 runner(s) de tenant, celle du tenant sur 14. |
|
||||
| P56 | Gabarit minimal, et rien de retire n'est perdu | — | ✅ OK | Gabarit minimal : 4 role(s), tous indispensables au premier demarrage ; 14 role(s) retire(s), tous repris par le socle ou le durcissement. |
|
||||
| P57 | Comptes en prose : les chiffres du depot sur lui-meme | — | ✅ OK | Les comptes ecrits en prose correspondent a la mesure (69 preuves, 68 roles, 41 groupes). |
|
||||
| P57 | Comptes en prose : les chiffres du depot sur lui-meme | — | ✅ OK | Les comptes ecrits en prose correspondent a la mesure (70 preuves, 68 roles, 41 groupes). |
|
||||
| P58 | Habilitations : chaque service dit a quel GROUPE, et par quoi | — | ✅ OK | 8 habilitation(s) declarees, toutes nommant un groupe, un mecanisme connu et une raison ; les `role-realm` sont projetees. |
|
||||
| P59 | Enumerations annoncees : le nombre correspond a ce qui suit | — | ✅ OK | 2 enumeration(s) annoncee(s) correspondent a ce qu'elles annoncent (formes non ambigues seulement). |
|
||||
| P60 | Wiki publie : la forge sert ce que le depot dit | AFF-002 | ✅ OK | Le wiki publie correspond au depot : `wiki/` n'a pas bouge depuis `904ece3` (publie le 2026-09-12). |
|
||||
|
|
@ -82,6 +82,7 @@
|
|||
| P67 | Nom public : le service porte celui du plan, pas celui du role | — | ✅ OK | 6 service(s) expose(s) portent le nom du plan (6 groupe(s) derive(s)). |
|
||||
| P68 | Cle de depot telechargee : mesuree avant d'etre utilisee | — | ✅ OK | 5 role(s) telechargent une cle de depot, tous la mesurent avant de s'en servir. |
|
||||
| P69 | Amorcage d'un tenant : l'adresse designe le site REEL | — | ✅ OK | 2 adresse(s) d'amorcage designent bien une machine du site. |
|
||||
| P70 | Depot de binaires : il tient tout ce que les roles vont chercher | — | ✅ OK | 6 artefact(s) direct(s) tenus par le depot du site. |
|
||||
|
||||
## Couverture des affirmations ✅ du registre
|
||||
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ Ce que la reconstruction couvre, par capacité :
|
|||
| Plateforme webapp | `serveur_web_frontal`, `serveur_web_dorsal` | sites statiques et webapps natives (venv + systemd + nginx), **zéro conteneur** |
|
||||
| Sauvegardes | `serveur_backup`, `client_backup` | restic hors-nœud, **restauration éprouvée** (2026-08-12 : la donnée revient) |
|
||||
| Exploitation | `serveur_ops` | le poste depuis lequel l'ecosysteme se reconstruit : Ansible epingle, genome clone depuis **sa propre forge**, cle SSH propre — **sans** la voute ni son mot de passe |
|
||||
| Source d'artefacts | `serveur_artefacts`, `client_artefacts` | cache apt de l'ecosysteme (apt-cacher-ng) : les paquets viennent de chez soi, pas de six serveurs etrangers — **mode hors ligne** pour prouver ce que le cache detient vraiment |
|
||||
| Source d'artefacts | `serveur_artefacts`, `client_artefacts` | **deux faces sur un seul service.** Cache apt (apt-cacher-ng) : les paquets viennent de chez soi, pas de six serveurs etrangers — **mode hors ligne** pour prouver ce que le cache detient vraiment. Et **depot des binaires directs** (`LocalDirs`) : Forgejo, Keycloak, Nextcloud, oauth2-proxy ne vivent dans aucun depot apt et etaient tires d'Internet par CHAQUE runner — 570 Mo mesures le 2026-09-12. Meme port, meme regle de pare-feu, garde `P70` |
|
||||
| Runner de tenant | `serveur_ops_tenant` | le pouvoir de **configurer** : la voute de l'ecosysteme, deposee CHIFFREE sur son propre runner. Sans elle un runner calcule son inventaire et ne peut rien en faire — chaque role qui demande un secret echoue sur son assertion. N'atteint ni la fabric ni la frontiere |
|
||||
| Runner de site | `serveur_ops_site` | le pouvoir de **materialiser** : creer et detruire des VM sur la fabric. Detient la voute du SITE, chiffree, et n'entre JAMAIS chez un tenant — reserve a l'ecosysteme de l'hebergeur |
|
||||
| Resolution | `serveur_resolveur`, `client_resolveur` | UN resolveur recursif par tenant (Unbound), qui recurse depuis la racine et delegue la zone souveraine a PowerDNS. Remplace les N demons locaux d'avant le 2026-08-24 |
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ make placement-plan # chaque VM est-elle là où le plan la met
|
|||
|
||||
## Le trou qu'il comble
|
||||
|
||||
`scripts/prouver.py` porte 69 preuves (dont une conditionnelle, sautée sans la clé de la voûte). Elles sont toutes **statiques** : elles lisent le
|
||||
`scripts/prouver.py` porte 70 preuves (dont une conditionnelle, sautée sans la clé de la voûte). Elles sont toutes **statiques** : elles lisent le
|
||||
dépôt. Zéro appel réseau, zéro SSH, zéro `ansible`. Elles établissent que le dépôt est
|
||||
cohérent **avec lui-même** — que les handlers existent, que les intrants ont un
|
||||
propriétaire, que rien n'est codé en dur.
|
||||
|
|
|
|||
|
|
@ -60,7 +60,20 @@
|
|||
|
||||
- name: Telecharger la cle de signature Grafana
|
||||
when: not (telecharger_la_cle_de_signature_grafana_present.stat.exists and telecharger_la_cle_de_signature_grafana_present.stat.size | default(0) > 0)
|
||||
# `force` PARCE QU'UNE REPRISE CONDITIONNELLE NE REPREND RIEN (2026-09-12).
|
||||
#
|
||||
# Sans lui, `get_url` envoie un `If-Modified-Since` des que le fichier existe. Le cache
|
||||
# repond `304 Not Modified`, la tache reussit sans rien ecrire, et la boucle `until`
|
||||
# — qui exige une taille non nulle — retente cinq fois contre un serveur qui repondra
|
||||
# toujours 304 :
|
||||
#
|
||||
# HTTP Error 304: Not Modified size: 0 attempts: 5
|
||||
#
|
||||
# C'est la boucle de reprise qui se bat contre elle-meme. Le `when:` ci-dessus garantit
|
||||
# deja qu'on ne retelecharge pas une cle valide ; `force` ne concerne donc que le cas
|
||||
# ou l'on a DECIDE d'aller chercher.
|
||||
ansible.builtin.get_url:
|
||||
force: true
|
||||
url: "{{ client_journal_depot_cle_url }}"
|
||||
dest: "{{ client_journal_depot_cle_fichier }}"
|
||||
owner: root
|
||||
|
|
|
|||
|
|
@ -105,7 +105,20 @@
|
|||
|
||||
- name: Telecharger la cle de signature Smallstep
|
||||
when: not (telecharger_la_cle_de_signature_smallste_present.stat.exists and telecharger_la_cle_de_signature_smallste_present.stat.size | default(0) > 0)
|
||||
# `force` PARCE QU'UNE REPRISE CONDITIONNELLE NE REPREND RIEN (2026-09-12).
|
||||
#
|
||||
# Sans lui, `get_url` envoie un `If-Modified-Since` des que le fichier existe. Le cache
|
||||
# repond `304 Not Modified`, la tache reussit sans rien ecrire, et la boucle `until`
|
||||
# — qui exige une taille non nulle — retente cinq fois contre un serveur qui repondra
|
||||
# toujours 304 :
|
||||
#
|
||||
# HTTP Error 304: Not Modified size: 0 attempts: 5
|
||||
#
|
||||
# C'est la boucle de reprise qui se bat contre elle-meme. Le `when:` ci-dessus garantit
|
||||
# deja qu'on ne retelecharge pas une cle valide ; `force` ne concerne donc que le cas
|
||||
# ou l'on a DECIDE d'aller chercher.
|
||||
ansible.builtin.get_url:
|
||||
force: true
|
||||
url: "{{ client_pki_depot_cle_url }}"
|
||||
dest: "{{ client_pki_depot_cle_fichier }}"
|
||||
owner: root
|
||||
|
|
|
|||
|
|
@ -111,3 +111,66 @@ serveur_artefacts_remaps:
|
|||
# tomber tout le reste. Sans cette ligne, le remap serait vrai a moitie : les paquets
|
||||
# passeraient par le cache, et chaque machine sortirait quand meme chercher sa cle.
|
||||
serveur_artefacts_motif_fichiers_sup: '.*\.(key|asc)$'
|
||||
|
||||
# --- LE DEPOT DES BINAIRES DIRECTS (2026-09-12) --------------------------------------
|
||||
#
|
||||
# LE CACHE APT NE COUVRAIT QUE apt. Quatre artefacts arrivent autrement — Forgejo,
|
||||
# Keycloak, Nextcloud, oauth2-proxy — parce qu'ils ne vivent dans aucun depot apt : ce
|
||||
# sont des archives de publication, tirees par le CONTROLEUR (`delegate_to: localhost`)
|
||||
# puis poussees par SSH. Le role le disait deja : « deux mecanismes, parce que ce sont
|
||||
# deux problemes ». Ce qui manquait, c'est que le second n'avait pas de source locale.
|
||||
#
|
||||
# CE QUE CA COUTAIT, MESURE LE 2026-09-12 : `/opt/setops/.cache/setops` est ABSENT sur le
|
||||
# runner du site. Un second locataire monte depuis ce runner sortait donc chercher 570 Mo
|
||||
# sur codeberg.org, github.com (deux fois) et download.nextcloud.com — alors que le meme
|
||||
# ecosysteme ne demandait plus un seul paquet a Debian.
|
||||
#
|
||||
# POURQUOI PAS UN RELAIS TRANSPARENT, comme les `Remap-*` ci-dessus. Mesure des quatre
|
||||
# amonts le 2026-09-12 :
|
||||
#
|
||||
# codeberg.org 200, 0 redirection -> relayable
|
||||
# download.nextcloud.com 200, 0 redirection -> relayable
|
||||
# github.com 302 -> release-assets.githubusercontent.com
|
||||
# avec signature et jeton VALABLES UNE HEURE
|
||||
#
|
||||
# L'URL finale de GitHub change a chaque requete. Un cache qui la prend pour cle ne fait
|
||||
# jamais mouche, et ce qu'il garderait serait perime avant d'etre relu. Le relais
|
||||
# transparent marche pour deux amonts sur quatre : ce n'est pas un mecanisme, c'est une
|
||||
# coincidence. On tient donc un VRAI depot de fichiers, rempli une fois, servi toujours.
|
||||
#
|
||||
# ET IL N'A DEMANDE NI SERVICE NI PORT NI CERTIFICAT NI FLUX. `LocalDirs` d'apt-cacher-ng
|
||||
# publie un repertoire du disque sous un prefixe — eprouve sur site-cache-01 avant
|
||||
# d'ecrire ce rôle : 200, 90 158 octets, identiques a l'octet. L'`ingress 3142 pair:
|
||||
# flotte` deja declare couvre exactement ce chemin.
|
||||
serveur_artefacts_directs_actif: true
|
||||
serveur_artefacts_directs_dir: "/var/lib/setops/artefacts-directs"
|
||||
serveur_artefacts_directs_prefixe: "setops-binaires"
|
||||
|
||||
# LES ROLES DONT ON LIT LES DEFAUTS POUR SAVOIR QUOI TENIR.
|
||||
#
|
||||
# La version et l'URL amont sont declarees UNE SEULE FOIS, dans le role qui consomme
|
||||
# l'artefact. Les redeclarer ici ferait exactement ce que ce depot combat partout
|
||||
# ailleurs : deux sources du meme fait, qui divergent en silence — et la divergence se
|
||||
# lirait « le depot sert un fichier », pas « il sert le mauvais ».
|
||||
serveur_artefacts_directs_roles:
|
||||
- serveur_forgejo
|
||||
- serveur_keycloak
|
||||
- serveur_nextcloud
|
||||
- serveur_oauth2_proxy
|
||||
|
||||
# LES NOMS SONT CEUX QUE LE CACHE DU CONTROLEUR PORTE — pas des noms choisis ici.
|
||||
# Un nom different obligerait chaque role a traduire, et la traduction serait le
|
||||
# troisieme endroit ou la version pourrait se perimer.
|
||||
serveur_artefacts_directs:
|
||||
- nom: "forgejo-{{ serveur_forgejo_version }}"
|
||||
url: "{{ serveur_forgejo_url }}"
|
||||
- nom: "forgejo-{{ serveur_forgejo_version }}.asc"
|
||||
url: "{{ serveur_forgejo_signature_url }}"
|
||||
- nom: "keycloak-{{ serveur_keycloak_version }}.tar.gz"
|
||||
url: "{{ serveur_keycloak_url }}"
|
||||
- nom: "keycloak-{{ serveur_keycloak_version }}.tar.gz.asc"
|
||||
url: "{{ serveur_keycloak_signature_url }}"
|
||||
- nom: "nextcloud-{{ serveur_nextcloud_version }}.tar.bz2"
|
||||
url: "{{ serveur_nextcloud_url }}"
|
||||
- nom: "oauth2-proxy-{{ serveur_oauth2_proxy_version }}.tar.gz"
|
||||
url: "{{ serveur_oauth2_proxy_url }}"
|
||||
|
|
|
|||
|
|
@ -90,6 +90,38 @@
|
|||
- serveur_artefacts_amont | length > 0
|
||||
- not ansible_check_mode
|
||||
|
||||
# LE REPERTOIRE AVANT LA DIRECTIVE QUI LE PUBLIE. `LocalDirs` pointant sur un chemin
|
||||
# absent, apt-cacher-ng ne refuse pas de demarrer : il rend 503 sur ce prefixe, et le
|
||||
# message parle de « File or directory not found » — ce qui accuse le fichier demande,
|
||||
# pas la racine manquante.
|
||||
# LE PARENT AUSSI, ET C'EST LUI QUI A MORDU (mesure du 2026-09-12).
|
||||
#
|
||||
# `file: state=directory` cree les parents manquants avec les droits par defaut — ici
|
||||
# `0750 root:root`. apt-cacher-ng ne tourne PAS en root : il n'a pas pu traverser
|
||||
# `/var/lib/setops` et rendait 403 sur chaque fichier du depot, alors que le repertoire
|
||||
# final etait bien en 0755 et les fichiers bien la.
|
||||
#
|
||||
# 403 ACCUSE LE FICHIER DEMANDE, pas le repertoire qui le contient — et encore moins le
|
||||
# GRAND-PARENT. On a donc cherche du cote des motifs de fichiers d'apt-cacher-ng, qui
|
||||
# n'y etaient pour rien. Un chemin se traverse en entier ; un droit se verifie de meme.
|
||||
- name: Assurer la racine d'état de Set-OPS, traversable par les services
|
||||
ansible.builtin.file:
|
||||
path: "{{ serveur_artefacts_directs_dir | dirname }}"
|
||||
state: directory
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0755"
|
||||
when: serveur_artefacts_directs_actif | bool
|
||||
|
||||
- name: Assurer le répertoire du dépôt de binaires directs
|
||||
ansible.builtin.file:
|
||||
path: "{{ serveur_artefacts_directs_dir }}"
|
||||
state: directory
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0755"
|
||||
when: serveur_artefacts_directs_actif | bool
|
||||
|
||||
- name: Déployer la configuration Set-OPS
|
||||
ansible.builtin.template:
|
||||
src: zzz-setops.conf.j2
|
||||
|
|
@ -145,3 +177,70 @@
|
|||
owner: root
|
||||
group: root
|
||||
mode: "0750"
|
||||
|
||||
# --- REMPLIR LE DEPOT DE BINAIRES DIRECTS --------------------------------------------
|
||||
#
|
||||
# CETTE MACHINE EST LA SEULE A SORTIR, et c'est le meme principe que pour apt : un seul
|
||||
# telechargement pour tout le site, y compris pour les ecosystemes qui naitront demain.
|
||||
#
|
||||
# ON LIT LES DEFAUTS DES ROLES CONSOMMATEURS, on ne recopie pas leurs versions. Sans ca,
|
||||
# la version vivrait a deux endroits et le depot finirait par servir, en silence, un
|
||||
# artefact que plus personne ne demande.
|
||||
#
|
||||
# `include_vars` SANS `name:` — DELIBEREMENT. Enfermees dans un dictionnaire, les valeurs
|
||||
# qui se citent entre elles ne se resolvent plus : `serveur_forgejo_url` reference
|
||||
# `serveur_forgejo_version`. Le depot connait deja cette marche, payee sur le devis des
|
||||
# certificats ; on ne la repaye pas.
|
||||
- name: Lire ce que les rôles consommateurs déclarent télécharger
|
||||
ansible.builtin.include_vars:
|
||||
dir: "{{ role_path }}/../{{ item }}/defaults"
|
||||
loop: "{{ serveur_artefacts_directs_roles }}"
|
||||
loop_control:
|
||||
label: "{{ item }}"
|
||||
when: serveur_artefacts_directs_actif | bool
|
||||
|
||||
# `force: false` ET UN `dest` NOMME PAR LA VERSION : une publication ne change pas sous
|
||||
# le meme nom, donc un fichier deja present est le bon. Une nouvelle version porte un
|
||||
# autre nom et se telecharge d'elle-meme — le depot se met a jour en suivant les roles,
|
||||
# sans qu'on ait rien a purger.
|
||||
#
|
||||
# LES REPRISES SONT OBLIGATOIRES, PAS PRUDENTIELLES. `packages.smallstep.com` a deja fait
|
||||
# echouer un deploiement de quarante minutes en pendant au premier essai et en repondant
|
||||
# en 0,48 s au second ; codeberg.org n'a aucune raison d'etre plus fiable.
|
||||
- name: Tenir les binaires directs à la disposition des écosystèmes
|
||||
ansible.builtin.get_url:
|
||||
url: "{{ item.url }}"
|
||||
dest: "{{ serveur_artefacts_directs_dir }}/{{ item.nom }}"
|
||||
mode: "0644"
|
||||
force: false
|
||||
timeout: 60
|
||||
loop: "{{ serveur_artefacts_directs }}"
|
||||
loop_control:
|
||||
label: "{{ item.nom }}"
|
||||
register: serveur_artefacts_directs_tires
|
||||
retries: 5
|
||||
delay: 6
|
||||
until: serveur_artefacts_directs_tires is succeeded
|
||||
# UN AMONT MUET NE DOIT PAS ARRETER LE SITE. Ce depot est une COMMODITE : sans lui,
|
||||
# chaque runner sort lui-meme, comme avant. Le faire echouer rendrait le site
|
||||
# indeployable le jour ou codeberg.org tousse — on aurait echange une sortie Internet
|
||||
# contre une dependance a Internet, ce qui est exactement l'inverse du but.
|
||||
failed_when: false
|
||||
when:
|
||||
- serveur_artefacts_directs_actif | bool
|
||||
- not ansible_check_mode
|
||||
|
||||
# ON LE DIT, FORT. Un depot a moitie rempli sert a moitie, et le runner qui complete en
|
||||
# sortant sur Internet le fait sans que rien ne le signale.
|
||||
- name: Dire ce que le dépôt de binaires ne tient PAS
|
||||
ansible.builtin.debug:
|
||||
msg: >-
|
||||
{{ (serveur_artefacts_directs_tires.results | default([])
|
||||
| selectattr('failed', 'defined') | selectattr('failed')
|
||||
| map(attribute='item.nom') | list) | join(', ') }}
|
||||
— absent(s) du depot. Les runners qui en ont besoin sortiront sur Internet.
|
||||
when:
|
||||
- serveur_artefacts_directs_actif | bool
|
||||
- not ansible_check_mode
|
||||
- (serveur_artefacts_directs_tires.results | default([])
|
||||
| selectattr('failed', 'defined') | selectattr('failed') | list | length) > 0
|
||||
|
|
|
|||
|
|
@ -50,3 +50,16 @@ Remap-{{ r.nom }}: http://{{ r.hote }} ; https://{{ r.hote }}/
|
|||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% if serveur_artefacts_directs_actif | bool %}
|
||||
# --- LE DEPOT DES BINAIRES DIRECTS ---------------------------------------------------
|
||||
#
|
||||
# `LocalDirs` publie un repertoire du disque sous un prefixe. C'est la seule facon de
|
||||
# servir Forgejo, Keycloak, Nextcloud et oauth2-proxy sans monter un second service :
|
||||
# ils ne vivent dans aucun depot apt, et GitHub redirige vers une URL signee valable une
|
||||
# heure — qu'aucun relais transparent ne peut mettre en cache (voir defaults/main.yml).
|
||||
#
|
||||
# CE CHEMIN EST DEJA COUVERT PAR LE PARE-FEU : c'est le meme port 3142, la meme regle
|
||||
# `ingress pair: flotte`. Rien de neuf a ouvrir.
|
||||
LocalDirs: {{ serveur_artefacts_directs_prefixe }} {{ serveur_artefacts_directs_dir }}
|
||||
{% endif %}
|
||||
|
|
|
|||
|
|
@ -73,7 +73,20 @@
|
|||
|
||||
- name: Telecharger la cle de signature Collabora
|
||||
when: not (serveur_collabora_cle_presente.stat.exists and serveur_collabora_cle_presente.stat.size | default(0) > 0)
|
||||
# `force` PARCE QU'UNE REPRISE CONDITIONNELLE NE REPREND RIEN (2026-09-12).
|
||||
#
|
||||
# Sans lui, `get_url` envoie un `If-Modified-Since` des que le fichier existe. Le cache
|
||||
# repond `304 Not Modified`, la tache reussit sans rien ecrire, et la boucle `until`
|
||||
# — qui exige une taille non nulle — retente cinq fois contre un serveur qui repondra
|
||||
# toujours 304 :
|
||||
#
|
||||
# HTTP Error 304: Not Modified size: 0 attempts: 5
|
||||
#
|
||||
# C'est la boucle de reprise qui se bat contre elle-meme. Le `when:` ci-dessus garantit
|
||||
# deja qu'on ne retelecharge pas une cle valide ; `force` ne concerne donc que le cas
|
||||
# ou l'on a DECIDE d'aller chercher.
|
||||
ansible.builtin.get_url:
|
||||
force: true
|
||||
url: "{{ serveur_collabora_depot_cle_url }}"
|
||||
dest: "{{ serveur_collabora_depot_cle_fichier }}"
|
||||
owner: root
|
||||
|
|
|
|||
|
|
@ -131,6 +131,31 @@
|
|||
become: false
|
||||
when: not serveur_forgejo_binaire_present.stat.exists
|
||||
|
||||
# --- LE DEPOT DU SITE D'ABORD, L'INTERNET ENSUITE (2026-09-12) ----------------
|
||||
#
|
||||
# Le site tient ces artefacts dans son cache (`serveur_artefacts`, LocalDirs). Un runner
|
||||
# qui les y trouve ne sort pas du tout : c'est le meme principe que le cache apt, applique
|
||||
# aux publications qui ne vivent dans aucun depot apt.
|
||||
#
|
||||
# PLACEE AVANT LE `stat` DU CACHE DU CONTROLEUR, ET C'EST TOUT LE MONTAGE : si le depot
|
||||
# sert le fichier, le `stat` qui suit le voit, et la tache de telechargement amont se
|
||||
# saute d'elle-meme. Aucune tache existante n'a change.
|
||||
#
|
||||
# `failed_when: false` — le depot est une COMMODITE. Site sans depot, depot qui n'a pas
|
||||
# encore cette version, cache du site tout neuf : on retombe sur l'amont, comme avant.
|
||||
- name: Prendre le binaire Forgejo au dépôt du site, avant de sortir sur Internet
|
||||
ansible.builtin.get_url:
|
||||
url: "{{ setops_depot_binaires }}/forgejo-{{ serveur_forgejo_version }}"
|
||||
dest: "{{ serveur_forgejo_cache_local }}/forgejo-{{ serveur_forgejo_version }}"
|
||||
mode: "0644"
|
||||
timeout: 30
|
||||
delegate_to: localhost
|
||||
become: false
|
||||
failed_when: false
|
||||
when:
|
||||
- setops_depot_binaires | default('') | length > 0
|
||||
- not serveur_forgejo_binaire_present.stat.exists
|
||||
|
||||
- name: L artefact est-il déjà dans le cache du contrôleur ?
|
||||
ansible.builtin.stat:
|
||||
path: "{{ serveur_forgejo_cache_local }}/forgejo-{{ serveur_forgejo_version }}"
|
||||
|
|
@ -167,6 +192,35 @@
|
|||
# Un serveur tiers intermittent ne doit pas arreter un deploiement de quarante minutes
|
||||
# (mesure du 2026-08-23 : la meme URL pend, puis rend 200 en 0,48 s au second essai).
|
||||
# Defaut de `get_url` : 10 s et aucune reprise.
|
||||
# LA SIGNATURE AUSSI VIENT DU DEPOT QUAND IL L'A (2026-09-12). Elle pese 228 octets,
|
||||
# mais elle est demandee a CHAQUE passage ou la cible n'a pas encore le binaire : sans
|
||||
# elle ici, un ecosysteme entierement servi par le site sortirait quand meme sur Internet
|
||||
# pour un quart de kilo-octet. Une sortie reste une sortie.
|
||||
- name: Prendre la signature au dépôt du site, avant de sortir sur Internet
|
||||
ansible.builtin.get_url:
|
||||
url: "{{ setops_depot_binaires }}/forgejo-{{ serveur_forgejo_version }}.asc"
|
||||
dest: "{{ serveur_forgejo_cache_local }}/forgejo-{{ serveur_forgejo_version }}.asc"
|
||||
mode: "0644"
|
||||
timeout: 30
|
||||
force: true
|
||||
delegate_to: localhost
|
||||
become: false
|
||||
failed_when: false
|
||||
when:
|
||||
- setops_depot_binaires | default('') | length > 0
|
||||
- not serveur_forgejo_binaire_present.stat.exists
|
||||
|
||||
# ON MESURE LE FICHIER, PAS LE VERDICT DE LA TACHE. `failed_when: false` REECRIT le
|
||||
# verdict : la tache n'est plus jamais `failed`, donc une garde posee dessus est toujours
|
||||
# vraie et ne garde rien. Le depot connait cette marche — elle a deja declare « joignable »
|
||||
# un amont mesure muet la seconde d'avant. Ce qui compte ici, c'est qu'un fichier soit la.
|
||||
- name: La signature est-elle arrivée dans le cache du contrôleur ?
|
||||
ansible.builtin.stat:
|
||||
path: "{{ serveur_forgejo_cache_local }}/forgejo-{{ serveur_forgejo_version }}.asc"
|
||||
register: serveur_forgejo_asc_present
|
||||
delegate_to: localhost
|
||||
become: false
|
||||
|
||||
- name: Récupérer la signature détachée du binaire
|
||||
ansible.builtin.get_url:
|
||||
url: "{{ serveur_forgejo_signature_url }}"
|
||||
|
|
@ -176,7 +230,10 @@
|
|||
force: true
|
||||
delegate_to: localhost
|
||||
become: false
|
||||
when: not serveur_forgejo_binaire_present.stat.exists
|
||||
when:
|
||||
- not serveur_forgejo_binaire_present.stat.exists
|
||||
# Le depot a servi : ne pas ressortir pour ecraser ce qu'on a deja.
|
||||
- not (serveur_forgejo_asc_present.stat.exists and (serveur_forgejo_asc_present.stat.size | default(0)) > 0)
|
||||
register: serveur_forgejo_sig
|
||||
retries: 5
|
||||
delay: 6
|
||||
|
|
|
|||
|
|
@ -92,7 +92,20 @@
|
|||
|
||||
- name: Telecharger la cle de signature Grafana
|
||||
when: not (telecharger_la_cle_de_signature_grafana_present.stat.exists and telecharger_la_cle_de_signature_grafana_present.stat.size | default(0) > 0)
|
||||
# `force` PARCE QU'UNE REPRISE CONDITIONNELLE NE REPREND RIEN (2026-09-12).
|
||||
#
|
||||
# Sans lui, `get_url` envoie un `If-Modified-Since` des que le fichier existe. Le cache
|
||||
# repond `304 Not Modified`, la tache reussit sans rien ecrire, et la boucle `until`
|
||||
# — qui exige une taille non nulle — retente cinq fois contre un serveur qui repondra
|
||||
# toujours 304 :
|
||||
#
|
||||
# HTTP Error 304: Not Modified size: 0 attempts: 5
|
||||
#
|
||||
# C'est la boucle de reprise qui se bat contre elle-meme. Le `when:` ci-dessus garantit
|
||||
# deja qu'on ne retelecharge pas une cle valide ; `force` ne concerne donc que le cas
|
||||
# ou l'on a DECIDE d'aller chercher.
|
||||
ansible.builtin.get_url:
|
||||
force: true
|
||||
url: "{{ serveur_grafana_depot_cle_url }}"
|
||||
dest: "{{ serveur_grafana_depot_cle_fichier }}"
|
||||
owner: root
|
||||
|
|
|
|||
|
|
@ -96,6 +96,31 @@
|
|||
become: false
|
||||
when: not serveur_keycloak_archive_present.stat.exists
|
||||
|
||||
# --- LE DEPOT DU SITE D'ABORD, L'INTERNET ENSUITE (2026-09-12) ----------------
|
||||
#
|
||||
# Le site tient ces artefacts dans son cache (`serveur_artefacts`, LocalDirs). Un runner
|
||||
# qui les y trouve ne sort pas du tout : c'est le meme principe que le cache apt, applique
|
||||
# aux publications qui ne vivent dans aucun depot apt.
|
||||
#
|
||||
# PLACEE AVANT LE `stat` DU CACHE DU CONTROLEUR, ET C'EST TOUT LE MONTAGE : si le depot
|
||||
# sert le fichier, le `stat` qui suit le voit, et la tache de telechargement amont se
|
||||
# saute d'elle-meme. Aucune tache existante n'a change.
|
||||
#
|
||||
# `failed_when: false` — le depot est une COMMODITE. Site sans depot, depot qui n'a pas
|
||||
# encore cette version, cache du site tout neuf : on retombe sur l'amont, comme avant.
|
||||
- name: Prendre l'archive Keycloak au dépôt du site, avant de sortir sur Internet
|
||||
ansible.builtin.get_url:
|
||||
url: "{{ setops_depot_binaires }}/keycloak-{{ serveur_keycloak_version }}.tar.gz"
|
||||
dest: "{{ serveur_keycloak_cache_local }}/keycloak-{{ serveur_keycloak_version }}.tar.gz"
|
||||
mode: "0644"
|
||||
timeout: 30
|
||||
delegate_to: localhost
|
||||
become: false
|
||||
failed_when: false
|
||||
when:
|
||||
- setops_depot_binaires | default('') | length > 0
|
||||
- not serveur_keycloak_archive_present.stat.exists
|
||||
|
||||
- name: L artefact est-il déjà dans le cache du contrôleur ?
|
||||
ansible.builtin.stat:
|
||||
path: "{{ serveur_keycloak_cache_local }}/keycloak-{{ serveur_keycloak_version }}.tar.gz"
|
||||
|
|
@ -125,6 +150,35 @@
|
|||
# l'empreinte epinglee. Elle ne consulte aucun serveur de cles : un serveur de cles n'est
|
||||
# pas une autorite, et en dependre au moment du deploiement rendrait la garde aussi
|
||||
# fragile que ce qu'elle protege.
|
||||
# LA SIGNATURE AUSSI VIENT DU DEPOT QUAND IL L'A (2026-09-12). Elle pese 228 octets,
|
||||
# mais elle est demandee a CHAQUE passage ou la cible n'a pas encore le binaire : sans
|
||||
# elle ici, un ecosysteme entierement servi par le site sortirait quand meme sur Internet
|
||||
# pour un quart de kilo-octet. Une sortie reste une sortie.
|
||||
- name: Prendre la signature au dépôt du site, avant de sortir sur Internet
|
||||
ansible.builtin.get_url:
|
||||
url: "{{ setops_depot_binaires }}/keycloak-{{ serveur_keycloak_version }}.tar.gz.asc"
|
||||
dest: "{{ serveur_keycloak_cache_local }}/keycloak-{{ serveur_keycloak_version }}.tar.gz.asc"
|
||||
mode: "0644"
|
||||
timeout: 30
|
||||
force: true
|
||||
delegate_to: localhost
|
||||
become: false
|
||||
failed_when: false
|
||||
when:
|
||||
- setops_depot_binaires | default('') | length > 0
|
||||
- not serveur_keycloak_archive_present.stat.exists
|
||||
|
||||
# ON MESURE LE FICHIER, PAS LE VERDICT DE LA TACHE. `failed_when: false` REECRIT le
|
||||
# verdict : la tache n'est plus jamais `failed`, donc une garde posee dessus est toujours
|
||||
# vraie et ne garde rien. Le depot connait cette marche — elle a deja declare « joignable »
|
||||
# un amont mesure muet la seconde d'avant. Ce qui compte ici, c'est qu'un fichier soit la.
|
||||
- name: La signature est-elle arrivée dans le cache du contrôleur ?
|
||||
ansible.builtin.stat:
|
||||
path: "{{ serveur_keycloak_cache_local }}/keycloak-{{ serveur_keycloak_version }}.tar.gz.asc"
|
||||
register: serveur_keycloak_asc_present
|
||||
delegate_to: localhost
|
||||
become: false
|
||||
|
||||
- name: Récupérer la signature détachée de l archive
|
||||
ansible.builtin.get_url:
|
||||
url: "{{ serveur_keycloak_signature_url }}"
|
||||
|
|
@ -133,7 +187,10 @@
|
|||
force: true
|
||||
delegate_to: localhost
|
||||
become: false
|
||||
when: not serveur_keycloak_archive_present.stat.exists
|
||||
when:
|
||||
- not serveur_keycloak_archive_present.stat.exists
|
||||
# Le depot a servi : ne pas ressortir pour ecraser ce qu'on a deja.
|
||||
- not (serveur_keycloak_asc_present.stat.exists and (serveur_keycloak_asc_present.stat.size | default(0)) > 0)
|
||||
|
||||
- name: Vérifier la signature de l archive contre l empreinte épinglée
|
||||
ansible.builtin.command:
|
||||
|
|
|
|||
|
|
@ -60,7 +60,20 @@
|
|||
|
||||
- name: Telecharger la cle de signature Grafana
|
||||
when: not (telecharger_la_cle_de_signature_grafana_present.stat.exists and telecharger_la_cle_de_signature_grafana_present.stat.size | default(0) > 0)
|
||||
# `force` PARCE QU'UNE REPRISE CONDITIONNELLE NE REPREND RIEN (2026-09-12).
|
||||
#
|
||||
# Sans lui, `get_url` envoie un `If-Modified-Since` des que le fichier existe. Le cache
|
||||
# repond `304 Not Modified`, la tache reussit sans rien ecrire, et la boucle `until`
|
||||
# — qui exige une taille non nulle — retente cinq fois contre un serveur qui repondra
|
||||
# toujours 304 :
|
||||
#
|
||||
# HTTP Error 304: Not Modified size: 0 attempts: 5
|
||||
#
|
||||
# C'est la boucle de reprise qui se bat contre elle-meme. Le `when:` ci-dessus garantit
|
||||
# deja qu'on ne retelecharge pas une cle valide ; `force` ne concerne donc que le cas
|
||||
# ou l'on a DECIDE d'aller chercher.
|
||||
ansible.builtin.get_url:
|
||||
force: true
|
||||
url: "{{ serveur_loki_depot_cle_url }}"
|
||||
dest: "{{ serveur_loki_depot_cle_fichier }}"
|
||||
owner: root
|
||||
|
|
|
|||
|
|
@ -33,6 +33,31 @@
|
|||
become: false
|
||||
when: not serveur_nextcloud_archive_present.stat.exists
|
||||
|
||||
# --- LE DEPOT DU SITE D'ABORD, L'INTERNET ENSUITE (2026-09-12) ----------------
|
||||
#
|
||||
# Le site tient ces artefacts dans son cache (`serveur_artefacts`, LocalDirs). Un runner
|
||||
# qui les y trouve ne sort pas du tout : c'est le meme principe que le cache apt, applique
|
||||
# aux publications qui ne vivent dans aucun depot apt.
|
||||
#
|
||||
# PLACEE AVANT LE `stat` DU CACHE DU CONTROLEUR, ET C'EST TOUT LE MONTAGE : si le depot
|
||||
# sert le fichier, le `stat` qui suit le voit, et la tache de telechargement amont se
|
||||
# saute d'elle-meme. Aucune tache existante n'a change.
|
||||
#
|
||||
# `failed_when: false` — le depot est une COMMODITE. Site sans depot, depot qui n'a pas
|
||||
# encore cette version, cache du site tout neuf : on retombe sur l'amont, comme avant.
|
||||
- name: Prendre l'archive Nextcloud au dépôt du site, avant de sortir sur Internet
|
||||
ansible.builtin.get_url:
|
||||
url: "{{ setops_depot_binaires }}/nextcloud-{{ serveur_nextcloud_version }}.tar.bz2"
|
||||
dest: "{{ serveur_nextcloud_cache_local }}/nextcloud-{{ serveur_nextcloud_version }}.tar.bz2"
|
||||
mode: "0644"
|
||||
timeout: 30
|
||||
delegate_to: localhost
|
||||
become: false
|
||||
failed_when: false
|
||||
when:
|
||||
- setops_depot_binaires | default('') | length > 0
|
||||
- not serveur_nextcloud_archive_present.stat.exists
|
||||
|
||||
- name: L artefact est-il déjà dans le cache du contrôleur ?
|
||||
ansible.builtin.stat:
|
||||
path: "{{ serveur_nextcloud_cache_local }}/nextcloud-{{ serveur_nextcloud_version }}.tar.bz2"
|
||||
|
|
|
|||
|
|
@ -74,6 +74,31 @@
|
|||
become: false
|
||||
when: not serveur_oauth2_proxy_archive_present.stat.exists
|
||||
|
||||
# --- LE DEPOT DU SITE D'ABORD, L'INTERNET ENSUITE (2026-09-12) ----------------
|
||||
#
|
||||
# Le site tient ces artefacts dans son cache (`serveur_artefacts`, LocalDirs). Un runner
|
||||
# qui les y trouve ne sort pas du tout : c'est le meme principe que le cache apt, applique
|
||||
# aux publications qui ne vivent dans aucun depot apt.
|
||||
#
|
||||
# PLACEE AVANT LE `stat` DU CACHE DU CONTROLEUR, ET C'EST TOUT LE MONTAGE : si le depot
|
||||
# sert le fichier, le `stat` qui suit le voit, et la tache de telechargement amont se
|
||||
# saute d'elle-meme. Aucune tache existante n'a change.
|
||||
#
|
||||
# `failed_when: false` — le depot est une COMMODITE. Site sans depot, depot qui n'a pas
|
||||
# encore cette version, cache du site tout neuf : on retombe sur l'amont, comme avant.
|
||||
- name: Prendre l'archive oauth2-proxy au dépôt du site, avant de sortir sur Internet
|
||||
ansible.builtin.get_url:
|
||||
url: "{{ setops_depot_binaires }}/oauth2-proxy-{{ serveur_oauth2_proxy_version }}.tar.gz"
|
||||
dest: "{{ serveur_oauth2_proxy_cache_local }}/oauth2-proxy-{{ serveur_oauth2_proxy_version }}.tar.gz"
|
||||
mode: "0644"
|
||||
timeout: 30
|
||||
delegate_to: localhost
|
||||
become: false
|
||||
failed_when: false
|
||||
when:
|
||||
- setops_depot_binaires | default('') | length > 0
|
||||
- not serveur_oauth2_proxy_archive_present.stat.exists
|
||||
|
||||
- name: L artefact est-il déjà dans le cache du contrôleur ?
|
||||
ansible.builtin.stat:
|
||||
path: "{{ serveur_oauth2_proxy_cache_local }}/oauth2-proxy-{{ serveur_oauth2_proxy_version }}.tar.gz"
|
||||
|
|
|
|||
|
|
@ -3278,6 +3278,47 @@ def preuve_cle_telechargee_non_vide() -> tuple[bool, str]:
|
|||
f"avant de s'en servir.")
|
||||
|
||||
|
||||
def preuve_depot_binaires_complet() -> tuple[bool, str]:
|
||||
"""Le depot du site tient-il TOUT ce que les roles vont chercher sur Internet ?
|
||||
|
||||
UNE LISTE QUI SUIT UNE AUTRE PREND DU RETARD, et celle-ci a ete ecrite le meme jour
|
||||
que la garde — deliberement. Le depot des binaires directs (`serveur_artefacts_directs`)
|
||||
enumere ce que le cache du site tient ; les roles consommateurs, eux, declarent ce
|
||||
qu'ils telechargent. Deux listes, un seul fait.
|
||||
|
||||
CE QUE LE RETARD PRODUIRAIT : une version montee dans `serveur_forgejo_version` sans
|
||||
l'etre dans le depot. Le runner ne trouve pas le fichier, retombe sur codeberg.org,
|
||||
et tout fonctionne — en sortant sur Internet, sans que rien ne le dise. C'est
|
||||
exactement la famille de defaut que ce depot traque : du correct qui ment.
|
||||
|
||||
LA MESURE EST TEXTUELLE, ET C'EST SUFFISANT. On releve, dans les taches des roles,
|
||||
chaque `dest:` ecrit sous `<role>_cache_local`, et on exige que son nom de fichier
|
||||
figure au depot. Rendre Jinja n'apporterait rien : c'est le NOM qui doit correspondre,
|
||||
et il est ecrit des deux cotes dans la meme forme.
|
||||
"""
|
||||
import re as _re
|
||||
roles_dir = RACINE / "roles"
|
||||
defauts = (roles_dir / "serveur_artefacts" / "defaults" / "main.yml").read_text(encoding="utf-8")
|
||||
tenus = set(_re.findall(r'^\s*-\s*nom:\s*"([^"]+)"', defauts, _re.M))
|
||||
if not tenus:
|
||||
return False, "Le depot de binaires ne declare aucun artefact (serveur_artefacts_directs vide)."
|
||||
|
||||
attendus: dict[str, str] = {}
|
||||
for role in sorted(p.name for p in roles_dir.iterdir() if p.is_dir()):
|
||||
for tf in sorted((roles_dir / role / "tasks").glob("*.yml")) if (roles_dir / role / "tasks").is_dir() else []:
|
||||
texte = tf.read_text(encoding="utf-8")
|
||||
for dest in _re.findall(r'^\s*dest:\s*"\{\{\s*\w+_cache_local\s*\}\}/([^"]+)"', texte, _re.M):
|
||||
attendus[dest] = f"{role}/tasks/{tf.name}"
|
||||
|
||||
manquants = sorted(f"{nom} ({ou})" for nom, ou in attendus.items() if nom not in tenus)
|
||||
if manquants:
|
||||
return False, ("Telecharges par un role mais ABSENTS du depot du site — "
|
||||
"chaque runner sortira les chercher : " + " ; ".join(manquants))
|
||||
orphelins = sorted(tenus - set(attendus))
|
||||
detail = f" ({len(orphelins)} tenu(s) que plus aucun role ne demande : {', '.join(orphelins)})" if orphelins else ""
|
||||
return True, f"{len(attendus)} artefact(s) direct(s) tenus par le depot du site{detail}."
|
||||
|
||||
|
||||
def preuve_amorcage_suit_le_site() -> tuple[bool, str]:
|
||||
"""Les adresses d'amorcage d'un tenant designent-elles le site REEL ?
|
||||
|
||||
|
|
@ -3501,6 +3542,8 @@ PREUVES: list[dict] = [
|
|||
"refs": [], "func": preuve_cle_telechargee_non_vide},
|
||||
{"id": "P69", "titre": "Amorcage d'un tenant : l'adresse designe le site REEL",
|
||||
"refs": [], "func": preuve_amorcage_suit_le_site},
|
||||
{"id": "P70", "titre": "Depot de binaires : il tient tout ce que les roles vont chercher",
|
||||
"refs": [], "func": preuve_depot_binaires_complet},
|
||||
]
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -239,10 +239,24 @@ def inventaire() -> dict:
|
|||
for a in applications.values())
|
||||
and str(s.get("etat", "actif")) == "actif"), "")
|
||||
|
||||
# LE DEPOT DES BINAIRES DIRECTS — meme derivation que le resolveur, pour la meme
|
||||
# raison : l'adresse se lit la ou elle est vraie, dans le plan qui porte le service.
|
||||
#
|
||||
# Forgejo, Keycloak, Nextcloud et oauth2-proxy ne vivent dans aucun depot apt : le
|
||||
# controleur les tire puis les pousse par SSH. Le cache du site les tient desormais
|
||||
# (`serveur_artefacts`, LocalDirs), et c'est ce qui permet a un runner de monter un
|
||||
# ecosysteme entier sans sortir. Mesure du 2026-09-12 : sans lui, 570 Mo par runner.
|
||||
_cache_site = next((str(s.get("ip")) for nom, s in serveurs.items()
|
||||
if any(a.get("hote") == nom and a.get("groupe") == "serveur_artefacts"
|
||||
for a in applications.values())
|
||||
and str(s.get("etat", "actif")) == "actif"), "")
|
||||
|
||||
# Ce que TOUTE machine du site recoit, des intrants du plan.
|
||||
communes: dict = {}
|
||||
if _resolveur:
|
||||
communes["dns_amorcage"] = _resolveur
|
||||
if _cache_site:
|
||||
communes["setops_depot_binaires"] = f"http://{_cache_site}:3142/setops-binaires"
|
||||
for cle in ("domaine_interne", "organisation", "dns_amorcage"):
|
||||
if intrants.get(cle):
|
||||
communes[cle] = str(intrants[cle])
|
||||
|
|
|
|||
Loading…
Reference in a new issue