--- # CLOUD-INIT REECRIT /etc/hosts A CHAQUE DEMARRAGE (2026-08-24). # # Le gabarit dore laisse `manage_etc_hosts: true`. A chaque boot, cloud-init regenere # /etc/hosts depuis son propre modele — et efface le plancher de resolution que ce role # vient de poser. La machine perd alors la seule facon qu'elle a de nommer ses voisines # sans DNS, et personne ne le voit : rien n'echoue tant qu'on ne demande pas un nom. # # CONSTATE SUR infra-dns-01 : redemarre a 18h53 lors d'un deplacement de disque, il a # perdu ses six entrees de flotte. Le defaut est apparu deux jours plus tard, sous la # forme d'un `apt update` qui n'arrivait plus a resoudre le cache d'artefacts — un # message qui ne parlait pas du tout du vrai probleme. # # On le desactive par un fragment plutot qu'en editant cloud.cfg : le gabarit reste # intact, et `apt purge cloud-init` ou une remise a zero rend la machine a son etat # d'origine. - name: Empêcher cloud-init de réécrire le plancher à chaque démarrage ansible.builtin.copy: dest: /etc/cloud/cloud.cfg.d/99-setops-hosts.cfg content: | # GÉNÉRÉ par Set-OPS (rôle hosts_statiques). NE PAS éditer à la main. # /etc/hosts est le PLANCHER DE RÉSOLUTION de l'écosystème, dérivé de l'inventaire. # Laisser cloud-init le régénérer au démarrage l'effacerait en silence. manage_etc_hosts: false owner: root group: root mode: "0644" when: hosts_statiques_actif | bool # Alias d'exposition (best-effort) : dérivés du plan si celui-ci est disponible. - name: Vérifier la présence des registres du plan (sur le nœud de contrôle) ansible.builtin.stat: path: "{{ item }}" register: hosts_statiques_plan delegate_to: localhost # les registres du plan vivent sur le nœud de contrôle become: false # lire un fichier ne nécessite pas de privilèges loop: - "{{ setops_plan_dir }}/applications.yml" - "{{ setops_plan_dir }}/domaines.yml" loop_control: label: "{{ item | basename }}" when: - hosts_statiques_actif | bool - hosts_statiques_publier_expositions | bool - setops_plan_dir is defined # CHAQUE REGISTRE EST CHARGÉ POUR LUI-MÊME (2026-08-25). # # La condition exigeait que les DEUX fichiers existent (`length == 2`). Un tout-ou-rien # sans raison : `domaines.yml` déclare les domaines PUBLICS, et un écosystème peut # légitimement n'en avoir aucun — c'est le cas du SITE, dont tous les services sont # internes. # # Conséquence : `applications.yml` n'était pas chargé non plus, la dérivation rendait une # liste vide, et le plancher s'écrivait SANS AUCUN ALIAS — sans erreur. Le certificat de # la forge portait `forge.genese.internal` et ce nom ne résolvait nulle part. # # On charge donc ce qui existe, fichier par fichier. Un registre absent n'est pas une # faute ; il est simplement absent. - name: Charger les registres applications et domaines (si présents) ansible.builtin.include_vars: file: "{{ item.item }}" loop: "{{ hosts_statiques_plan.results | default([]) }}" loop_control: label: "{{ item.item | basename }}" when: - hosts_statiques_actif | bool - hosts_statiques_publier_expositions | bool - item.stat.exists | default(false) - name: Dériver les alias d'exposition (FQDN exposés → edge qui les sert) ansible.builtin.set_fact: hosts_statiques_expositions: >- {{ {'applications': applications | default({})} | expositions_des_applications({'domaines_publics': domaines_publics | default({})}) }} when: - hosts_statiques_actif | bool - hosts_statiques_publier_expositions | bool - name: Générer /etc/hosts depuis l'inventaire (plancher de résolution, indépendant du DNS) ansible.builtin.template: src: hosts.j2 dest: /etc/hosts owner: root group: root mode: "0644" when: hosts_statiques_actif | bool