107 lines
4.8 KiB
Python
107 lines
4.8 KiB
Python
|
|
#!/usr/bin/env python3
|
||
|
|
"""La portee de la console : un pouvoir se mesure sur ce qu'on peut OUVRIR.
|
||
|
|
|
||
|
|
`docs/responsabilites-locataire-hebergeur.md` §2 attache chaque pouvoir a une voute :
|
||
|
|
|
||
|
|
calculer plan -> inventaire aucune voute
|
||
|
|
configurer des roles sur ses machines voute du TENANT
|
||
|
|
materialiser creer / detruire des VM voute du SITE
|
||
|
|
|
||
|
|
Le code lisait les SYMLINKS — les cartes. Mesure du 2026-09-20 : le runner de
|
||
|
|
Chezlepro-locataire montait la carte du site sans en avoir jamais eu la voute ; sa
|
||
|
|
console se declarait « poste » et offrait 126 etapes sur 126, creation et destruction de
|
||
|
|
machines comprises. Ces gestes seraient partis, puis tombes sur un secret vide.
|
||
|
|
|
||
|
|
Ces cas fabriquent les quatre situations sur disque et exigent la portee qui leur revient.
|
||
|
|
Le quatrieme est le defaut lui-meme, nomme pour qu'il ne revienne pas.
|
||
|
|
"""
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
import sys
|
||
|
|
import tempfile
|
||
|
|
import unittest
|
||
|
|
from pathlib import Path
|
||
|
|
|
||
|
|
RACINE = Path(__file__).resolve().parents[2]
|
||
|
|
sys.path.insert(0, str(RACINE / "scripts"))
|
||
|
|
|
||
|
|
import inventory_gui as gui # noqa: E402
|
||
|
|
|
||
|
|
|
||
|
|
class TestPorteeDeLaConsole(unittest.TestCase):
|
||
|
|
"""Chaque cas monte un faux disque, puis lit ce que la console en conclut."""
|
||
|
|
|
||
|
|
def setUp(self):
|
||
|
|
self.dossier = Path(tempfile.mkdtemp(prefix="portee-"))
|
||
|
|
self._instance = gui.INSTANCE
|
||
|
|
self._underlay = gui.FICHIER_UNDERLAY
|
||
|
|
|
||
|
|
def tearDown(self):
|
||
|
|
gui.INSTANCE = self._instance
|
||
|
|
gui.FICHIER_UNDERLAY = self._underlay
|
||
|
|
import shutil
|
||
|
|
shutil.rmtree(self.dossier, ignore_errors=True)
|
||
|
|
|
||
|
|
def _monter(self, instance: bool, voute_tenant: bool,
|
||
|
|
fabric: bool, voute_site: bool) -> dict:
|
||
|
|
eco = self.dossier / "OPS-Essai"
|
||
|
|
site = self.dossier / "SITE-Essai"
|
||
|
|
if instance:
|
||
|
|
(eco / "inventories" / "principal" / "group_vars" / "all").mkdir(parents=True)
|
||
|
|
if voute_tenant:
|
||
|
|
(eco / "inventories/principal/group_vars/all/vault.yml").write_text(
|
||
|
|
"$ANSIBLE_VAULT;1.1;AES256\n", encoding="utf-8")
|
||
|
|
if fabric:
|
||
|
|
site.mkdir(parents=True, exist_ok=True)
|
||
|
|
(site / "underlay.yml").write_text("underlay: {}\n", encoding="utf-8")
|
||
|
|
if voute_site:
|
||
|
|
(site / "underlay.vault.yml").write_text(
|
||
|
|
"$ANSIBLE_VAULT;1.1;AES256\n", encoding="utf-8")
|
||
|
|
gui.INSTANCE = eco
|
||
|
|
gui.FICHIER_UNDERLAY = site / "underlay.yml"
|
||
|
|
return gui.contexte()
|
||
|
|
|
||
|
|
def test_le_runner_du_site_materialise_et_ne_configure_pas(self):
|
||
|
|
ctx = self._monter(instance=False, voute_tenant=False, fabric=True, voute_site=True)
|
||
|
|
self.assertEqual(ctx["portee"], "site")
|
||
|
|
self.assertTrue(ctx["peut"]["materialiser"])
|
||
|
|
self.assertFalse(ctx["peut"]["configurer"])
|
||
|
|
|
||
|
|
def test_le_runner_du_locataire_configure_et_n_engendre_pas(self):
|
||
|
|
ctx = self._monter(instance=True, voute_tenant=True, fabric=False, voute_site=False)
|
||
|
|
self.assertEqual(ctx["portee"], "tenant")
|
||
|
|
self.assertTrue(ctx["peut"]["configurer"])
|
||
|
|
self.assertFalse(ctx["peut"]["materialiser"])
|
||
|
|
|
||
|
|
def test_le_poste_du_mainteneur_porte_les_deux_voutes(self):
|
||
|
|
ctx = self._monter(instance=True, voute_tenant=True, fabric=True, voute_site=True)
|
||
|
|
self.assertEqual(ctx["portee"], "poste")
|
||
|
|
self.assertTrue(all(ctx["peut"][p] for p in
|
||
|
|
("configurer", "materialiser", "editer_le_plan", "fabric")))
|
||
|
|
|
||
|
|
def test_la_carte_sans_la_voute_n_engendre_rien(self):
|
||
|
|
"""LE DEFAUT, NOMME. Chezlepro-locataire, le 2026-09-20 : la carte, pas les cles."""
|
||
|
|
ctx = self._monter(instance=True, voute_tenant=True, fabric=True, voute_site=False)
|
||
|
|
self.assertEqual(ctx["portee"], "tenant",
|
||
|
|
"une carte lue ne vaut pas une voute ouverte")
|
||
|
|
self.assertFalse(ctx["peut"]["materialiser"])
|
||
|
|
# Il LIT toujours la fabric — c'est un miroir, et le dire vaut mieux que le taire.
|
||
|
|
self.assertTrue(ctx["peut"]["fabric"])
|
||
|
|
self.assertIn("voute", ctx["raison"]["materialiser"].lower())
|
||
|
|
|
||
|
|
def test_calculer_ne_demande_aucune_voute(self):
|
||
|
|
"""La seule des trois portees qui se passe de secret : le plan suffit."""
|
||
|
|
ctx = self._monter(instance=True, voute_tenant=False, fabric=False, voute_site=False)
|
||
|
|
self.assertTrue(ctx["peut"]["editer_le_plan"])
|
||
|
|
self.assertFalse(ctx["peut"]["configurer"])
|
||
|
|
self.assertEqual(ctx["portee"], "orphelin")
|
||
|
|
|
||
|
|
def test_une_console_sans_rien_le_dit(self):
|
||
|
|
ctx = self._monter(instance=False, voute_tenant=False, fabric=False, voute_site=False)
|
||
|
|
self.assertEqual(ctx["portee"], "orphelin")
|
||
|
|
self.assertFalse(any(ctx["peut"].values()))
|
||
|
|
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
unittest.main(verbosity=2)
|