Set-OPS-Public/roles/client_artefacts/tasks/main.yml
Daniel Allaire 0dd57ba995 artefacts : deux filets — un cache mort ne doit plus rendre un tenant irreparable
CE CACHE EST UN MAILLON DONT DEPEND SA PROPRE RECONSTRUCTION. Toute la flotte y
prend ses paquets. S il pointe vers un amont mort, apt echoue sur les quinze
machines, donc le socle echoue, donc le deploiement n atteint jamais la couche qui
poserait le bon amont. LE CORRECTIF SE RETROUVE DANS LA COUCHE QUE LA PANNE EMPECHE
D ATTEINDRE, et il faut une main pour en sortir.

C est arrive le 2026-08-31 : l amont pointait sur le cache de patient 0, eteint la
veille pour liberer de la RAM. apt rendait « 503 Connection timeout » EN CITANT
L ADRESSE DU CACHE LOCAL, jamais celle de l amont manquant — la panne accusait le
maillon visible.

DEUX FILETS, SYMETRIQUES.

serveur_artefacts sonde l amont AVANT d ecrire, et refuse s il est muet : mieux
vaut un cache qui garde sa configuration precedente qu un cache qu on vient de
rendre inutilisable pour toute la flotte.

client_artefacts sonde la source AVANT de detourner apt vers elle. Ce fichier
ECRASE le plancher d amorcage pose par le socle — le poser sur un cache mort prive
la machine du cache du SITE, qui lui fonctionnait. En refusant, le plancher reste :
la flotte est degradee mais debout, et REPARABLE PAR UN DEPLOIEMENT.

`ignore_errors` ET NON `failed_when: false` — la difference est toute la garde.
failed_when: false REECRIT le verdict : la tache n est plus jamais failed, donc
`is not failed` est toujours vrai, donc l assertion ne peut pas tirer. Ecrite ainsi,
ma premiere version a declare « joignable » un amont mesure MUET la seconde d avant.
Une garde qui ne peut pas echouer ne garde rien.

Deux controles verifies sur la machine : amont eteint -> refuse ; amont reel -> pose.

make verifier : vert. make prouver : CONFORME, 55 OK, 0 echec, 0 saute.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on
2026-08-31 18:31:21 -04:00

89 lines
4.3 KiB
YAML

---
- name: Exiger un hôte de source d'artefacts quand l'intégration est active
ansible.builtin.assert:
that:
- client_artefacts_hote | length > 0
fail_msg: >-
client_artefacts est actif mais aucun hôte ne porte `serveur_artefacts`.
Déclarer le service au plan, ou forcer client_artefacts_actif=false.
when: client_artefacts_actif | bool
# --- LE FILET, COTE CLIENT : NE PAS VISER UN CACHE QUI NE SERT PAS ------------
#
# Symetrique de la garde de `serveur_artefacts` (mesure du 2026-08-31). Celle-la protege
# le CACHE d'un amont mort ; celle-ci protege LA FLOTTE d'un cache mort.
#
# Ce fichier ECRASE le plancher d'amorcage pose par le socle — il trie apres. Le poser
# alors que le cache du tenant ne repond pas prive donc la machine du cache du SITE, qui
# lui fonctionnait : on remplace une source qui marche par une qui ne marche pas.
#
# Quinze machines ont ainsi perdu `apt` d'un coup, et le deploiement ne pouvait plus
# atteindre la couche qui aurait repare le cache. En laissant le plancher en place, elles
# restent servies par le site — dégradé, mais debout, et reparable par un deploiement.
- name: La source de l'écosystème répond-elle vraiment ?
ansible.builtin.wait_for:
host: "{{ client_artefacts_hote }}.{{ domaine_interne }}"
port: "{{ client_artefacts_port }}"
timeout: 10
register: client_artefacts_sonde
changed_when: false
# `ignore_errors` ET NON `failed_when: false` — LA DIFFERENCE EST TOUTE LA GARDE.
#
# `failed_when: false` REECRIT le verdict : la tache n'est plus jamais `failed`, donc
# `is not failed` est toujours vrai, donc l'assertion qui suit ne peut PAS tirer. Ecrite
# ainsi, elle a declare « joignable » un amont mesure MUET la seconde d'avant
# (2026-08-31). Une garde qui ne peut pas echouer ne garde rien.
#
# `ignore_errors` laisse le verdict intact et se contente de ne pas arreter le play :
# c'est ce qu'il faut pour SONDER puis DECIDER.
ignore_errors: true
when:
- client_artefacts_actif | bool
- not ansible_check_mode
- name: Refuser de detourner apt vers une source muette
ansible.builtin.assert:
that:
- client_artefacts_sonde is not failed
fail_msg: >-
{{ client_artefacts_hote }}:{{ client_artefacts_port }} ne repond pas. Diriger `apt`
vers elle retirerait a cette machine le cache d'amorcage du SITE, qui fonctionne —
et la flotte perdrait `apt` sans qu'un deploiement puisse la reparer.
Deployer `serveur_artefacts` sur {{ client_artefacts_hote }} d'abord.
success_msg: "Source d'artefacts {{ client_artefacts_hote }} joignable."
when:
- client_artefacts_actif | bool
- not ansible_check_mode
- name: Diriger apt vers la source de l'écosystème
ansible.builtin.copy:
dest: "{{ client_artefacts_fichier }}"
content: |
// GÉNÉRÉ par Set-OPS (rôle client_artefacts). NE PAS éditer à la main.
// Les paquets viennent de l'écosystème, pas de six serveurs étrangers.
Acquire::http::Proxy "http://{{ client_artefacts_hote }}.{{ domaine_interne }}:{{ client_artefacts_port }}";
{% if client_artefacts_https | bool %}
Acquire::https::Proxy "http://{{ client_artefacts_hote }}.{{ domaine_interne }}:{{ client_artefacts_port }}";
{% else %}
// « DIRECT » EST OBLIGATOIRE, PAS DECORATIF (mesure du 2026-08-23).
//
// apt fait HERITER `Acquire::https::Proxy` de la valeur HTTP quand elle n'est pas
// definie. Poser le seul proxy HTTP envoyait donc AUSSI les depots tiers en HTTPS
// dans le cache, qui refuse les tunnels — a juste titre :
// « Invalid response from proxy: HTTP/1.0 403 CONNECT denied »
// et packages.smallstep.com devenait injoignable pour toute la flotte.
Acquire::https::Proxy "DIRECT";
{% endif %}
owner: root
group: root
mode: "0644"
when: client_artefacts_actif | bool
# L'écosystème qui RETIRE sa source d'artefacts doit voir ses hôtes revenir à l'amont,
# sans quoi ils resteraient braqués sur une machine disparue et n'installeraient plus
# rien. Une intégration qui ne sait pas se retirer est un piège différé.
- name: Retirer la direction quand l'écosystème n'a pas de source
ansible.builtin.file:
path: "{{ client_artefacts_fichier }}"
state: absent
when: not (client_artefacts_actif | bool)