mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2026-10-08 22:46:41 +00:00
fix(oci): preserve Monitor stack editor action
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
"""Keep the Monitor OCI editor path distinct from coordinated stack recreation."""
|
||||
import ast
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from types import ModuleType, SimpleNamespace
|
||||
from unittest import TestCase
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[3]
|
||||
MENU = ROOT / 'oci/src/proxmenux_oci/management.py'
|
||||
CLI = ROOT / 'oci/src/proxmenux_oci/cli.py'
|
||||
MONITOR = ROOT / 'AppImage/components/virtual-machines.tsx'
|
||||
|
||||
|
||||
def extracted_stack_manager(scope):
|
||||
source = MENU.read_text(encoding='utf-8').replace(
|
||||
' from .stack_recreation import modify_stack\n', '')
|
||||
node = next(item for item in ast.parse(source).body
|
||||
if isinstance(item, ast.FunctionDef) and item.name == '_manage_stack')
|
||||
exec(compile(ast.fix_missing_locations(ast.Module(body=[node], type_ignores=[])),
|
||||
str(MENU), 'exec'), scope)
|
||||
return scope['_manage_stack']
|
||||
|
||||
|
||||
class MonitorModifyContractTests(TestCase):
|
||||
def test_monitor_sends_modify_for_a_stack_and_recreate_for_a_single_instance(self):
|
||||
source = MONITOR.read_text(encoding='utf-8')
|
||||
self.assertIn('action: "update" | "modify" | "recreate" | "recover"', source)
|
||||
self.assertIn('action: ociInstance.stack ? "modify" : "recreate"', source)
|
||||
|
||||
def test_cli_accepts_the_monitor_modify_action(self):
|
||||
self.assertIn('choices=("update", "modify", "recreate")', CLI.read_text(encoding='utf-8'))
|
||||
|
||||
def test_modify_action_opens_the_stack_editor_without_running_lifecycle(self):
|
||||
primary = {'stack': {'members': [{'vmid': 101}]}}
|
||||
instances = ModuleType('oci_instances')
|
||||
instances.ROOT = Path('/inert')
|
||||
instances.read = lambda *_: primary
|
||||
modify_stack = Mock(return_value=True)
|
||||
run_lifecycle = Mock()
|
||||
manager = extracted_stack_manager({
|
||||
'sys': SimpleNamespace(path=[]),
|
||||
'Path': Path,
|
||||
'modify_stack': modify_stack,
|
||||
'_run_lifecycle': run_lifecycle,
|
||||
})
|
||||
ui = SimpleNamespace()
|
||||
with patch.dict(sys.modules, {
|
||||
'oci_instances': instances,
|
||||
'oci_stack_replay': ModuleType('oci_stack_replay'),
|
||||
}):
|
||||
result = manager(Path('/inert'), ui, {'vmid': 101}, action='modify')
|
||||
|
||||
self.assertTrue(result)
|
||||
modify_stack.assert_called_once_with(Path('/inert'), ui, primary, run_lifecycle)
|
||||
run_lifecycle.assert_not_called()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import unittest
|
||||
unittest.main()
|
||||
@@ -954,7 +954,7 @@ export function VirtualMachines() {
|
||||
// A container restored from a backup carries the mark of its installation
|
||||
// and has no record on this host until it is recovered from the menu.
|
||||
const [ociRestored, setOciRestored] = useState(false)
|
||||
const [ociAction, setOciAction] = useState<{ vmid: number; action: "update" | "recreate" | "recover" } | null>(null)
|
||||
const [ociAction, setOciAction] = useState<{ vmid: number; action: "update" | "modify" | "recreate" | "recover" } | null>(null)
|
||||
|
||||
// Firewall log state — fetched only when the operator opens that tab
|
||||
// so a CT/VM without firewall use doesn't pay the pvesh cost on every
|
||||
@@ -5341,7 +5341,10 @@ const handleDownloadLogs = async (vmid: number, vmName: string) => {
|
||||
<Button
|
||||
size="sm"
|
||||
className={neutralBtnCls}
|
||||
onClick={() => setOciAction({ vmid: selectedVM.vmid, action: "recreate" })}
|
||||
onClick={() => setOciAction({
|
||||
vmid: selectedVM.vmid,
|
||||
action: ociInstance.stack ? "modify" : "recreate",
|
||||
})}
|
||||
>
|
||||
<RotateCcw className="h-4 w-4 mr-1.5" />
|
||||
{t("vmLxc.ociUpdates.recreate")}
|
||||
|
||||
@@ -595,9 +595,9 @@ def build_parser() -> argparse.ArgumentParser:
|
||||
rclone_parser.add_argument("--host", default="auto")
|
||||
rclone_parser.add_argument("--dry-run", action="store_true")
|
||||
subparsers.add_parser("recover", help="Register again the OCI applications restored from a backup")
|
||||
manage_parser = subparsers.add_parser("manage", help="Update or recreate one installed OCI instance")
|
||||
manage_parser = subparsers.add_parser("manage", help="Update, modify or recreate one installed OCI instance")
|
||||
manage_parser.add_argument("vmid", type=int)
|
||||
manage_parser.add_argument("--action", choices=("update", "recreate"), required=True)
|
||||
manage_parser.add_argument("--action", choices=("update", "modify", "recreate"), required=True)
|
||||
manage_parser.add_argument("--keep-backup", metavar="STORAGE",
|
||||
help="Keep the backup taken before the update in this Proxmox storage")
|
||||
manage_parser.add_argument("--unattended", action="store_true",
|
||||
|
||||
@@ -285,6 +285,8 @@ def manage_instance(project, ui, row, action=None, lifecycle_args=()):
|
||||
return False
|
||||
if row['stack']:
|
||||
return _manage_stack(project, ui, row, action, lifecycle_args)
|
||||
if action == 'modify':
|
||||
return False
|
||||
if not row['pending']:
|
||||
if row['status'] != 'installed' or row['reason'] != 'matched':
|
||||
ui.message(translate('The instance identity or status must be reviewed before updating.'), translate('OCI management'))
|
||||
|
||||
Reference in New Issue
Block a user