Garde-fou de déploiement par instance (setops_production)
Le droit de déployer ne dépend plus du nom de l'inventaire mais d'un drapeau explicite setops_production dans group_vars/all/ de l'instance (true = prod, false = bac à sable). Le GUI (champ "production" de l'API) et executer_flux le lisent ; rétro-compatible (à défaut, ancien repère "inventaire production"). Première pierre de la séparation par instance. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
c4e73f8712
commit
8b3648e9f9
2 changed files with 31 additions and 3 deletions
|
|
@ -2,6 +2,15 @@
|
|||
|
||||
## 2026-06-30
|
||||
|
||||
### Ajouté
|
||||
- **Garde-fou de déploiement par instance (`setops_production`).** Le droit de
|
||||
déployer pour de vrai (GUI Vérifier/Déployer, `executer_flux`) ne dépend plus du
|
||||
*nom* de l'inventaire mais d'un drapeau explicite `setops_production` dans
|
||||
`group_vars/all/` de l'instance (true = prod, false = bac à sable). Rétro-compatible
|
||||
(à défaut de drapeau, ancien repère « inventaire production »). Pose la base de la
|
||||
séparation **par instance** : un bac à sable garde `false` et ne peut rien déployer,
|
||||
quel que soit le nom de ses dossiers.
|
||||
|
||||
### Modifié
|
||||
- **Voûte de secrets unique par environnement.** Fini les voûtes éparpillées : tous
|
||||
les secrets de l'instance (token Proxmox + 17 `vault_*` pour PKI, LDAP/SSO, bases,
|
||||
|
|
|
|||
|
|
@ -239,7 +239,7 @@ def inventaire_api(path: Path) -> dict:
|
|||
dependencies = charger_dependances(FICHIER_DEPENDANCES)
|
||||
return {
|
||||
"inventaire": os.path.relpath(path, RACINE),
|
||||
"production": path.resolve() == INVENTAIRE_PRODUCTION,
|
||||
"production": instance_autorise_deploiement(path),
|
||||
"groupes": groupes_disponibles(data),
|
||||
"dependances": dependencies,
|
||||
"nomenclature": charger_nomenclature(FICHIER_NOMENCLATURE),
|
||||
|
|
@ -262,6 +262,25 @@ def _lire_yaml_dict(path: Path) -> dict:
|
|||
return data if isinstance(data, dict) else {}
|
||||
|
||||
|
||||
def instance_autorise_deploiement(path: Path) -> bool:
|
||||
"""Le déploiement réel est-il permis pour l'instance de cet inventaire ?
|
||||
|
||||
Repère explicite, par instance : `setops_production` dans
|
||||
`<inventaire>/group_vars/all/`. Rétro-compatibilité : à défaut de drapeau,
|
||||
on retombe sur l'ancien repère (inventaire nommé « production »).
|
||||
"""
|
||||
base = path.parent / "group_vars" / "all"
|
||||
fichiers = sorted(base.glob("*.yml")) if base.is_dir() else []
|
||||
plat = path.parent / "group_vars" / "all.yml"
|
||||
if plat.exists():
|
||||
fichiers.append(plat)
|
||||
for f in fichiers:
|
||||
d = _lire_yaml_dict(f)
|
||||
if isinstance(d, dict) and "setops_production" in d:
|
||||
return bool(d["setops_production"])
|
||||
return path.resolve() == INVENTAIRE_PRODUCTION
|
||||
|
||||
|
||||
def intrants_courants() -> dict:
|
||||
cache: dict = {}
|
||||
valeurs: dict = {}
|
||||
|
|
@ -1829,8 +1848,8 @@ class Gestionnaire(BaseHTTPRequestHandler):
|
|||
if not MOTIF_HOTE.match(hote or ""):
|
||||
self.repondre_json(400, {"erreur": "Nom d'hote invalide."})
|
||||
return
|
||||
if self.inventaire != INVENTAIRE_PRODUCTION:
|
||||
self.repondre_json(409, {"erreur": "Deploiement reserve a l'inventaire production."})
|
||||
if not instance_autorise_deploiement(self.inventaire):
|
||||
self.repondre_json(409, {"erreur": "Deploiement non autorise pour cette instance (setops_production: false)."})
|
||||
return
|
||||
if hote not in hotes_actifs_fichier(self.inventaire):
|
||||
self.repondre_json(409, {"erreur": f"{hote} n'est pas un hote actif sauvegarde."})
|
||||
|
|
|
|||
Loading…
Reference in a new issue