PR 1218 review

harvester-ui-extension PR 1218 (wheatdog) at head dfbc960, reviewed 2026-11-04. It adds a Shut Down VM on Migration Failure control to the upgrade-config setting, with a banner that explains the selected behaviour, on v1.10.0 or later. It targets main and carries require backport/v1.10. Yu-Jack approved it, so it can merge at any time.

Preview: harvesterpr1218r.mf-created-dashboard.ui.rancher.space. Built from dfbc960, backend Harvester .118 (v1.9.0-rc4). Main baseline: the shared harvester-main preview, rebuilt today from 840f9de.

Verdict: the code works. Comment, and hold the merge for the backend. Grade A-

Summary

Since harvester PR 9326, a VM whose live migration fails in the upgrade pre-drain phase is always shut down. Backend PR 12125 makes that a choice through shutdownVMOnMigrationFailure, off by default, so the upgrade waits instead. This PR is the form for that field. The linked issue is a stub ("GUI Issue from 12036"), so I checked the form against the backend PR at 950e36f3d:

Backend PR saysForm on this PRResult
Field defaults to false, and a missing field counts as falseShows Disabled and saves falseMatch
Disabled: the VM keeps running and the upgrade waits. Unblock by fixing the migration, stopping the VM, or setting the field to trueThe Disabled banner says the same, with the same three ways outMatch
Enabled: the VM is shut down so the drain can go onThe Enabled banner says thisMatch
Enabled: the VM is started again after the node is upgraded, regardless of restoreVMNot mentionedGap, finding 1
A non-boolean value is treated as "unknown", and the VM is not shut downShows Disabled, and Save writes falseConsistent
Older versions ignore the field, so it can be set with kubectl before upgradingBelow v1.10.0 the control is hidden, and Save keeps a field that is already thereMatch
The field can change while an upgrade waits, and applies within one loopThe form opens and Save sends its PUT (to my stub) while .118's own upgrade is in progress. The real webhook was not exercisedAs far as tested
Walkthrough
Server version lifted to v1.10.0 in the browser. Disabled by default, enable it, the banner flips, and Save sends the real payload to a stub.
main: no shutdown control
main at v1.10.0: no control
PR on the real v1.9.0-rc4: no shutdown control
PR 1218 on the real v1.9.0-rc4: hidden, pixel-identical to main
PR at v1.10.0, Disabled, with the waiting banner
PR 1218 at v1.10.0, Disabled
PR at v1.10.0, Enabled, with the shutdown banner
PR 1218 at v1.10.0, Enabled

Test it yourself

The control needs v1.10.0 and .118 runs v1.9.0-rc4, so lift the version in your browser. Log in as admin (HARVESTER_PASSWORD). I ran these steps.

  1. Open Settings on the PR preview.
  2. Run this in DevTools:
    document.querySelector('#app').__vue_app__.config.globalProperties.$store.getters['harvester/byId']('harvesterhci.io.setting','server-version').value='v1.10.0'
  3. On upgrade-config, open ⋮ → Edit Setting. Don't reload, because that drops the lift.
  4. Toggle the control and watch the banner. Don't press Save: unlike my test runs, this writes to the shared .118. Use Cancel.
Blockers

None.

Suggestions
1

The Enabled banner doesn't say the VM comes back

Suggestion

What it's about

The text is: "If a VM fails to live migrate during the node pre-drain phase of an upgrade, the VM is shut down so that the upgrade can continue." That matched the backend when this PR was written. It no longer does.

What I found

After ihcsim asked for it on the backend PR, commit 950e36f3d records the VM before stopping it and starts it again after the node is upgraded. The new comment in settings_helper.go says: "the vm is started again after the node is upgraded, regardless of RestoreVM." Restore VM sits directly above this control. With Restore VM Disabled, which is the default, a reader concludes the VM stays off, and the opposite is true.

Suggested change

Extend the Enabled text: "…the VM is shut down so that the upgrade can continue, and it is started again after the node is upgraded, even if Restore VM is disabled."

Evidence

The banner text was observed on the preview. The restart was read from the backend diff (restorevm.go, job_controller.go, migration_failed_vm.go) and not run, because it needs a multi-node v1.10 upgrade. So this stays a suggestion. Paused VMs and the upgrade repo VM are excluded from the restart, which is too much detail for the banner.
Questions
2

Can this merge after backend PR 12125?

harvester/harvester PR 12125: open, mergeable_state: blocked
Question

What it's about

This PR is approved and its merge state is clean. On the backend PR, the approvals from ihcsim, bk201 and Yu-Jack are all dismissed, and no maintainer has reviewed the current head.

What I found

