mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2026-11-01 18:32:13 +00:00
feat(oci): the menu shows the saved records being brought up to date.
This commit is contained in:
+2
-1
@@ -7919,5 +7919,6 @@
|
|||||||
"⚠ Disk data will NOT be erased.": "⚠ Los datos del disco NO se borrarán.",
|
"⚠ Disk data will NOT be erased.": "⚠ Los datos del disco NO se borrarán.",
|
||||||
"⚠ Disk will be unmounted and removed from /etc/fstab.": "⚠ El disco se desmontará y se eliminará de /etc/fstab.",
|
"⚠ Disk will be unmounted and removed from /etc/fstab.": "⚠ El disco se desmontará y se eliminará de /etc/fstab.",
|
||||||
"⚠ The /etc/fstab entry will be removed.": "⚠ Se eliminará la entrada /etc/fstab.",
|
"⚠ The /etc/fstab entry will be removed.": "⚠ Se eliminará la entrada /etc/fstab.",
|
||||||
"⚠ The disk will be unmounted.": "⚠ El disco se desmontará."
|
"⚠ The disk will be unmounted.": "⚠ El disco se desmontará.",
|
||||||
|
"Updating the copy of the record...": "Actualizando la copia del registro..."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -439,7 +439,7 @@ def install_template(ui, template: dict[str, Any], identifier: str, mode: str) -
|
|||||||
return None
|
return None
|
||||||
if result:
|
if result:
|
||||||
from .management import carry_records
|
from .management import carry_records
|
||||||
carry_records(PROJECT_ROOT)
|
carry_records(PROJECT_ROOT, shown=True)
|
||||||
vmids = {int(v) for v in [result.get("vmid"), *(result.get("stack_vmids") or {}).values()] if v}
|
vmids = {int(v) for v in [result.get("vmid"), *(result.get("stack_vmids") or {}).values()] if v}
|
||||||
_, removed = images.offer_removal(ui, sorted(vmids))
|
_, removed = images.offer_removal(ui, sorted(vmids))
|
||||||
_print_installation_summary(result, removed)
|
_print_installation_summary(result, removed)
|
||||||
@@ -461,7 +461,7 @@ def _rclone_mount(catalog: Catalog, ui) -> None:
|
|||||||
result = run_remote_rclone_mount(PROJECT_ROOT, template, deployment, "auto")
|
result = run_remote_rclone_mount(PROJECT_ROOT, template, deployment, "auto")
|
||||||
if result:
|
if result:
|
||||||
from .management import carry_records
|
from .management import carry_records
|
||||||
carry_records(PROJECT_ROOT)
|
carry_records(PROJECT_ROOT, shown=True)
|
||||||
console.msg_ok(f"Remote: {result['remote']}:")
|
console.msg_ok(f"Remote: {result['remote']}:")
|
||||||
console.msg_ok(f"{translate('Read/write')}: {result['read_write_path']}")
|
console.msg_ok(f"{translate('Read/write')}: {result['read_write_path']}")
|
||||||
console.msg_ok(f"{translate('Read-only')}: {result['read_only_path']}")
|
console.msg_ok(f"{translate('Read-only')}: {result['read_only_path']}")
|
||||||
@@ -562,6 +562,8 @@ def interactive(catalog: Catalog) -> int:
|
|||||||
f"{translate('All applications')} ({len(applications)})")
|
f"{translate('All applications')} ({len(applications)})")
|
||||||
elif selection == "manage":
|
elif selection == "manage":
|
||||||
from .management import interactive_management
|
from .management import interactive_management
|
||||||
|
console.show_logo()
|
||||||
|
console.msg_title(translate("Manage installed OCI applications"))
|
||||||
interactive_management(PROJECT_ROOT, ui)
|
interactive_management(PROJECT_ROOT, ui)
|
||||||
elif selection == "custom":
|
elif selection == "custom":
|
||||||
from .custom import explore
|
from .custom import explore
|
||||||
|
|||||||
@@ -125,22 +125,29 @@ def _run_lifecycle(command, title):
|
|||||||
console.msg_title(title)
|
console.msg_title(title)
|
||||||
environment = dict(os.environ, OCI_SPINNER='1' if sys.stdout.isatty() else '0')
|
environment = dict(os.environ, OCI_SPINNER='1' if sys.stdout.isatty() else '0')
|
||||||
completed = subprocess.run(command, env=environment, check=False)
|
completed = subprocess.run(command, env=environment, check=False)
|
||||||
carry_records(images.PROJECT_ROOT)
|
carry_records(images.PROJECT_ROOT, shown=True)
|
||||||
if sys.stdin.isatty():
|
if sys.stdin.isatty():
|
||||||
console.wait_for_enter(translate('Press Enter to return to the menu...'))
|
console.wait_for_enter(translate('Press Enter to return to the menu...'))
|
||||||
return completed.returncode == 0
|
return completed.returncode == 0
|
||||||
|
|
||||||
|
|
||||||
def carry_records(project, mount_stopped=True, vmids=None, verify=False):
|
def carry_records(project, mount_stopped=True, vmids=None, verify=False, shown=False):
|
||||||
"""Leave inside each container the copy of its record that a restore on
|
"""Leave inside each container the copy of its record that a restore on
|
||||||
another host needs. It is refreshed after every operation. Returns what
|
another host needs. It is refreshed after every operation. Returns what
|
||||||
was found for each container."""
|
was found for each container. Every installed application is looked at,
|
||||||
|
which takes a while with many of them: `shown` says so on the screen."""
|
||||||
|
from . import console
|
||||||
sys.path.insert(0, str(project / 'remote'))
|
sys.path.insert(0, str(project / 'remote'))
|
||||||
|
if shown:
|
||||||
|
console.msg_info(translate('Updating the copy of the record...'))
|
||||||
try:
|
try:
|
||||||
import oci_carried_record
|
import oci_carried_record
|
||||||
return oci_carried_record.sync(oci_carried_record.instances.ROOT, vmids, mount_stopped, verify)
|
return oci_carried_record.sync(oci_carried_record.instances.ROOT, vmids, mount_stopped, verify)
|
||||||
except (ImportError, OSError, ValueError, RuntimeError, subprocess.SubprocessError):
|
except (ImportError, OSError, ValueError, RuntimeError, subprocess.SubprocessError):
|
||||||
return {}
|
return {}
|
||||||
|
finally:
|
||||||
|
if shown:
|
||||||
|
console.stop_spinner()
|
||||||
|
|
||||||
|
|
||||||
def recover_automatically(project):
|
def recover_automatically(project):
|
||||||
@@ -245,7 +252,7 @@ def _interactive_management(project, ui):
|
|||||||
_clean_orphans(project)
|
_clean_orphans(project)
|
||||||
recover_automatically(project)
|
recover_automatically(project)
|
||||||
offer_recovery(project, ui)
|
offer_recovery(project, ui)
|
||||||
carry_records(project, mount_stopped=False)
|
carry_records(project, mount_stopped=False, shown=True)
|
||||||
rows = saved_inventory(project)
|
rows = saved_inventory(project)
|
||||||
if not rows:
|
if not rows:
|
||||||
ui.message(translate('No registered OCI containers are available for selection on this host.'), translate('OCI management'))
|
ui.message(translate('No registered OCI containers are available for selection on this host.'), translate('OCI management'))
|
||||||
|
|||||||
@@ -77,5 +77,43 @@ class CarriedRecordTests(unittest.TestCase):
|
|||||||
self.assertEqual(carried.mapped_root(custom), (200000, 300000))
|
self.assertEqual(carried.mapped_root(custom), (200000, 300000))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class ShownSyncTests(unittest.TestCase):
|
||||||
|
"""Every installed application is looked at after an operation, which
|
||||||
|
takes a while with many of them: the screen says what is being done."""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
sys.path.insert(0, str(ROOT / "src"))
|
||||||
|
from proxmenux_oci import console, management
|
||||||
|
self.console, self.management = console, management
|
||||||
|
|
||||||
|
def run_sync(self, outcome, **options):
|
||||||
|
from unittest.mock import patch
|
||||||
|
events = []
|
||||||
|
with patch.object(self.console, "msg_info", side_effect=lambda text: events.append(("info", text))), \
|
||||||
|
patch.object(self.console, "stop_spinner", side_effect=lambda *a, **k: events.append(("stop",))), \
|
||||||
|
patch.object(carried, "sync", side_effect=outcome) as sync:
|
||||||
|
result = self.management.carry_records(ROOT, **options)
|
||||||
|
return result, events, sync
|
||||||
|
|
||||||
|
def test_the_sync_is_announced_and_the_notice_is_cleared(self):
|
||||||
|
result, events, sync = self.run_sync(lambda *args: {120: "current"}, shown=True)
|
||||||
|
self.assertEqual(result, {120: "current"})
|
||||||
|
self.assertEqual([event[0] for event in events], ["info", "stop"])
|
||||||
|
self.assertEqual(events[0][1], "Updating the copy of the record...")
|
||||||
|
sync.assert_called_once()
|
||||||
|
|
||||||
|
def test_the_notice_is_cleared_when_the_sync_fails(self):
|
||||||
|
result, events, _sync = self.run_sync(OSError("registry"), shown=True)
|
||||||
|
self.assertEqual(result, {})
|
||||||
|
self.assertEqual([event[0] for event in events], ["info", "stop"])
|
||||||
|
|
||||||
|
def test_nothing_is_shown_unless_asked(self):
|
||||||
|
_result, events, sync = self.run_sync(lambda *args: {}, vmids=[120], verify=True)
|
||||||
|
self.assertEqual(events, [])
|
||||||
|
self.assertEqual(sync.call_args.args[1:], ([120], True, True))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|||||||
Reference in New Issue
Block a user