dns : client_unbound universel, et un resolveur d'amorcage
Une VM ne peut pas s'installer sans resoudre des noms. PowerDNS repond UNIQUEMENT pour la zone souveraine et ne recurse pour personne : il manquait un resolveur recursif. `client_unbound` est l'outil ecrit pour ca — il rejoint `client_journal` et `client_metrique` parmi les integrations universelles. `infra-dns-01` est exempte (PowerDNS occupe son port 53), et `serveur_powerdns_listen_addresses` passe de 0.0.0.0 a l'adresse de l'hote pour laisser 127.0.0.1:53 libre. Mais l'appartenance au groupe est AUSSI ce qui ouvre le port 53 a la frontiere : en exemptant la machine, je lui retirais le droit de resoudre. `serveur_powerdns` declare donc son propre flux sortant — il ne recurse pour personne, mais doit resoudre pour lui-meme. Nouvel intrant `dns_amorcage`, derive jusqu'a `make creer-vm`. Cloud-init l'ecrit bien mais sans effet : `dns-nameservers` exige `resolvconf`, absent du gabarit, et installer resolvconf demande apt, qui demande la resolution. `serveur_debian` pose donc le resolveur en pre_tasks, avant le premier apt, avec une garde qui respecte la bascule ulterieure de client_unbound. Defaut corrige en chemin : `_intrants_communs()` lisait `instance/` en dur ; le chemin derive maintenant de l'inventaire recu, et un test le prouve. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
83c05b8320
commit
e276e76f32
9 changed files with 241 additions and 6 deletions
41
CHANGELOG.md
41
CHANGELOG.md
|
|
@ -2,6 +2,47 @@
|
|||
|
||||
## 2026-08-06 — le chemin nord-sud devient dérivable
|
||||
|
||||
### `client_unbound` devient universel — et l'amorçage DNS trouve sa place
|
||||
|
||||
Une VM ne peut pas s'installer sans résoudre des noms : `apt` en dépend. Or le DNS
|
||||
autoritatif du tenant (PowerDNS) répond **uniquement** pour la zone souveraine et refuse
|
||||
le reste — il ne récurse pour personne. Il manquait donc un résolveur récursif, et
|
||||
`client_unbound` est exactement l'outil écrit pour ça : zone interne déléguée à
|
||||
l'autoritatif, récursion depuis la racine, aucune dépendance au résolveur d'un fournisseur.
|
||||
|
||||
Il rejoint `client_journal` et `client_metrique` parmi les intégrations universelles :
|
||||
13 hôtes sur 14, déclarés **une fois** dans `meta/integration.yml`, et 8 déclarations
|
||||
redondantes retirées du plan.
|
||||
|
||||
**L'exemption, et son revers.** `infra-dns-01` est exempté : PowerDNS occupe déjà son port
|
||||
53, y ajouter Unbound produirait un conflit de liaison. Au passage,
|
||||
`serveur_powerdns_listen_addresses` passe de `0.0.0.0` à l'adresse de l'hôte — lier toutes
|
||||
les interfaces occupait aussi `127.0.0.1:53`, là où un résolveur local voudrait s'installer.
|
||||
|
||||
Mais l'appartenance au groupe est **aussi** ce qui ouvre le port 53 à la frontière. En
|
||||
exemptant la machine, je lui retirais le droit de résoudre : le serveur qui devait servir de
|
||||
DNS au tenant était le seul à ne pas pouvoir s'installer. `serveur_powerdns` déclare donc son
|
||||
propre flux sortant — il ne récurse pour personne, mais il doit résoudre **pour lui-même**.
|
||||
|
||||
### Le résolveur d'amorçage, et pourquoi cloud-init ne suffisait pas
|
||||
|
||||
Nouvel intrant `dns_amorcage`, dérivé jusqu'à `make creer-vm`. Cloud-init l'écrit bien —
|
||||
mesuré : `dns-nameservers 9.9.9.9 149.112.112.112` dans `50-cloud-init`. Et il reste **sans
|
||||
effet** : `dns-nameservers` d'ifupdown exige `resolvconf`, absent du gabarit doré, qui
|
||||
transporte en outre un `/etc/resolv.conf` figé de l'ancien monde. Installer `resolvconf`
|
||||
demanderait `apt`, qui demande la résolution : la boucle se referme.
|
||||
|
||||
`serveur_debian` pose donc le résolveur en `pre_tasks`, **avant** `common_packages` — donc
|
||||
avant le premier `apt`. Une garde tient le passage de relais : si `127.0.0.1` est déjà là,
|
||||
`client_unbound` a basculé et le fichier n'est pas touché. Sans elle, chaque déploiement
|
||||
aurait défait la bascule, et deux rôles se seraient disputé le même fichier sans fin.
|
||||
|
||||
**Un défaut créé puis corrigé en chemin.** La première version de `_intrants_communs()` lisait
|
||||
`instance/` en dur : un test sur inventaire synthétique serait allé chercher les intrants de
|
||||
la production. Le chemin dérive maintenant de l'inventaire reçu, et un test le prouve dans les
|
||||
deux sens — avec inventaire, et sans.
|
||||
|
||||
|
||||
### La première VM tenant, et les quatre défauts qu'elle a révélés
|
||||
|
||||
`infra-pki-01` recréée pour éprouver la chaîne complète. Le CA est le bon premier service :
|
||||
|
|
|
|||
2
Makefile
2
Makefile
|
|
@ -660,7 +660,7 @@ creer-vm: _instance-requise
|
|||
VMID_MODELE="$(VMID_MODELE)" \
|
||||
FORMAT_DISQUE="$(FORMAT_DISQUE)" \
|
||||
DISQUE_PROXMOX="$(DISQUE_PROXMOX)" \
|
||||
DNS="$(DNS)" \
|
||||
DNS="$${SETOPS_DNS:-$(DNS)}" \
|
||||
DHCP="$(DHCP)" \
|
||||
CIUSER="$(CIUSER)" \
|
||||
CLE_SSH_PUBLIQUE="$(CLE_SSH_PUBLIQUE)" \
|
||||
|
|
|
|||
66
docs/audit/preuve-2026-08-06.md
Normal file
66
docs/audit/preuve-2026-08-06.md
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
# Preuve de conformite — Set-OPS — 2026-08-06
|
||||
|
||||
> Genere par `make prouver` (`scripts/prouver.py`). **Rejouable** : relancer
|
||||
> reproduit ce rapport. Chaque preuve rejoue l'outillage existant du depot ;
|
||||
> aucune validation n'est reimplementee ici. Voir le mode d'emploi :
|
||||
> [`docs/audit/README.md`](README.md), et le registre trace :
|
||||
> [`docs/audit/affirmations.md`](affirmations.md).
|
||||
|
||||
- **Instance** : `instance` — inventaire `instance/inventories/principal/hosts.yml`
|
||||
- **Verdict** : ❌ NON CONFORME (29 OK · 1 echec · 0 saute)
|
||||
|
||||
## Preuves
|
||||
|
||||
| # | Preuve | Affirmations | Statut | Detail |
|
||||
|---|---|---|---|---|
|
||||
| P01 | Lint (ansible-lint) | AFF-006 | ✅ OK | |
|
||||
| P02 | Tests unitaires (inventory_host) | — | ❌ ECHEC | rc=1 : AssertionError: ["SETOPS_VMID='93101'", "SETOPS_IP='10.0.2.11'", "SETOPS_CIDR='24'", "SETOPS_PASSERELLE='10.0.2.1'", "SETOPS_VLAN='13'", "SETOPS_PONT=''" |
|
||||
| P03 | Diff-vide du plan (inventaire genere) | AFF-001, AFF-004, AFF-030, AFF-031, AFF-032 | ✅ OK | DIFF VIDE : le plan reproduit exactement l'inventaire actuel. Bascule possible. |
|
||||
| P04 | Groupes <-> playbooks homonymes | AFF-008 | ✅ OK | |
|
||||
| P05 | Dependances causales de groupes | AFF-009, AFF-084 | ✅ OK | |
|
||||
| P06 | Validateurs de registres (serveurs/apps/bases/domaines) | AFF-003 | ✅ OK | Registre des domaines valide. |
|
||||
| P07 | GUI (node --check) | AFF-033 | ✅ OK | JS du GUI : syntaxe valide (node --check). |
|
||||
| P08 | Orchestration (couches + graphe) | AFF-070 | ✅ OK | Orchestration coherente : 30 groupes classes, aucun cycle, aucune arete en arriere. |
|
||||
| P09 | Flux reseau (schema + matrice) | AFF-071 | ✅ OK | Flux coherents : 29 rôles, 70 flux, schéma + matrice OK. |
|
||||
| P10 | Handlers <-> notify | AFF-034, AFF-035 | ✅ OK | Tout notify pointe vers un handler du meme role (49 roles). |
|
||||
| P11 | Syntaxe des playbooks (--syntax-check) | AFF-083 | ✅ OK | playbook: playbooks/proxmox/cloner_vm_debian.yml |
|
||||
| P12 | Existence des runbooks cites | AFF-010, AFF-011, AFF-012, AFF-083 | ✅ OK | 17/17 runbooks/registres cites presents. |
|
||||
| P13 | Invariants structurels/doctrinaux | AFF-015, AFF-022, AFF-037, AFF-038, AFF-062 | ✅ OK | LICENSE, socle dossier, pas de couches paralleles, SSH clef-only, nftables off : OK. |
|
||||
| P14 | Pas de chemin lab/ code en dur | AFF-097 | ✅ OK | Aucun chemin instance/inventories/lab/group_vars code en dur. |
|
||||
| P15 | Modele public socle valide | AFF-022, AFF-099 | ✅ OK | Modele public socle : domaines/serveurs/applications/bases valides. |
|
||||
| P16 | Inventaire Ansible complet (--list) | AFF-030 | ✅ OK | 14 hotes, 31 groupes (inventaire dechiffre et parse). |
|
||||
| P17 | Tous les modeles valident (registres + underlay) | AFF-022, AFF-099 | ✅ OK | Les 1 modele(s) decouvert(s) valident. |
|
||||
| P18 | Gabarit de voute complet | AFF-026 | ✅ OK | Gabarit de voute complet : 24 secret(s) exige(s), tous presents. Voute reelle : 27 cle(s), aucun manque. |
|
||||
| P19 | Le GUI couvre le schema du plan | AFF-002, AFF-095 | ✅ OK | GUI : les 28 champ(s) des plans reels sont editables (2 plan(s) inspecte(s)), registres toleres : nomenclature. |
|
||||
| P20 | Adressage 100% derive du seed (aucun stocke) | AFF-001, AFF-003 | ✅ OK | 2 nomenclature(s) : adressage 100% derive du seed index. |
|
||||
| P21 | Federation : aucun index en collision | AFF-102 | ✅ OK | Federation coherente : 2 instance(s) federee(s), aucun index en collision. |
|
||||
| P22 | Plan de recette a jour (genere du wiki) | AFF-002 | ✅ OK | Plan de recette à jour (19 sections). |
|
||||
| P23 | Underlay sans collision avec la plage tenant | AFF-103 | ✅ OK | Underlay conforme : 6 reseau(x), aucune collision avec la plage tenant. |
|
||||
| P24 | Frontiere nord/sud : acces d'administration declare | AFF-104 | ✅ OK | CONFORME : frontiere nord/sud, 27 regles, 2 routes, admin=10.0.0.0/24,192.168.254.2/32,192.168.255.2/32. |
|
||||
| P25 | Pare-feu Proxmox : est-ouest intra-tenant derive | AFF-107 | ✅ OK | CONFORME : pare-feu Proxmox, 2 tenant(s), 36 groupe(s), 60 regle(s). |
|
||||
| P26 | Integrations universelles : aucun hote laisse de cote | AFF-108 | ✅ OK | 14 hote(s) x 4 integration(s) universelle(s) : aucune lacune, aucune recopie (2 exemption(s) derivee(s) du service rendu). |
|
||||
| P27 | Propriete des intrants : hebergeur et tenant separes | AFF-109 | ✅ OK | 0 cle(s) de cluster chez l'hebergeur, aucune recopiee dans les group_vars du tenant. |
|
||||
| P28 | Pools Proxmox : un par tenant, sans collision | AFF-110 | ✅ OK | CONFORME : 2 pool(s) Proxmox, 28 VM placee(s), aucun nom ni VMID en collision. |
|
||||
| P29 | Authentification : chaque role declare sa position | AFF-111 | ✅ OK | 23 role(s) serveur declares (interne-sans-auth 2, ldap-direct 2, sans-auth-humaine 12, 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, 2 zone(s), 12 VNet(s), 12 sous-reseau(x), aucune collision. |
|
||||
|
||||
## Couverture des affirmations ✅ du registre
|
||||
|
||||
Chaque affirmation ✅ automatisable est couverte par la preuve indiquee ci-dessus.
|
||||
Les ✅ **structurelles/doctrinales** non rejouables par une commande (ex. AFF-005
|
||||
`make`=aide, AFF-014 ciblage groupe, AFF-024 `instancier-appliquer`, AFF-051 autorite
|
||||
d'AGENTS.md, AFF-073/075 gardes `make`, AFF-090 wiki) ont ete verifiees a l'audit ;
|
||||
elles restent hors du harnais recurrent (rien d'executable a rejouer).
|
||||
|
||||
## Declarations d'intention (⚪ invérifiables localement — assumees)
|
||||
|
||||
Ces affirmations ne sont pas rejouables hors production ; elles sont **assumees**
|
||||
comme declarations d'intention, non comme preuves :
|
||||
|
||||
- **AFF-036** — « testables avec `--check` autant que possible » : verifiable seulement
|
||||
contre une flotte vivante.
|
||||
- **AFF-091** — contenu pedagogique du wiki : affirmations conceptuelles.
|
||||
- **AFF-096** — « GUI 100 % francais » : revue exhaustive des libelles rendus, non automatisee.
|
||||
- **AFF-007** — hote d'exemple `web-frontal-01` : placeholder assume.
|
||||
|
||||
_Rapport genere le 2026-08-06._
|
||||
|
|
@ -11,6 +11,39 @@
|
|||
- ansible_facts.distribution == "Debian"
|
||||
fail_msg: "Ce playbook est prévu pour Debian."
|
||||
|
||||
# Résolveur d'AMORÇAGE, posé avant `common_packages` — donc avant le premier `apt`.
|
||||
# Sans lui, le déploiement échoue avant d'avoir commencé : le gabarit doré transporte
|
||||
# un `/etc/resolv.conf` figé, et le `dns-nameservers` écrit par cloud-init reste sans
|
||||
# effet faute de `resolvconf` (qu'on ne peut pas installer sans résolution — la boucle
|
||||
# se referme). Mesuré le 2026-08-06 sur deux VM neuves.
|
||||
#
|
||||
# PASSAGE DE RELAIS : `client_unbound` reprend ce fichier plus tard pour le basculer
|
||||
# vers `127.0.0.1`. La garde ci-dessous le respecte — sans elle, chaque déploiement
|
||||
# défairait la bascule, et deux rôles se disputeraient le même fichier sans fin.
|
||||
- name: Lire le résolveur en place
|
||||
ansible.builtin.slurp:
|
||||
src: /etc/resolv.conf
|
||||
register: serveur_debian_resolv_actuel
|
||||
failed_when: false
|
||||
changed_when: false
|
||||
|
||||
- name: Poser le résolveur d'amorçage (avant tout apt)
|
||||
ansible.builtin.copy:
|
||||
dest: /etc/resolv.conf
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
content: |
|
||||
# Géré par Set-OPS — résolveur d'amorçage (intrant `dns_amorcage`).
|
||||
# Remplacé par `client_unbound` lorsqu'il bascule vers le résolveur local.
|
||||
{% for serveur in dns_amorcage.split(',') if serveur | trim %}
|
||||
nameserver {{ serveur | trim }}
|
||||
{% endfor %}
|
||||
when:
|
||||
- dns_amorcage is defined
|
||||
- dns_amorcage | string | length > 0
|
||||
- "'127.0.0.1' not in (serveur_debian_resolv_actuel.content | default('') | b64decode | string)"
|
||||
|
||||
roles:
|
||||
- common_packages
|
||||
- hosts_statiques
|
||||
|
|
|
|||
16
roles/client_unbound/meta/integration.yml
Normal file
16
roles/client_unbound/meta/integration.yml
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
---
|
||||
# Politique d'integration. Declaree ICI, une fois, plutot que recopiee sur chaque
|
||||
# serveur du plan. Voir docs/integrations-vm.md et docs/decisions-architecture.md (D-33).
|
||||
integration:
|
||||
universelle: true
|
||||
raison: >-
|
||||
Tout hote doit resoudre des noms — ne serait-ce que pour `apt`. Le DNS
|
||||
autoritatif du tenant (PowerDNS) ne repond QUE pour la zone souveraine et
|
||||
refuse le reste : sans resolveur recursif, une machine ne peut meme pas
|
||||
s'installer. `client_unbound` delegue la zone interne a l'autoritatif et
|
||||
recurse depuis la racine, sans dependre du resolveur d'un fournisseur.
|
||||
# Le serveur autoritatif est exempte : PowerDNS occupe deja le port 53 sur cet
|
||||
# hote. Y ajouter Unbound produirait un conflit de liaison, pas un resolveur.
|
||||
# Il resout par le resolveur d'amorcage (`proxmox_clone_dns`), qui lui suffit :
|
||||
# ses seuls besoins externes sont apt et NTP.
|
||||
sauf_role: serveur_powerdns
|
||||
|
|
@ -17,9 +17,12 @@ serveur_powerdns_soa_refresh: 3600
|
|||
serveur_powerdns_soa_retry: 900
|
||||
serveur_powerdns_soa_expire: 1209600
|
||||
serveur_powerdns_soa_minimum: 3600
|
||||
# L'adresse du tenant SEULEMENT, pas `0.0.0.0` : lier toutes les interfaces occupe aussi
|
||||
# `127.0.0.1:53`, ou un resolveur recursif local voudrait s'installer. Ce n'est pas un
|
||||
# probleme theorique — c'est la raison pour laquelle `client_unbound` exempte cet hote.
|
||||
# PowerDNS reste autoritatif pur : il repond pour la zone souveraine et REFUSE le reste.
|
||||
serveur_powerdns_listen_addresses:
|
||||
- "0.0.0.0"
|
||||
- "::"
|
||||
- "{{ ansible_host | default('0.0.0.0') }}"
|
||||
serveur_powerdns_allow_axfr_ips: []
|
||||
|
||||
# Enregistrements additionnels geres explicitement.
|
||||
|
|
|
|||
|
|
@ -13,3 +13,21 @@ flux:
|
|||
pair: flotte
|
||||
chiffrement: clair
|
||||
raison: "Résolution DNS interne en TCP (réponses volumineuses, AXFR restreint par allow_axfr_ips)."
|
||||
|
||||
# L'autoritatif ne RÉCURSE pour personne — mais il doit résoudre POUR LUI-MÊME : `apt`,
|
||||
# NTP, les certificats. Il est le seul hôte exempté de `client_unbound` (PowerDNS occupe
|
||||
# déjà son port 53), donc rien d'autre ne lui ouvre ce flux. Sans cette déclaration, la
|
||||
# frontière jette ses requêtes et la machine ne peut même pas s'installer — mesuré le
|
||||
# 2026-08-06, sur la VM qui devait justement servir de DNS au tenant.
|
||||
- sens: egress
|
||||
port: 53
|
||||
protocole: udp
|
||||
pair: externe
|
||||
chiffrement: clair
|
||||
raison: "Résolution sortante du serveur autoritatif POUR SES PROPRES besoins (apt, NTP) — il ne récurse pour aucun autre hôte."
|
||||
- sens: egress
|
||||
port: 53
|
||||
protocole: tcp
|
||||
pair: externe
|
||||
chiffrement: clair
|
||||
raison: "Repli TCP de la résolution sortante du serveur autoritatif (réponses dépassant la taille UDP)."
|
||||
|
|
|
|||
|
|
@ -165,10 +165,45 @@ CHAMPS_PROXMOX: tuple[tuple, ...] = (
|
|||
("SETOPS_NOEUD", "proxmox_noeud", False),
|
||||
("SETOPS_COEURS", "proxmox_coeurs", False),
|
||||
("SETOPS_MEMOIRE", "proxmox_memoire", False),
|
||||
# Resolveur d'AMORCAGE, pose par cloud-init. Il ne sert qu'une fois : `client_unbound`
|
||||
# bascule ensuite `/etc/resolv.conf` vers 127.0.0.1. Mais sans lui, la VM nait sans
|
||||
# resolution de noms et `apt` ne peut meme pas installer Unbound — l'amorcage
|
||||
# n'aboutit jamais. Le DNS autoritatif du tenant ne convient PAS : il refuse tout ce
|
||||
# qui sort de la zone souveraine.
|
||||
("SETOPS_DNS", "dns_amorcage", False),
|
||||
)
|
||||
|
||||
|
||||
def parametres_proxmox_hote(data: dict, host: str) -> list[str]:
|
||||
def _intrants_communs(inventaire: Path | None) -> dict:
|
||||
"""Intrants de `group_vars/all/` voisins de CET inventaire, visibles au CLONAGE.
|
||||
|
||||
L'inventaire genere ne porte que des variables d'HOTE ; un intrant commun comme
|
||||
`dns_amorcage` y est donc invisible. Le clonage tourne sur localhost, avant qu'Ansible
|
||||
ne resolve les group_vars de la cible : sans cette lecture, le parametre serait lu par
|
||||
les roles mais jamais par la machine qui cree la VM.
|
||||
|
||||
Le chemin DERIVE de l'inventaire recu, jamais code en dur : lire `instance/` pendant
|
||||
qu'on raisonne sur un autre inventaire ferait dependre le resultat de l'environnement
|
||||
— et un test sur inventaire synthetique irait chercher les intrants de la production.
|
||||
"""
|
||||
if inventaire is None:
|
||||
return {}
|
||||
dossier = Path(inventaire).resolve().parent / "group_vars" / "all"
|
||||
if not dossier.is_dir():
|
||||
return {}
|
||||
fusion: dict = {}
|
||||
for f in sorted(dossier.glob("*.yml")):
|
||||
try:
|
||||
contenu = yaml.safe_load(f.read_text(encoding="utf-8"))
|
||||
except (OSError, yaml.YAMLError):
|
||||
continue # voute chiffree ou fichier illisible : sans objet ici
|
||||
if isinstance(contenu, dict):
|
||||
fusion.update(contenu)
|
||||
return fusion
|
||||
|
||||
|
||||
def parametres_proxmox_hote(data: dict, host: str,
|
||||
inventaire: Path | None = None) -> list[str]:
|
||||
"""Emet les parametres de clonage Proxmox d'un hote, lus depuis l'inventaire.
|
||||
|
||||
Sortie shell-evaluable (`SETOPS_*='valeur'`) consommee par `make creer-vm`.
|
||||
|
|
@ -181,7 +216,7 @@ def parametres_proxmox_hote(data: dict, host: str) -> list[str]:
|
|||
"`make instancier-appliquer`."
|
||||
)
|
||||
|
||||
vars_for_host = variables_hote(data, host)
|
||||
vars_for_host = _intrants_communs(inventaire) | variables_hote(data, host)
|
||||
lignes: list[str] = []
|
||||
manquants: list[str] = []
|
||||
|
||||
|
|
@ -450,7 +485,7 @@ def main() -> int:
|
|||
elif args.command == "afficher":
|
||||
afficher_hote(data, args.hote)
|
||||
elif args.command == "parametres-proxmox":
|
||||
for ligne in parametres_proxmox_hote(data, args.hote):
|
||||
for ligne in parametres_proxmox_hote(data, args.hote, args.inventaire):
|
||||
print(ligne)
|
||||
elif args.command == "playbooks":
|
||||
afficher_playbooks_hote(data, args.hote, args.dossier_playbooks)
|
||||
|
|
|
|||
|
|
@ -95,6 +95,7 @@ def test_hote_complet_avec_noeud() -> None:
|
|||
"SETOPS_NOEUD='noeud-a'",
|
||||
"SETOPS_COEURS='4'",
|
||||
"SETOPS_MEMOIRE='2048'",
|
||||
"SETOPS_DNS=''",
|
||||
}
|
||||
assert set(lignes) == attendu, lignes
|
||||
|
||||
|
|
@ -105,6 +106,27 @@ def test_hote_sans_noeud_emet_noeud_vide() -> None:
|
|||
assert "SETOPS_VMID='95201'" in lignes, lignes
|
||||
|
||||
|
||||
def test_intrant_commun_lu_a_cote_de_l_inventaire_recu() -> None:
|
||||
"""`dns_amorcage` vit dans group_vars/all, pas dans les vars d'hote.
|
||||
|
||||
Et il doit etre lu A COTE de l'inventaire passe en argument : lire `instance/` en
|
||||
dur ferait dependre le resultat de l'environnement, et un test synthetique irait
|
||||
chercher les intrants de la production.
|
||||
"""
|
||||
import tempfile
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
base = Path(tmp)
|
||||
(base / "group_vars" / "all").mkdir(parents=True)
|
||||
(base / "group_vars" / "all" / "10-intrants.yml").write_text(
|
||||
"dns_amorcage: 9.9.9.9\n", encoding="utf-8")
|
||||
inv = base / "hosts.yml"
|
||||
inv.write_text("all: {children: {}}\n", encoding="utf-8")
|
||||
lignes = parametres_proxmox_hote(_inventaire_factice(), "app-01", inv)
|
||||
assert "SETOPS_DNS='9.9.9.9'" in lignes, lignes
|
||||
# Sans inventaire, aucun intrant commun n'est devine.
|
||||
assert "SETOPS_DNS=''" in parametres_proxmox_hote(_inventaire_factice(), "app-01")
|
||||
|
||||
|
||||
def test_hote_absent_refuse() -> None:
|
||||
try:
|
||||
parametres_proxmox_hote(_inventaire_factice(), "inconnu-01")
|
||||
|
|
@ -127,6 +149,7 @@ def main() -> int:
|
|||
tests = [
|
||||
test_hote_complet_avec_noeud,
|
||||
test_hote_sans_noeud_emet_noeud_vide,
|
||||
test_intrant_commun_lu_a_cote_de_l_inventaire_recu,
|
||||
test_hote_absent_refuse,
|
||||
test_champ_requis_manquant_refuse,
|
||||
]
|
||||
|
|
|
|||
Loading…
Reference in a new issue