Without the backend change, upgrade_node.sh always shuts the VM down when its migration fails. A v1.10 build with this form and without that change would show the default as "the VM keeps running and the upgrade waits" and then stop the VM, which is the silent shutdown issue 12036 is about. The same applies to the backport into release-harvester-v1.10, already on 1.10.0-rc1. Merging second also lets finding 1 go in against the final backend.
Nits
3

The save test doesn't go through the radio

Nit

What I found

should save the selected value sets parseDefaultValue and calls update() directly. I broke the template three ways and all 6 tests still passed each time: radio bound to restoreVM, its @update:value removed, and string options instead of booleans. The gate, default and banner logic are well covered: breaking those four ways each failed a test.

Suggested change

Emit update:value from the stubbed radio instead. I tried it: the spec stays green, and it now fails for the first two breaks.
4

The new radio group has no accessible name

Optional, left out of the review
The accessibility tree shows an unnamed radiogroup with "Enabled" and "Disabled", because the <label> isn't tied to it. Restore VM has the same pattern and this copies it, so I left it out of the suggested review. RadioGroup reads aria-label or label, from its source, and I didn't run that fix. Your call if you want it raised.
What was checked

Run with Playwright on the two previews, with every write answered in the browser so nothing reached .118. Versions other than the real v1.9.0-rc4 are lifted by rewriting the server-version response, so hard reloads are covered too.

Casemain (840f9de)PR 1218 (dfbc960)
PR test steps: new field shown, Disabled by default, can be enabled and disabledNo controlAll pass
Enable: radio and banner flip together. Save sends a real booleantrue, others intact
Reopen a saved true, turn it off, savefalse, Restore VM stays true
Use the default value, with the current default and with the backend PR's new defaultBack to Disabled, saves false
Value customized on an older version (parallel + concurrency, manual + pauseNodes, logReadyTimeout)Shows Disabled, nothing lost
Enable it, then edit Restore VMBoth saved
Non-boolean "yes" left by an older versionShows Disabled, saves false
Real v1.9.0-rc4, nothing liftedNo control, no banner, field not added
Real v1.9.0-rc4, field already set with kubectlKept on Save
Version strings: v1.9.1 hidden; v1.10.0-rc1, v1.10.0-dev-20261101, v1.10.1, v1.11.0, master-abcd1234-head shownAs expected
Keyboard: Tab reaches the group, ArrowUp selects Enabled, the banner followsWorks
CancelSends nothing
Width 700pxNo horizontal scroll
Real upgrade-config on .118 after all of thisUnchanged, no value set
Value without nodeUpgradeOptionForm never rendersSame crash, so out of scope. Not raised
New spec6 of 6
Full jest suite158 passed, 1 skipped
CI at dfbc960lint, unit-tests, DCO, Mergify green
Merge with current main (840f9de)No conflicts
Backport to release-harvester-v1.10Applies cleanly

Not tested: a real upgrade with a failed migration, the webhook rejecting a non-boolean, the extension loaded through Rancher (it uses the same version getter), and the light theme.

Coverage
Filecorrectnesstestsa11yi18nconventionserrorsperf
upgrade-config.vueclearedclearedfindingclearedclearedclearedcleared
en-us.yamlfindingn-an-aclearedclearedn-an-a
feature-flags.jsclearedclearedn-an-aclearedn-an-a
upgrade-config.test.jsclearedfindingn-an-aclearedn-an-a

Per the repo's habits I didn't raise .js tests or the Options API. The method instead of a computed for the gate is justified: normalizeValue runs in data(), before computed properties exist, and the comment says so.

Suggested review (Comment). Not posted

I tested this on a preview against a real Harvester, with the version gate lifted to v1.10.0 in the browser. The control, the default, the saved value and the older-cluster path all work, and the backport to release-harvester-v1.10 applies cleanly. One wording point inline.

Could this merge after harvester/harvester#12125? The banners describe what that PR does. Until it lands, the default banner says the VM keeps running while the upgrade script still shuts it down.

Inline: en-us.yaml line 1649 (attach img/pr-enabled.png)

Since this was opened, your commit 950e36f3d on the backend PR starts these VMs again after the node is upgraded, regardless of Restore VM. This text stops at "shut down", and Restore VM sits right above it, so with Restore VM disabled it reads as if the VM stays off. Could we add that it comes back? For example: "…the VM is shut down so that the upgrade can continue, and it is started again after the node is upgraded, even if Restore VM is disabled."

Inline: upgrade-config.test.js line 58

Nit: this sets parseDefaultValue and calls update() directly, so it never goes through the radio. I bound the radio to restoreVM, then separately removed its @update:value, and all 6 tests still passed each time. Emitting update:value from the stub instead catches both.