161 lines
5.4 KiB
Bash
Executable file
161 lines
5.4 KiB
Bash
Executable file
#!/usr/bin/env bash
|
||
set -euo pipefail
|
||
|
||
ROOT="${1:-.}"
|
||
|
||
need_file() {
|
||
local f="$1"
|
||
if [[ ! -f "$ROOT/$f" ]]; then
|
||
echo "Fichier introuvable: $f" >&2
|
||
exit 1
|
||
fi
|
||
}
|
||
|
||
echo "==> Vérification"
|
||
need_file "scripts/generate_services.py"
|
||
need_file "domains.yaml"
|
||
|
||
echo "==> Sauvegarde"
|
||
mkdir -p "$ROOT/.patch-backup-days-until-due-v3"
|
||
cp -a "$ROOT/scripts/generate_services.py" "$ROOT/.patch-backup-days-until-due-v3/generate_services.py.bak"
|
||
cp -a "$ROOT/domains.yaml" "$ROOT/.patch-backup-days-until-due-v3/domains.yaml.bak"
|
||
|
||
echo "==> Patch robuste de scripts/generate_services.py"
|
||
python3 - "$ROOT/scripts/generate_services.py" <<'PYEOF'
|
||
from pathlib import Path
|
||
import re
|
||
import sys
|
||
|
||
path = Path(sys.argv[1])
|
||
text = path.read_text(encoding="utf-8")
|
||
|
||
# 1) Ajouter les seuils lt/gte manquants dans render_probe_service, juste avant on_error
|
||
if 'vars.life_noc_threshold_unknown_gte' not in text:
|
||
marker = ' on_error = str(policy.get("on_error", "critical")).strip()\n'
|
||
inject = (
|
||
' if "unknown_gte" in thresholds:\n'
|
||
' lines.append(f\' vars.life_noc_threshold_unknown_gte = "{icinga_escape(str(thresholds["unknown_gte"]))}"\')\n'
|
||
' if "ok_lt" in thresholds:\n'
|
||
' lines.append(f\' vars.life_noc_threshold_ok_lt = "{icinga_escape(str(thresholds["ok_lt"]))}"\')\n'
|
||
' if "warning_lt" in thresholds:\n'
|
||
' lines.append(f\' vars.life_noc_threshold_warning_lt = "{icinga_escape(str(thresholds["warning_lt"]))}"\')\n'
|
||
' if "critical_lt" in thresholds:\n'
|
||
' lines.append(f\' vars.life_noc_threshold_critical_lt = "{icinga_escape(str(thresholds["critical_lt"]))}"\')\n'
|
||
)
|
||
if marker not in text:
|
||
raise SystemExit("Impossible de trouver le point d'injection des seuils dans render_probe_service")
|
||
text = text.replace(marker, inject + marker, 1)
|
||
|
||
# 2) Remplacer entièrement validate_probe(...)
|
||
new_validate = '''def validate_probe(item: dict) -> None:
|
||
probe = item["probe"]
|
||
if not isinstance(probe, dict):
|
||
raise ValueError("probe doit être un objet")
|
||
|
||
probe_type = str(probe.get("type", "")).strip()
|
||
if probe_type not in {"elapsed_time", "days_until_due"}:
|
||
raise ValueError("seuls probe.type=elapsed_time et days_until_due sont supportés")
|
||
|
||
source = probe.get("source")
|
||
if not isinstance(source, dict):
|
||
raise ValueError("probe.source doit être un objet")
|
||
if str(source.get("type", "")).strip() != "manual_date":
|
||
raise ValueError("seul probe.source.type=manual_date est supporté en v1")
|
||
|
||
has_inline_value = "value" in source
|
||
has_store_ref = "inputs_file" in source and "item_key" in source
|
||
if not has_inline_value and not has_store_ref:
|
||
raise ValueError("probe.source.value ou probe.source.inputs_file + item_key requis")
|
||
|
||
metric = probe.get("metric")
|
||
if not isinstance(metric, dict):
|
||
raise ValueError("probe.metric doit être un objet")
|
||
if str(metric.get("unit", "")).strip() != "days":
|
||
raise ValueError("seul probe.metric.unit=days est supporté en v1")
|
||
|
||
thresholds = probe.get("thresholds")
|
||
if not isinstance(thresholds, dict):
|
||
raise ValueError("probe.thresholds doit être un objet")
|
||
'''
|
||
|
||
pattern = r'def validate_probe\(item: dict\) -> None:\n(?: .*\n)+?(?=\ndef render_servicegroup|\ndef main\()'
|
||
new_text, count = re.subn(pattern, new_validate + "\n", text, flags=re.MULTILINE)
|
||
if count == 0:
|
||
raise SystemExit("Impossible de remplacer validate_probe(...) dans generate_services.py")
|
||
text = new_text
|
||
|
||
path.write_text(text, encoding="utf-8")
|
||
print("generate_services.py patché")
|
||
PYEOF
|
||
|
||
echo "==> Création du store"
|
||
mkdir -p "$ROOT/data/inputs"
|
||
cat > "$ROOT/data/inputs/obligations-legales-personnelles.yaml" <<'YAMLEOF'
|
||
renouvellement-permis-conduire:
|
||
value: "2026-09-20"
|
||
captured_at: "2026-03-14T23:30:00Z"
|
||
origin: manual
|
||
YAMLEOF
|
||
|
||
echo "==> Mise à jour de domains.yaml"
|
||
python3 - "$ROOT/domains.yaml" <<'PYEOF'
|
||
from pathlib import Path
|
||
import sys
|
||
import yaml
|
||
|
||
path = Path(sys.argv[1])
|
||
data = yaml.safe_load(path.read_text(encoding="utf-8"))
|
||
|
||
domain = "obligations-legales-personnelles"
|
||
items = data.get("domains", {}).get(domain)
|
||
if not isinstance(items, list):
|
||
raise SystemExit(f"Domaine introuvable ou invalide: {domain}")
|
||
|
||
target = None
|
||
for item in items:
|
||
if isinstance(item, dict) and item.get("name") == "renouvellement-permis-conduire":
|
||
target = item
|
||
break
|
||
|
||
if target is None:
|
||
raise SystemExit("Item 'renouvellement-permis-conduire' introuvable")
|
||
|
||
target["date"] = "2026-09-20"
|
||
target["notes"] = "Vérifier la proximité de l’échéance du permis"
|
||
target["probe"] = {
|
||
"type": "days_until_due",
|
||
"source": {
|
||
"type": "manual_date",
|
||
"inputs_file": "/opt/life-noc/data/inputs/obligations-legales-personnelles.yaml",
|
||
"item_key": "renouvellement-permis-conduire",
|
||
},
|
||
"metric": {
|
||
"unit": "days",
|
||
},
|
||
"thresholds": {
|
||
"critical_lt": 7,
|
||
"warning_lt": 30,
|
||
"ok_lt": 90,
|
||
"unknown_gte": 90,
|
||
},
|
||
"policy": {
|
||
"on_error": "critical",
|
||
},
|
||
}
|
||
|
||
path.write_text(yaml.safe_dump(data, sort_keys=False, allow_unicode=True), encoding="utf-8")
|
||
print("domains.yaml mis à jour")
|
||
PYEOF
|
||
|
||
echo "==> Régénération"
|
||
(
|
||
cd "$ROOT"
|
||
python3 scripts/generate_services.py
|
||
)
|
||
|
||
echo
|
||
echo "Patch terminé."
|
||
echo "Étapes suivantes :"
|
||
echo " python3 scripts/generate_services.py"
|
||
echo " make check"
|
||
echo " make deploy-with-bpm"
|