[FIX] déploiement : suivre une VM même sans installation ERPLibre
Décocher l'installation d'ERPLibre faisait disparaître le tableau de bord. La case « suivi » vivait DANS le groupe de l'installation, build_spec ne la recopiait même pas dans la spec, et l'épilogue était gardé par « if install or desktop » : sans rien à installer, il ne se passait rien. Le suivi devient un choix du DÉPLOIEMENT. Et sans rien à installer, la commande distante ne vaut plus « true » — journal vide, ✅ instantané : elle regarde la VM ARRIVER, attend cloud-init, puis relève système, noyau, adresse, disque et mémoire. Le journal cesse aussi d'annoncer une installation ERPLibre qui n'a pas lieu. --- EN --- Unchecking the ERPLibre install made the dashboard vanish. The "monitoring" checkbox lived INSIDE the install group, build_spec did not even copy it into the spec, and the deploy epilogue was gated by "if install or desktop": with nothing to install, nothing happened. Monitoring is now a DEPLOYMENT-level choice. And with nothing to install, the remote command is no longer "true" — empty log, instant ✅: it watches the VM ARRIVE, waits for cloud-init, then reports system, kernel, address, disk and memory. The log also stops announcing an ERPLibre install that never happens. Assisted-by: Claude Opus 5
This commit is contained in:
parent
7923e37e4f
commit
3c10ca2eb2
5 changed files with 377 additions and 10 deletions
|
|
@ -298,6 +298,10 @@ def build_spec(vms, domains, form):
|
|||
"python_provider": form.get("python_provider", ""),
|
||||
"app_store": form.get("app_store", "deb"),
|
||||
"install": form["install"],
|
||||
# Au NIVEAU DU DÉPLOIEMENT, pas de l'installation : une VM sans
|
||||
# ERPLibre se suit aussi (cloud-init, puis relevé système). Absent de
|
||||
# cette assemblée, le choix du formulaire n'atteignait jamais la spec.
|
||||
"monitor": form.get("monitor", True),
|
||||
"add_ssh_config": form["add_ssh_config"],
|
||||
"parallelism": form["parallelism"],
|
||||
}
|
||||
|
|
@ -1828,6 +1832,10 @@ def run_deploy_form(ctx, run_app: bool = True):
|
|||
}
|
||||
key = self.query_one("#f_key", Input).value.strip()
|
||||
return {
|
||||
# Le suivi est demandé au NIVEAU DU DÉPLOIEMENT, pas de
|
||||
# l'installation : décocher ERPLibre emportait la case avec
|
||||
# elle, et le tableau de bord ne s'ouvrait plus du tout.
|
||||
"monitor": self.query_one("#f_monitor", Checkbox).value,
|
||||
"res_label": (
|
||||
t("custom")
|
||||
if self.profile == "custom"
|
||||
|
|
|
|||
|
|
@ -80,7 +80,11 @@ def list_install_runs() -> list:
|
|||
|
||||
|
||||
def _launch_one(
|
||||
ip: str, remote_cmd: str, log_path: str, name: str = ""
|
||||
ip: str,
|
||||
remote_cmd: str,
|
||||
log_path: str,
|
||||
name: str = "",
|
||||
installs: bool = True,
|
||||
) -> None:
|
||||
"""Lance une install SSH DÉTACHÉE : attend le sshd, exécute, journalise
|
||||
la sortie puis écrit le marqueur de fin avec le code de sortie."""
|
||||
|
|
@ -102,7 +106,13 @@ def _launch_one(
|
|||
# log reste VIDE pendant tout le boot émulé et paraît « bloqué ».
|
||||
msg_wait = t("Waiting for the VM to start (boot + cloud-init)")
|
||||
msg_slow = t("(an emulated architecture can be slow; this is normal)")
|
||||
msg_ready = t("VM ready - starting the ERPLibre install")
|
||||
# « installation ERPLibre en cours » sur un déploiement qui n'installe
|
||||
# RIEN était un mensonge du journal : la ligne dit maintenant ce qui suit.
|
||||
msg_ready = (
|
||||
t("VM ready - starting the ERPLibre install")
|
||||
if installs
|
||||
else t("VM ready - taking its measurements")
|
||||
)
|
||||
msg_giveup = t(
|
||||
"cloud-init still running after 20 min - install starts anyway"
|
||||
" (it waits for cloud-init first)"
|
||||
|
|
@ -230,19 +240,26 @@ def _launch_one(
|
|||
def _log_header(vm: dict, branch: str, when: str) -> str:
|
||||
"""En-tête du log : date, VM, distribution, version, architecture, branche.
|
||||
Permet d'identifier l'installation d'un coup d'œil (et de ne jamais laisser
|
||||
le log vide pendant l'attente du boot)."""
|
||||
le log vide pendant l'attente du boot).
|
||||
|
||||
Sans branche, il n'y a rien à installer : le titre le dit et la ligne
|
||||
« Branche » disparaît, au lieu d'annoncer une installation ERPLibre qui
|
||||
n'aura pas lieu.
|
||||
"""
|
||||
distro = vm.get("distro") or "?"
|
||||
version = vm.get("version") or ""
|
||||
arch = vm.get("arch") or "?"
|
||||
bar = "=" * 64
|
||||
titre = t("installation") if branch else t("VM start-up")
|
||||
ligne_branche = f" Branche : {branch}\n" if branch else ""
|
||||
return (
|
||||
f"{bar}\n"
|
||||
f" ERPLibre — {t('installation')}\n"
|
||||
f" ERPLibre — {titre}\n"
|
||||
f" Date : {when}\n"
|
||||
f" VM : {vm['name']}\n"
|
||||
f" Distribution : {distro} {version}\n"
|
||||
f" Architecture : {arch}\n"
|
||||
f" Branche : {branch}\n"
|
||||
f"{ligne_branche}"
|
||||
f" IP : {vm['ip']}\n"
|
||||
f"{bar}\n\n"
|
||||
)
|
||||
|
|
@ -268,7 +285,11 @@ def launch_installs(vms: list[dict], branch: str, remote_cmd: str) -> str:
|
|||
# plus le même pour toutes. `remote_cmd` reste le défaut, ce qui laisse
|
||||
# intacts les appelants qui n'en fournissent qu'une.
|
||||
_launch_one(
|
||||
vm["ip"], vm.get("remote_cmd") or remote_cmd, log_path, vm["name"]
|
||||
vm["ip"],
|
||||
vm.get("remote_cmd") or remote_cmd,
|
||||
log_path,
|
||||
vm["name"],
|
||||
installs=bool(branch),
|
||||
)
|
||||
entries.append(
|
||||
{
|
||||
|
|
@ -411,6 +432,10 @@ def active_run():
|
|||
# avertissements connus et bénins) : on réutilise la MÊME logique de détection
|
||||
# que la suite de tests ERPLibre pour analyser les logs d'installation.
|
||||
_LST_IGNORE_WARNING = (
|
||||
# ssh annonce l'ajout d'une clé d'hôte à chaque PREMIÈRE connexion à une
|
||||
# VM neuve. Ce n'est pas un avertissement d'installation : compté, il
|
||||
# allumait la colonne ⚠ sur TOUTE installation, dès sa première ligne.
|
||||
"Warning: Permanently added",
|
||||
"have the same label:",
|
||||
"odoo.addons.code_generator.extractor_module_file: Ignore next error about"
|
||||
" ALTER TABLE DROP CONSTRAINT.",
|
||||
|
|
|
|||
|
|
@ -5654,6 +5654,31 @@ class TODO:
|
|||
"fi; "
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _qemu_vm_ready_report():
|
||||
"""Relevé de mise en route, pour une VM où l'on n'installe RIEN.
|
||||
|
||||
Sans lui, la commande distante valait « true » : le suivi affichait un
|
||||
✅ instantané sur un journal vide, ce qui n'apprend rien de la machine
|
||||
qu'on vient de créer. Ici, il y a une fin claire (le marqueur de sortie
|
||||
que pose le lanceur) et de quoi juger qu'elle est prête : système,
|
||||
noyau, adresse, disque, mémoire, et le verdict de cloud-init.
|
||||
"""
|
||||
return (
|
||||
f'echo "===> {t("VM start-up")}"; '
|
||||
". /etc/os-release 2>/dev/null || true; "
|
||||
f'echo " {t("system:")} ${{PRETTY_NAME:-?}}"; '
|
||||
f'echo " {t("kernel:")} $(uname -r) ($(uname -m))"; '
|
||||
f'echo " {t("address:")} '
|
||||
"$(hostname -I 2>/dev/null | awk '{print $1}')\"; "
|
||||
f'echo " {t("disk:")} '
|
||||
"$(df -h / | awk 'NR==2 {print $3\"/\"$2\" (\"$5\")\"}')\"; "
|
||||
f'echo " {t("memory:")} '
|
||||
"$(free -h 2>/dev/null | awk 'NR==2 {print $3\"/\"$2}')\"; "
|
||||
f'echo " {t("uptime:")} $(uptime -p 2>/dev/null || true)"; '
|
||||
f'echo "<=== {t("VM start-up")}"; '
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _qemu_no_auto_upgrade(prod, app_store="deb"):
|
||||
"""Coupe les mises à jour automatiques sur une VM de DÉVELOPPEMENT.
|
||||
|
|
@ -7236,7 +7261,15 @@ class TODO:
|
|||
# attente de cloud-init et coupure des mises à jour automatiques,
|
||||
# sans quoi le verrou apt ferait échouer l'installation du bureau.
|
||||
if not desktop:
|
||||
return "true"
|
||||
# Rien à installer : le suivi n'a alors qu'à regarder la VM
|
||||
# ARRIVER. Un « true » rendait un journal vide et un ✅
|
||||
# instantané — et c'est pourquoi le suivi « ne marchait plus »
|
||||
# dès qu'on décochait ERPLibre.
|
||||
return (
|
||||
"set -e; "
|
||||
+ self._qemu_cloud_init_wait()
|
||||
+ self._qemu_vm_ready_report()
|
||||
)
|
||||
# Les outils de la phase « after » vivent DANS le dépôt — la
|
||||
# compilation mobile, l'AVD, le script Forgejo. Sans clone, ils
|
||||
# n'existent pas ici. Les écarter en silence laissait croire qu'une
|
||||
|
|
@ -8970,6 +9003,15 @@ class TODO:
|
|||
"cmd": cmd,
|
||||
"monitor": monitor,
|
||||
}
|
||||
else:
|
||||
# Rien à installer : le suivi garde tout son sens — il regarde les
|
||||
# VM arriver (cloud-init, puis relevé système) et porte le tableau
|
||||
# d'état, de débit d'écriture, de RAM et de disque. La question
|
||||
# était posée DANS la branche ERPLibre : refuser l'une emportait
|
||||
# l'autre sans qu'on l'ait demandé.
|
||||
monitor = self._is_yes_default_yes(
|
||||
input(f"{t('Watch the VMs start (no install)')} ? (O/n) : ")
|
||||
)
|
||||
|
||||
add_ssh_config = self._is_yes_default_yes(
|
||||
input(t("Add each VM to ~/.ssh/config? (Y/n): "))
|
||||
|
|
@ -9030,6 +9072,9 @@ class TODO:
|
|||
"python_provider": python_provider,
|
||||
"app_store": app_store,
|
||||
"install": install,
|
||||
# Au niveau du déploiement : le suivi survit à une installation
|
||||
# décochée (voir _qemu_run_spec).
|
||||
"monitor": monitor,
|
||||
"add_ssh_config": add_ssh_config,
|
||||
"parallelism": parallelism,
|
||||
}
|
||||
|
|
@ -9191,8 +9236,16 @@ class TODO:
|
|||
# 7) Installation ERPLibre (clone + make) et/ou bureau GNOME. Le bureau
|
||||
# ne dépend PAS d'ERPLibre : une VM peut être voulue graphique et nue.
|
||||
# Il passe par la même commande distante, donc par le même suivi.
|
||||
if install or desktop:
|
||||
monitor = install["monitor"] if install else True
|
||||
#
|
||||
# Et quand il n'y a RIEN à installer, le suivi s'ouvre quand même : la
|
||||
# commande distante regarde alors la VM arriver (cloud-init puis relevé
|
||||
# système). Sans cela, décocher ERPLibre faisait disparaître le tableau
|
||||
# de bord — rapporté, et c'est ce qui donnait « le suivi ne fonctionne
|
||||
# plus ». Le choix vient du déploiement, pas de l'installation.
|
||||
monitor = (
|
||||
install["monitor"] if install else spec.get("monitor", True)
|
||||
)
|
||||
if install or desktop or monitor:
|
||||
if monitor:
|
||||
# Installs détachées en parallèle + dashboard Textual.
|
||||
self._qemu_install_erplibre_monitored(
|
||||
|
|
@ -9206,7 +9259,7 @@ class TODO:
|
|||
app_store=app_store,
|
||||
vm_tools=vm_tools,
|
||||
)
|
||||
else:
|
||||
elif install:
|
||||
print(
|
||||
f"\n{t('Installing ERPLibre on each VM')} "
|
||||
f"({install_branch})…"
|
||||
|
|
|
|||
|
|
@ -3115,6 +3115,38 @@ TRANSLATIONS = {
|
|||
"fr": "Nœud de rendu",
|
||||
"en": "Render node",
|
||||
},
|
||||
"VM ready - taking its measurements": {
|
||||
"fr": "VM prête — relevé en cours",
|
||||
"en": "VM ready - taking its measurements",
|
||||
},
|
||||
"VM start-up": {
|
||||
"fr": "Mise en route de la VM",
|
||||
"en": "VM start-up",
|
||||
},
|
||||
"system:": {
|
||||
"fr": "système :",
|
||||
"en": "system:",
|
||||
},
|
||||
"kernel:": {
|
||||
"fr": "noyau :",
|
||||
"en": "kernel: ",
|
||||
},
|
||||
"address:": {
|
||||
"fr": "adresse :",
|
||||
"en": "address:",
|
||||
},
|
||||
"disk:": {
|
||||
"fr": "disque :",
|
||||
"en": "disk: ",
|
||||
},
|
||||
"uptime:": {
|
||||
"fr": "actif :",
|
||||
"en": "uptime: ",
|
||||
},
|
||||
"Watch the VMs start (no install)": {
|
||||
"fr": "Suivre le démarrage des VM (sans installation)",
|
||||
"en": "Watch the VMs start (no install)",
|
||||
},
|
||||
"Proxmox VE hypervisor (no Odoo)": {
|
||||
"fr": "Hyperviseur Proxmox VE (sans Odoo)",
|
||||
"en": "Proxmox VE hypervisor (no Odoo)",
|
||||
|
|
|
|||
249
test/test_qemu_deploy_monitor.py
Normal file
249
test/test_qemu_deploy_monitor.py
Normal file
|
|
@ -0,0 +1,249 @@
|
|||
#!/usr/bin/env python3
|
||||
# © 2026 TechnoLibre (http://www.technolibre.ca)
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
|
||||
"""Suivre un déploiement qui n'installe PAS ERPLibre.
|
||||
|
||||
Symptôme rapporté : en décochant l'installation d'ERPLibre, le tableau de bord
|
||||
ne s'ouvrait plus du tout. Deux causes, l'une derrière l'autre :
|
||||
|
||||
- la case « suivi » vivait DANS le groupe de l'installation ERPLibre, et
|
||||
`build_spec` ne la recopiait même pas dans la spec finale ;
|
||||
- l'épilogue du déploiement était gardé par « if install or desktop » : sans
|
||||
rien à installer, il ne se passait rien.
|
||||
|
||||
Et si le suivi s'ouvrait quand même, il n'aurait rien montré : la commande
|
||||
distante valait « true », donc un journal vide et un ✅ instantané. Elle
|
||||
regarde maintenant la VM ARRIVER — cloud-init, puis un relevé système — ce qui
|
||||
donne un début, une fin, et de quoi juger qu'elle est prête.
|
||||
"""
|
||||
|
||||
import contextlib
|
||||
import io
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
sys.argv = ["todo.py"]
|
||||
from script.todo import qemu_install_monitor as mon # noqa: E402
|
||||
from script.todo.qemu_deploy_form import build_spec # noqa: E402
|
||||
from script.todo.todo import TODO # noqa: E402
|
||||
|
||||
|
||||
class TestLaCommandeDistante(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.todo = TODO.__new__(TODO)
|
||||
|
||||
def _sans_rien(self):
|
||||
return self.todo._qemu_erplibre_remote_cmd(None)
|
||||
|
||||
def test_it_is_no_longer_a_bare_true(self):
|
||||
"""« true » rendait un journal vide et un ✅ instantané : le suivi
|
||||
s'ouvrait sur rien."""
|
||||
self.assertNotEqual("true", self._sans_rien().strip())
|
||||
|
||||
def test_it_is_valid_shell(self):
|
||||
res = subprocess.run(
|
||||
["bash", "-n"],
|
||||
input=self._sans_rien(),
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
self.assertEqual(0, res.returncode, res.stderr)
|
||||
|
||||
def test_it_waits_for_cloud_init(self):
|
||||
"""C'est le vrai « suivi » d'une VM neuve : tant que cloud-init tourne,
|
||||
la machine n'est pas prête."""
|
||||
self.assertIn("cloud-init status --wait", self._sans_rien())
|
||||
|
||||
def test_it_frames_the_step_so_the_monitor_can_bound_it(self):
|
||||
"""Le tableau lit ces marqueurs pour dire où en est la VM."""
|
||||
cmd = self._sans_rien()
|
||||
self.assertIn("===>", cmd)
|
||||
self.assertIn("<===", cmd)
|
||||
|
||||
def test_it_reports_what_says_the_vm_is_up(self):
|
||||
cmd = self._sans_rien()
|
||||
for morceau in ("/etc/os-release", "uname -r", "hostname -I", "df -h"):
|
||||
self.assertIn(morceau, cmd, morceau)
|
||||
|
||||
def test_it_runs_and_says_something(self):
|
||||
"""Exécutée ici, hors VM : l'attente de cloud-init est neutralisée, le
|
||||
relevé doit sortir et rendre 0."""
|
||||
cmd = self._sans_rien().replace(
|
||||
"sudo timeout 900 cloud-init status --wait", "true"
|
||||
)
|
||||
res = subprocess.run(
|
||||
["bash", "-c", cmd], capture_output=True, text=True, timeout=60
|
||||
)
|
||||
self.assertEqual(0, res.returncode, res.stderr[-300:])
|
||||
self.assertIn("===>", res.stdout)
|
||||
self.assertIn("<===", res.stdout)
|
||||
# Trois lignes de relevé au moins : sans elles, le journal est creux.
|
||||
self.assertGreaterEqual(len(res.stdout.strip().splitlines()), 5)
|
||||
|
||||
def test_a_desktop_only_vm_still_installs_its_desktop(self):
|
||||
"""Le chemin qui marchait déjà ne doit pas changer de sens."""
|
||||
self.todo._qemu_desktop_remote_cmd = lambda d, s: "INSTALLE_BUREAU; "
|
||||
self.todo._qemu_tools_remote_cmd = lambda *a, **k: ""
|
||||
self.todo._qemu_no_auto_upgrade = lambda *a, **k: ""
|
||||
cmd = self.todo._qemu_erplibre_remote_cmd(None, desktop="gnome")
|
||||
self.assertIn("INSTALLE_BUREAU", cmd)
|
||||
|
||||
|
||||
class TestLaSpecDuFormulaire(unittest.TestCase):
|
||||
def _form(self, **extra):
|
||||
base = {
|
||||
"res_label": "x1",
|
||||
"ssh_key": "/k.pub",
|
||||
"install": None,
|
||||
"add_ssh_config": False,
|
||||
"parallelism": 1,
|
||||
}
|
||||
base.update(extra)
|
||||
return base
|
||||
|
||||
def test_the_choice_reaches_the_spec(self):
|
||||
"""Il ne la recopiait pas : le choix du formulaire n'atteignait jamais
|
||||
le déploiement."""
|
||||
spec = build_spec([], [], self._form(monitor=True))
|
||||
self.assertTrue(spec["monitor"])
|
||||
spec = build_spec([], [], self._form(monitor=False))
|
||||
self.assertFalse(spec["monitor"])
|
||||
|
||||
def test_an_old_form_without_the_key_still_monitors(self):
|
||||
"""Compatibilité : une spec enregistrée avant ce changement ne doit pas
|
||||
perdre son tableau de bord."""
|
||||
self.assertTrue(build_spec([], [], self._form())["monitor"])
|
||||
|
||||
|
||||
class TestLaDecisionDuDeploiement(unittest.TestCase):
|
||||
"""L'épilogue : qui est appelé, et avec quoi."""
|
||||
|
||||
def _joue(self, spec):
|
||||
todo = TODO.__new__(TODO)
|
||||
appels = []
|
||||
todo._qemu_install_erplibre_monitored = lambda *a, **k: appels.append(
|
||||
"suivi"
|
||||
)
|
||||
todo._qemu_install_erplibre_vm = lambda *a, **k: appels.append("muet")
|
||||
todo._qemu_resolve_ips = lambda names, labels=None: {}
|
||||
base = {
|
||||
"vms": [],
|
||||
"existing": ["vm-a"],
|
||||
"install": None,
|
||||
"add_ssh_config": False,
|
||||
"parallelism": 1,
|
||||
}
|
||||
base.update(spec)
|
||||
with contextlib.redirect_stdout(io.StringIO()):
|
||||
todo._qemu_run_spec(base)
|
||||
return appels
|
||||
|
||||
def test_without_erplibre_the_monitor_still_opens(self):
|
||||
"""Le cœur du problème rapporté."""
|
||||
self.assertEqual(["suivi"], self._joue({"monitor": True}))
|
||||
|
||||
def test_a_spec_without_the_key_monitors_too(self):
|
||||
self.assertEqual(["suivi"], self._joue({}))
|
||||
|
||||
def test_refusing_the_monitor_does_nothing_at_all(self):
|
||||
"""Et surtout : ne pas partir installer un profil qui n'existe pas.
|
||||
L'ancien repli faisait « install['cmd'] » sur un None."""
|
||||
self.assertEqual([], self._joue({"monitor": False}))
|
||||
|
||||
def test_an_install_without_the_monitor_takes_the_quiet_path(self):
|
||||
appels = self._joue(
|
||||
{
|
||||
"monitor": False,
|
||||
"install": {
|
||||
"branch": "develop",
|
||||
"prod": False,
|
||||
"cmd": "make x",
|
||||
"monitor": False,
|
||||
},
|
||||
}
|
||||
)
|
||||
self.assertEqual(["muet"], appels)
|
||||
|
||||
def test_the_install_keeps_the_last_word_on_its_own_monitoring(self):
|
||||
appels = self._joue(
|
||||
{
|
||||
"monitor": False,
|
||||
"install": {
|
||||
"branch": "develop",
|
||||
"prod": False,
|
||||
"cmd": "make x",
|
||||
"monitor": True,
|
||||
},
|
||||
}
|
||||
)
|
||||
self.assertEqual(["suivi"], appels)
|
||||
|
||||
|
||||
class TestLeJournal(unittest.TestCase):
|
||||
"""L'en-tête et le prologue ne doivent pas annoncer ce qui n'a pas lieu."""
|
||||
|
||||
def _vm(self):
|
||||
return {
|
||||
"name": "vm-a",
|
||||
"ip": "10.0.0.9",
|
||||
"distro": "debian",
|
||||
"version": "13",
|
||||
"arch": "amd64",
|
||||
}
|
||||
|
||||
def test_an_install_is_titled_an_install(self):
|
||||
head = mon._log_header(self._vm(), "develop", "2026-01-01 00:00:00")
|
||||
self.assertIn(mon.t("installation"), head)
|
||||
self.assertIn("develop", head)
|
||||
|
||||
def test_without_a_branch_it_is_not_called_an_install(self):
|
||||
"""« ERPLibre — installation » puis « Branche : » vide, sur un
|
||||
déploiement qui n'installe rien : le journal se contredisait."""
|
||||
head = mon._log_header(self._vm(), "", "2026-01-01 00:00:00")
|
||||
self.assertIn(mon.t("VM start-up"), head)
|
||||
self.assertNotIn("Branche", head)
|
||||
|
||||
def test_the_prologue_says_what_actually_follows(self):
|
||||
"""« installation ERPLibre en cours » alors que rien ne s'installe."""
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
vus = []
|
||||
vrai_launch, vrai_dir = mon._launch_one, mon.session_dir
|
||||
mon._launch_one = (
|
||||
lambda ip, cmd, log, name="", installs=True: vus.append(installs)
|
||||
)
|
||||
# session_dir détournée : sans cela le test écrivait de VRAIES sessions
|
||||
# dans ~/.erplibre/qemu-install, qui polluaient l'historique que
|
||||
# « Rouvrir le suivi » propose à l'utilisateur.
|
||||
tmp = tempfile.TemporaryDirectory()
|
||||
self.addCleanup(tmp.cleanup)
|
||||
mon.session_dir = lambda: Path(tmp.name)
|
||||
try:
|
||||
mon.launch_installs([self._vm()], "", "true")
|
||||
mon.launch_installs([self._vm()], "develop", "true")
|
||||
finally:
|
||||
mon._launch_one, mon.session_dir = vrai_launch, vrai_dir
|
||||
self.assertEqual([False, True], vus)
|
||||
|
||||
def test_the_host_key_notice_is_not_a_warning(self):
|
||||
"""ssh l'écrit à CHAQUE première connexion : comptée, la colonne ⚠
|
||||
s'allumait sur toute installation, dès sa première ligne."""
|
||||
import tempfile
|
||||
|
||||
with tempfile.NamedTemporaryFile(
|
||||
"w", suffix=".log", delete=False, encoding="utf-8"
|
||||
) as fh:
|
||||
fh.write(
|
||||
"Warning: Permanently added '10.0.0.9' (ED25519) to the"
|
||||
" list of known hosts.\n__ERPLIBRE_EXIT__ 0\n"
|
||||
)
|
||||
chemin = fh.name
|
||||
self.addCleanup(os.unlink, chemin)
|
||||
self.assertEqual((0, 0), mon.scan_log_errors(chemin))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main(verbosity=1)
|
||||
Loading…
Reference in a new issue