--- - 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 # DEGRADER, PAS ARRETER — le commentaire ci-dessus disait deja quoi faire, le code # faisait autre chose (mesure du 2026-09-01, reconstruction de Chezlepro depuis zero). # # La garde etait une `assert` qui ARRETAIT le deploiement. Or ce qu'elle protege, c'est # le plancher d'amorcage du site : il suffit de NE PAS l'ecraser. Arreter en plus, ca ne # protege rien de plus, et ca rend la reconstruction depuis zero IMPOSSIBLE. # # CE QUE CA A COUTE : `_amorcer-socle` monte l'autorite en premier, comme il se doit. # `infra-pki-01` est donc la premiere machine debout — a un moment ou le cache du # locataire, qui vit DANS la flotte qu'on reconstruit, n'existe evidemment pas encore. # La garde tirait sur la premiere machine de la premiere couche, et tout s'arretait la. # Aucun ordre de deploiement ne pouvait la satisfaire : la contradiction etait dans la # garde, pas dans le plan. # # Le comportement juste est la CONVERGENCE : tant que la source du locataire est muette, # la machine reste servie par le site — degradee, debout, et reparable par le passage # suivant. Quand `serveur_artefacts` sera monte sur son hote, le deploiement d'apres # basculera apt de lui-meme. # # On le DIT, fort : une machine qui reste sur le cache du site alors qu'elle devrait # etre passee a celui de son ecosysteme est un ecart qu'il faut pouvoir lire dans le # journal, pas deviner. - name: Rester sur le cache du SITE tant que celui de l'écosystème est muet ansible.builtin.debug: msg: >- {{ client_artefacts_hote }}:{{ client_artefacts_port }} ne repond pas — apt N'EST PAS detourne. Cette machine reste servie par le cache d'amorcage du SITE, qui fonctionne : degrade, debout, reparable. La bascule se fera au deploiement suivant, une fois `serveur_artefacts` monte sur {{ client_artefacts_hote }}. when: - client_artefacts_actif | bool - not ansible_check_mode - client_artefacts_sonde is failed - 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 # Ne jamais ecraser le plancher du site par une source qu'on n'a pas VU repondre. # En `check_mode` la sonde ne tourne pas : `is failed` est alors faux, et on garde # le comportement de simulation d'avant. - not (client_artefacts_sonde is failed) # 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)