diff --git a/.github/scripts/tests/test_oci_monitor_modify_contract.py b/.github/scripts/tests/test_oci_monitor_modify_contract.py
index 565b5bd0..625443f1 100644
--- a/.github/scripts/tests/test_oci_monitor_modify_contract.py
+++ b/.github/scripts/tests/test_oci_monitor_modify_contract.py
@@ -27,10 +27,31 @@ def extracted_stack_manager(scope):
class MonitorModifyContractTests(TestCase):
- def test_monitor_sends_modify_for_a_stack_and_recreate_for_a_single_instance(self):
+ def test_monitor_sends_modify_for_every_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)
+ self.assertIn('action: "update" | "modify" | "recover"', source)
+ self.assertIn('action: "modify",', source)
+ self.assertNotIn('"recreate"', source)
+
+ def test_the_editor_has_one_name_for_one_container_and_for_several(self):
+ menu = MENU.read_text(encoding='utf-8')
+ self.assertIn("('modify', translate('Modify: edit resources, network, paths and GPU'))", menu)
+ self.assertIn("('modify', translate('Modify extra paths and devices'))", menu)
+ self.assertEqual(menu.count("('recreate', translate("), 1)
+ self.assertIn("('recreate', translate('Recreate every container with its saved configuration'))", menu)
+
+ def test_recreate_on_a_single_instance_changes_nothing(self):
+ node = next(item for item in ast.parse(MENU.read_text(encoding='utf-8')).body
+ if isinstance(item, ast.FunctionDef) and item.name == 'manage_instance')
+ row = {'vmid': 101, 'reason': 'matched', 'stack': None, 'pending': False, 'status': 'installed'}
+ run_lifecycle = Mock()
+ scope = {'carry_records': lambda *_, **__: {}, 'check_selected': lambda _project, selected: selected,
+ 'translate': lambda text: text, '_run_lifecycle': run_lifecycle, '_manage_stack': Mock()}
+ exec(compile(ast.fix_missing_locations(ast.Module(body=[node], type_ignores=[])), str(MENU), 'exec'), scope)
+ ui = Mock()
+ self.assertFalse(scope['manage_instance'](Path('/inert'), ui, row, action='recreate'))
+ self.assertEqual(ui.method_calls, [])
+ run_lifecycle.assert_not_called()
def test_cli_and_monitor_wrapper_accept_the_modify_action(self):
self.assertIn('choices=("update", "modify", "recreate")', CLI.read_text(encoding='utf-8'))
diff --git a/AppImage/components/release-notes-modal.tsx b/AppImage/components/release-notes-modal.tsx
index 86c883e0..4cbcd68d 100644
--- a/AppImage/components/release-notes-modal.tsx
+++ b/AppImage/components/release-notes-modal.tsx
@@ -312,7 +312,7 @@ const CURRENT_VERSION_FEATURES = [
{
icon: ,
key: "releaseNotes.currentFeatures.ociUpdatesTab",
- text: "Updates for OCI containers — Update and Recreate open the same flow as the OCI menu, the backup taken before updating can be kept in a backup storage, and the image can be updated on a schedule.",
+ text: "Updates for OCI containers — Update and Modify open the same flow as the OCI menu, the backup taken before updating can be kept in a backup storage, and the image can be updated on a schedule.",
},
{
icon: ,
diff --git a/AppImage/components/virtual-machines.tsx b/AppImage/components/virtual-machines.tsx
index 031e88c3..722c72ae 100644
--- a/AppImage/components/virtual-machines.tsx
+++ b/AppImage/components/virtual-machines.tsx
@@ -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" | "modify" | "recreate" | "recover" } | null>(null)
+ const [ociAction, setOciAction] = useState<{ vmid: number; action: "update" | "modify" | "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
@@ -5343,11 +5343,11 @@ const handleDownloadLogs = async (vmid: number, vmName: string) => {
className={neutralBtnCls}
onClick={() => setOciAction({
vmid: selectedVM.vmid,
- action: ociInstance.stack ? "modify" : "recreate",
+ action: "modify",
})}
>
-
- {t("vmLxc.ociUpdates.recreate")}
+
+ {t("vmLxc.ociUpdates.modify")}
)}