PR 19768: Hide Scale Down on machines in an autoscaling pool

rancher/dashboard PR 19768 (codyrancher) at head 6709cc33e, tested 2026-11-04. It answers izaac's reopen of issue 15656: the pool header disables − and +, but a machine's row menu still offered Scale Down, and the autoscaler brought the node back.

Preview: rancherpr19768r.mf-created-dashboard.ui.rancher.space, built from 6709cc33e, backend mf Rancher (v2.16.0-7758e3e-head). Baseline: the shared dashboard-master preview. These files have not changed on master since it was built.

Verdict: approve

Test it yourself

Don't click Delete in a Scale Down dialog. These previews talk to the real mf Rancher, so the write is real. On master, confirming on the autoscale machine removes the worker that hosts these previews.
  1. Log in as the local admin. The login page defaults to OpenLDAP, so click "Log in as a local user" first.
  2. The fix: open "PR: Machine Pools" and the kebab on created-dashboard-2-autoscale-…. There is no Scale Down, and − + are disabled. On "master: Machine Pools" it's there.
  3. The blank table (card 3): open "PR: Clusters" or "master: Clusters", then Advanced > Machines in the side nav. The table is empty while the nav says 5, and the console shows t is not iterable. Reload and the rows come back.
  4. The route in card 2: open the longhorn cluster's Machine Pools, then Advanced > Machines. The autoscale machine still offers Scale Down. Clicking it opens nothing, and the console shows reading 'nodes'.

Manual, paused and bulk can't be walked by hand on this cluster, since it has one autoscaling pool and two machines. I ran those with fixtures, listed in the table below.

Master row menu listing Scale Down for an autoscaling machine
master: Scale Down on a machine in the autoscaling pool, with − + disabled above it.
PR row menu without Scale Down
PR: the same machine, no Scale Down. The rest of the menu and its dividers are intact.
Master bulk dialog deleting 4 machines
master, 4 selected: "delete 4 Machines", including both autoscaling ones.
PR bulk dialog deleting 2 machines
PR, same 4 selected: "delete 2 Machines", only the manual and paused ones.
PR row menu listing Scale Down for a paused pool machine
PR, fixture: a machine in a paused pool keeps Scale Down. The menu opens upward from the highlighted paused-x-a row.
Findings
1

Two test steps can't be run on the YAML in the description

PR description, "Areas or cases that should be tested"
Nit

The YAML gives one autoscaling pool (workers) and one etcd/CP machine (cp). created-dashboard-2 has the same shape, so I walked the steps on it.

