fix: address scheduled stopped LXC review feedback

This commit is contained in:
VAIO73
2026-10-16 17:21:34 +00:00
parent fdbd420961
commit 9bc83a13ef
11 changed files with 31 additions and 14 deletions
+1 -1
View File
@@ -164,7 +164,7 @@ run_case stopped_restore RUN_HELPER=0 UPDATE_COMMAND='update-custom' MOCK_INITIA
run_case scheduled_stopped RUN_HELPER=0 UPDATE_COMMAND='update-custom' \
EXECUTION_MODE=scheduled SCHEDULED_STOPPED_MARKER='fixture-stopped-marker' MOCK_INITIAL_STATE=stopped
[[ $CASE_RC -eq 8 ]] || fail "scheduled_stopped returned $CASE_RC"
[[ $CASE_RC -eq 9 ]] || fail "scheduled_stopped returned $CASE_RC"
[[ $(count 'pct start 101') -eq 0 ]] || fail "scheduled update started a stopped CT"
[[ $(count 'pct exec') -eq 0 ]] || fail "scheduled update executed inside a stopped CT"
[[ $(count CUSTOM_EXEC) -eq 0 ]] || fail "scheduled update ran a custom updater"
+9 -3
View File
@@ -78,7 +78,7 @@ function walk(node) {
if (ts.isVariableDeclaration(node)) {
const name = node.name.getText(tree)
if (wanted.has(name)) definitions.push(`const ${node.getText(tree)};`)
if (["applySchedulePayload", "loadSchedule", "loadBulkUpdate"].includes(name))
if (["LEGACY_STOPPED_SCHEDULE_REASONS", "scheduleReasonCodeFromPersisted", "applySchedulePayload", "loadSchedule", "loadBulkUpdate"].includes(name))
preferenceDefinitions.push(`const ${node.getText(tree)};`)
if (["osUpdateStale", "osUpdateStatusKnown", "hasOsUpdates"].includes(name))
status.push(`const ${node.getText(tree)};`)
@@ -325,11 +325,11 @@ context.selectedVMRef.current = { vmid: 100 }
}
for (const name of ["ScheduleEnabled", "ScheduleConfigured", "ScheduleCron", "SchedulePreset",
"ScheduleTarget", "ScheduleTargets", "ScheduleReleaseDelayDays", "ApplyBackup", "ApplyBackupStorage",
"ApplyRestart", "ScheduleLastRunAt", "ScheduleLastRunStatus", "ScheduleLastRunReason", "ScheduleLastRunLog",
"ApplyRestart", "ScheduleLastRunAt", "ScheduleLastRunStatus", "ScheduleLastRunReason", "ScheduleLastRunReasonCode", "ScheduleLastRunLog",
"ScheduleLastRunRebootRequired", "ScheduleLastRunRebootPackages", "ExternalCron", "ScheduleError",
"ScheduleLoaded", "BulkError", "BulkLoaded", "BulkEditMode", "BulkConfigured", "BulkTargets", "BulkPersistedTargets"])
preferenceContext[`set${name}`] = value => { preferenceState[name] = value }
assert.equal(preferenceDefinitions.length, 3)
assert.equal(preferenceDefinitions.length, 5)
const preferences = compile(preferenceDefinitions.join("\n") + `
exports.apply = applySchedulePayload; exports.schedule = loadSchedule; exports.bulk = loadBulkUpdate;
`, preferenceContext)
@@ -340,6 +340,12 @@ context.selectedVMRef.current = { vmid: 100 }
assert.equal(preferenceState.ApplyRestart, false, "missing restart cannot inherit a reboot")
preferences.apply({ backup: false, backup_storage: "", restart: false })
assert.equal(preferenceState.ApplyBackupStorage, "")
preferences.apply({ last_run_reason_code: "stopped_container", last_run_reason: "raw stopped reason" })
assert.equal(preferenceState.ScheduleLastRunReasonCode, "stopped_container",
"schedule payload publishes its structured stopped reason")
preferences.apply({})
assert.equal(preferenceState.ScheduleLastRunReasonCode, null,
"a missing reason code cannot leak from the previously selected guest")
preferenceContext.vmModalCacheRef.current.schedule.set(213, { backup: true, backup_storage: "local", restart: true })
const previousSchedule = preferences.schedule(213)
assert.equal(preferenceState.ApplyBackup, true, "use only the selected guest's own cache")
@@ -73,7 +73,9 @@ for (const locale of ['en', 'sk']) {
const render = compile(context)
const translated = flattenText(render(true))
const expected = t('vmLxc.scheduled.reasonStoppedContainer')
const skippedLabel = t('vmLxc.scheduled.runSkippedStatus')
assert(translated.includes(expected), `${locale}: known reason code must use the locale catalog`)
assert(translated.includes(skippedLabel), `${locale}: skipped run must not claim that no updates are pending`)
assert(!translated.includes(context.scheduleLastRunReason), `${locale}: raw stopped reason must not leak into UI`)
context.scheduleLastRunReasonCode = 'unknown_reason'
@@ -135,7 +135,7 @@ raise SystemExit(h.main())
"_json_list": lambda value: json.loads(value or '[]'),
"_lxc_update_finalizations": {}, "_lxc_update_finalization_lock": threading.RLock(),
"_LXC_UPDATE_FINALIZATION_TTL": 3600,
"_SCHEDULED_STOPPED_EXIT": 8,
"_SCHEDULED_STOPPED_EXIT": 9,
"_fast_guest_status": lambda *_: self.state.read_text(),
"_lxc_update_snapshot": lambda *_: {"ct_name": "fixture"},
"_lxc_update_target_labels": lambda *_: ["OS"],
+8 -6
View File
@@ -362,17 +362,18 @@ class ScheduledStoppedContainerTests(unittest.TestCase):
def test_stopped_marker_requires_the_scheduler_token_not_only_exit_code(self):
marker = "PROXMENUX_SCHEDULED_STOPPED:fixture"
reader = self._stopped_marker_reader({"_SCHEDULED_STOPPED_EXIT": 8, "os": os})
self.assertTrue(reader(SimpleNamespace(returncode=8, stdout=marker, stderr=""), None, marker))
self.assertFalse(reader(SimpleNamespace(returncode=8, stdout="", stderr=""), None, marker))
reader = self._stopped_marker_reader({"_SCHEDULED_STOPPED_EXIT": 9, "os": os})
self.assertTrue(reader(SimpleNamespace(returncode=9, stdout=marker, stderr=""), None, marker))
self.assertFalse(reader(SimpleNamespace(returncode=9, stdout="", stderr=""), None, marker))
self.assertFalse(reader(SimpleNamespace(returncode=8, stdout=marker, stderr=""), None, marker))
self.assertFalse(reader(SimpleNamespace(returncode=4, stdout=marker, stderr=""), None, marker))
with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8") as log:
log.write("prefix\\n" + marker + "\\n")
log.flush()
self.assertTrue(reader(SimpleNamespace(returncode=8, stdout="", stderr=""), log.name, marker))
self.assertTrue(reader(SimpleNamespace(returncode=9, stdout="", stderr=""), log.name, marker))
def test_runner_race_to_stopped_is_reported_as_skipped_without_reboot_probe(self):
runner = MagicMock(return_value=SimpleNamespace(returncode=8, stdout="", stderr=""))
runner = MagicMock(return_value=SimpleNamespace(returncode=9, stdout="", stderr=""))
finalizer = MagicMock(return_value={"recorded": True})
reboot_probe = MagicMock()
stopped_marker = MagicMock(return_value=True)
@@ -388,7 +389,7 @@ class ScheduledStoppedContainerTests(unittest.TestCase):
"os": os,
"json": json,
"_APPLY_UPDATES_SCRIPT": "/bin/true",
"_SCHEDULED_STOPPED_EXIT": 8,
"_SCHEDULED_STOPPED_EXIT": 9,
"_DOCKER_ENGINE_INTEGRATED_COMMAND": "integrated-docker",
"_create_lxc_update_log": lambda _vmid, _run_id: (None, None),
"_fast_guest_status": MagicMock(return_value="running"),
@@ -420,6 +421,7 @@ class ScheduledStoppedContainerTests(unittest.TestCase):
stopped_marker.reset_mock()
stopped_marker.return_value = False
runner.return_value = SimpleNamespace(returncode=8, stdout="", stderr="")
reboot_probe.return_value = (None, [], None)
outcome = self._worker(scope)(101, {"target": "os"})
self.assertEqual(outcome["status"], "failure")