QA is doing this one by hand (the issue has QA/dev-automation, and IsaSih's comment asks for manual tests), so it's worth a line. A second autoscaling pool and a manual worker pool in the YAML make both steps runnable.

Tooltip: this is the only pool left autoscaling
The disabled pause button on a pool shaped like the one in the YAML.
2

The "pre-existing" note: Scale Down can still be listed, but it can't fire

PR description: "Advanced > Machines may offer it before the pool template loads"
FYI

The note is right that a gap exists, but the cause I could reach is different, and the result is milder. Open a different cluster's detail page, then Advanced > Machines. The store then has the autoscaling machine's pool and template, but not its provisioning cluster. So inClusterSpec finds no pool spec, isAutoscalerEnabled is false, and Scale Down is listed.

Clicking it throws Cannot read properties of undefined (reading 'nodes') in the dialog's data(). No dialog opens and no write is sent. Master behaves the same. I would leave the code as is and, at most, adjust the wording of the note.

PR Advanced Machines list offering Scale Down
PR, after the longhorn cluster's detail page: Scale Down listed for the created-dashboard-2 autoscaling machine.
3

Same on master, not this PR: Advanced > Machines goes blank

shell/models/cluster.x-k8s.io.machine.js, canScaleDown → notOnlyOfRole
Your call

Go to Clusters or MachineDeployments, then Advanced > Machines. The table is empty, with "All (0)" beside a nav count of 5, and the console shows TypeError: t is not iterable. Clusters is where Cluster Management opens, so this is the normal way in. A direct load works, so a reload hides it.

Why: those pages load the MachineDeployments, so pool resolves, but the provisioning clusters aren't in the store. canScaleDown then calls notOnlyOfRole(this, undefined). For any etcd or control plane machine, for (const m of all) throws. Commit 801f8de51 (issue 9973, 2023) added the ?. for this same case, which moved the crash into the helper.

Evidence: reproduced on both builds from both routes. Wrapping the getter in a try/catch in the running page brings back all 5 rows with no errors, so this throw alone causes it. I found no open issue for it, from three searches.

Possible fix, one line: pass this.cluster?.machines || []. An etcd/CP machine then answers "can't scale down" until its cluster is loaded. A unit test on it passes with the guard and fails without it. I tested this locally and did not build a preview with it.

Your call: this is outside the PR's scope, so I left it out of the suggested review. I can file an issue, or you can mention it to Cody since he's in this getter already.

Empty Machines table with a nav count of 5
PR, arriving from another page: empty Machines table, nav count 5. master looks the same.
4

Also hidden when cluster-autoscaling is off or the whole cluster is paused

Follows from reusing isAutoscalerEnabled
FYI

Both match the pool header, and the PR says so for the flag. For the cluster pause, keeping scaling disabled is the decision from PR 19220 (quantity goes stale). With the flag off, the edit form still shows the quantity field (MachinePool.vue:358; read, not run), so the pool isn't stuck. I faked the flag in the browser and didn't check what the backend does in that state. No action needed.

Master vs PR, live
CasemasterPR
Real cluster, no fixtures (created-dashboard-2: pool1 is the only etcd/CP machine, autoscale has min 1, max 1)
Row menu, autoscaling machineofferedhidden
Row menu, last etcd/CP machinehiddenhidden
Pool header − and + on the autoscaling pooldisableddisabled
Machine detail page, opened from the cluster pageofferedhidden
Machine detail page after a hard reload (pool not loaded)hiddenhidden
Bulk menu with both machines selectedScale Down listednot listed
Fixtures added to the real responses in the browser (writes captured, never sent)
Row menu, manual poolofferedoffered
Row menu, paused poolofferedoffered
Row menu, etcd machine with another etcd machine leftofferedoffered
Whole cluster paused, machine in a pool with boundsoffered, − + disabledhidden, − + disabled
cluster-autoscaling off, pool still has boundsoffered, − + disabledhidden, − + disabled
User without update on the machinehiddenhidden
Pause, then Resume, on the same page without a reloadoffered throughouthidden → offered → hidden. Saved spec stashes and restores min 1, max 4, quantity 2
Bulk, only an autoscaling machine selectedScale Down enabledScale Down disabled
Bulk, 4 selected: 2 autoscaling, 1 manual, 1 pausedAnnotates all 4. autoscale 1 → 0 (below its min of 1), auto2 2 → 1Annotates the manual and paused machines only. Both autoscaling pools keep their quantity
Bulk, 2 selected, only 1 can run"Delete 2 Machines", autoscale 1 → 0Dialog names the manual machine, 1 write
Confirm Scale Down on a manual, paused and etcd machineannotation, plus quantity −1 on that pool onlyidentical writes
Advanced > Machines, by the route you take to get there
Direct loadhidden on every row (no pool in the store)same
After this cluster's detail pageofferedhidden
After a different cluster's detail pageoffered, dialog crashes on click, no writesame (card 2)
After Clusters or MachineDeploymentstable blanksame (card 3)
Also checked
Unit testsThe 8 new tests pass, and 56 of 56 across the machine, machine deployment and scale-down dialog suites. Removing the new condition fails exactly the autoscaling test.
Team checklistEdit to an existing JS model, new test in TS, toStrictEqual, no i18n, style or a11y change. Master is still 2.16 (no release-2.16 branch), and milestone v2.16.0 matches the issue.
Other Scale Down surfacesThis action is the only reader of the machine's canScaleDown. RKE1 nodes have their own getter, and pool-level − stays guarded in the header.
New read on the row menu pathinClusterSpec reads annotations[…] unguarded when a template exists, and row menus now reach it. All 4 Amazon EC2 templates on mf carry the annotation, and the header already reads the same getter. Not raised.
Record<string, any> in the test mockAgainst the checklist's letter, but 5 other model tests do the same. Not raised.
Not testedReal Standard User and User Base logins: I used a fixture with no update link, and the unit tests cover machine and pool permissions. The change can only hide the action, so it can't widen access. A real machine being removed: writes were captured and never sent, because the worker hosts the previews. RKE1, custom, Elemental and Harvester clusters: by reading the code only. Light and dark: no visual change.
Left runningPreview dashboard-pr19768. Remove with bash preview/cleanup-branch.sh dashboard pr19768. Nothing changed on mf or on the cluster beyond that preview and this page.
Suggested review (Approve)

Approving. I tested it against a real autoscaling pool and with manual, paused and cluster-paused pools. Scale Down goes away from the row menu, machine detail and bulk, and a mixed bulk selection now only touches machines whose pool isn't autoscaling. On master that same selection took an autoscaling pool below its minimum.

Two small things on the description, neither blocks this:

  • The YAML has one autoscaling pool and one etcd/CP machine, so pausing workers is disabled (it's the last autoscaling pool) and the bulk step has nothing it can act on. A second autoscaling pool and a manual worker pool would make those steps runnable.
  • On the Advanced > Machines note: the case I could reach comes from the provisioning cluster not being in the store, not the template. Scale Down is listed there, but the dialog crashes on open, so nothing gets written.