Previews PR 1236 (462f9df) main (ee91524) PR on GitHub node-manager #251

PR 1236: support configuring static hugepages

harvester/harvester-ui-extension · tserong · reviewed at 462f9df (base aa6d11a) · 2026-11-10 · two passes: Cody's process (ai-skills 5f9619b) and ours

Preview: https://harvesterpr1236r.mf-created-dashboard.ui.rancher.space/ · built from 462f9df, backend Harvester v1.10.0-rc1 (.105, one node, node-manager #251 not installed). Login admin / HARVESTER_PASSWORD.

Verdict: request changes (both passes)

The kB change is right and the new field saves a number, but the whole Hugepages tab goes blank when the Hugepage object has no spec.static. Meminfo and Transparent Hugepages disappear with it, on the host page and on Edit Config. That's .105 today (v1.10.0-rc1 without the backend PR). After node-manager #251 it should still happen on clusters upgraded with Longhorn V2 off, because nothing writes spec.static there. The simple fix is to treat a missing count as 0 in both components, which also fixes the empty value you get for 0 pages.

Besides that: 3 questions (merge order and field name, requested vs allocated count, help text for the rke2 restart and the memory reserved at boot) and 2 optional points. Nothing is posted, and there's no pending review on GitHub.

Test it yourself

  1. Open the host hp-159-tink-system → Hugepages on main and on the PR. On the PR the panel is empty, and the Console shows Cannot read properties of undefined (reading 'nrHugepages'). Add ?mode=edit to the URL for Edit Config, which breaks the same way.
  2. To see the feature itself: DevTools → Network → block *subscribe*, then Override content on the node.harvesterhci.io.hugepages request and add "static": {"nrHugepages": 1024} to the host's spec. For the 0 case use "static": {}.
  3. Don't press Save on .105. It writes the node and the other host resources too. The payloads below were captured with every write answered locally.
How the two passes compare
Cody's processOurs
VerdictRequest changes (acceptance criterion AC4 not met)Request changes (1 blocker)
Filed5 line comments (blank tab on view, blank tab on edit, empty value at 0, input values, info link name) + before/after demos; 2 open questions kept in the report (field rename, a memory warning in the GUI)1 blocker (blank tab, with the 0 case folded in), 1 optional nit, 3 questions
Found by bothBlank tab without spec.static (live on .105) · empty value and empty required field at 0 · empty/decimal/negative values sent as typed · merge order and the nrHugepages rename on #251 · the request for a GUI hint about memory reserved at boot
Only one passThe new info link has no accessible name (axe link-name, serious; ours saw it and left it out as main's pattern) · before/after demos · acceptance table · the webhook message checked in the growl and the notification centerThe blank tab also after #251, on upgraded clusters with Longhorn V2 off · requested vs allocated count · Save makes node-manager restart rke2 on the node · ran lint and the feature-flag unit tests
Dropped in the mergeNothing. Every finding held when I re-checked it against the code at 462f9df and on the previews.
CostThis session: code-only steps while ours ran, browser steps after itFresh agent: ~23 min, 248k tokens, 80 tool calls

Order: ours ran first as a fresh agent that never saw Cody's pass. Cody's code-only steps ran meanwhile, and its candidate list (blank tab, empty 0 value, input values) was written before ours reported. Its browser checks ran after ours had finished, so they aren't fully independent of it.

Findings, merged
1

The Hugepages tab goes blank when the Hugepage object has no spec.static

detail/harvesterhci.io.host/HarvesterHugepages.vue:140 · edit/harvesterhci.io.host/HarvesterHugepages.vue:108, :178
Cody'sOurs
Should fix

Both templates read spec.static.nrHugepages. When static is missing, rendering throws TypeError: Cannot read properties of undefined (reading 'nrHugepages') and Vue drops the whole component. On Edit Config, the PR's new static: {} default doesn't help, because fetch() replaces spec with the server's object (line 108).

When it happens

main: Meminfo and Transparent Hugepages
main, host page (your screenshot)
PR: empty Hugepages panel
PR, same host (your screenshot)
main Edit Config: Transparent Hugepages selects
main, Edit Config
PR Edit Config: only the Hugepages heading
PR, Edit Config: the selects are gone
0 pages: empty value
PR, 0 pages (stubbed static: {}): no value
0 pages: empty required field
PR, 0 pages: empty required field

Simplest fix

Treat a missing count as 0: make sure spec.static exists with 0 after fetch() takes the spec, and read it with a 0 fallback on the detail page. One change covers the rc builds, the upgraded clusters and the 0 case.

2

Merge after node-manager #251, once the field name is settled?

config/feature-flags.js:89
Cody'sOurs
Question

The flag turns the section on for every v1.10.0 build, rc builds without the backend included. On #251, votdev questioned the nr prefix of nrHugepages, and nobody has answered yet. If it's renamed, this PR has to follow.

3

The view shows the requested count, not the allocated one

detail/harvesterhci.io.host/HarvesterHugepages.vue:138
Ours
Question

#251 also reports status.static.nrHugepages, the count actually allocated, and tserong said on #251 that a partial allocation can need a reboot. With 2048 requested and 1536 allocated, the section says 2048 while Total Hugepages above says 1536. Is Meminfo enough? Live, stubbed.

Total 1536, Number of Persistent Hugepages 2048
PR, stubbed spec 2048 / status 1536
4

Should the field say it restarts rke2 and reserves memory at boot?

edit/harvesterhci.io.host/HarvesterHugepages.vue:177
Cody'sOurs
Question

Once a new count is reached, #251's restartKubelet() restarts whichever of rke2-server/rke2-agent runs on the node. On a single-node cluster that briefly takes the API down. The count is also persisted to boot, and abonillabeeche asked on #251 for "a simple warning, either in the GUI (e.g. "this will reserve ~X GiB at boot") or called out clearly in the docs". GUI or docs is still open. Code-read.

5

Empty, decimal and negative values go to the API as typed

edit/harvesterhci.io.host/HarvesterHugepages.vue:181
Cody'sOurs
Optional

min="0" isn't enforced on a number input, and .number leaves an empty field as a string. Captured Save payloads (stubbed, every write answered locally):

TypedSent as spec.static.nrHugepages
20482048 (a number, so the normal path works)
(empty)""
1.51.5
-3-3

The API should refuse all three (the CRD field is an integer, and #251's webhook decodes into uint64), so nothing bad is stored. The user just gets a raw type error instead of a message on the field. Code-read for the API side.

6

The new info link has no accessible name

l10n/en-us.yaml:667
Cody's
Your call

<a href target="_blank"><i class="icon icon-info" /></a>: Chromium's AX tree names it "", and axe reports link-name (serious, WCAG 2.4.4/4.1.2), so a screen reader only says "link". The Transparent Hugepages link on main is the same, which is why ours left it out. Cody's rule keeps it, because the PR adds a new instance. I'd post it as optional.

Checked, nothing to raise
WhatResultHow
kB changeRight on every backendMeminfo has been bytes since the CR was added (node-manager master and #251). Live: 2097152 B → 2048 kB, 12121538560 B → ~11.8M kB.
New field saves a numberYesStubbed 1024 → typed 2048 → PUT carries spec.static.nrHugepages: 2048 next to the unchanged transparent
Webhook refusal (Longhorn V2 minimum)Message reaches the userSimulated 422: growl with the webhook text plus the notification center. The "saveHugepages error" banner is main's existing path.
Other locales, CIFineen-us is the only locale in the repo. Lint, unit tests and DCO green; ours re-ran lint and the feature-flag tests (7/7).
Simpler shape, reused components, designNothing in scopeNo new component, no design; the computeds follow the file's own pattern
a11y probe on Edit Config (light, dark)Nothing else from the PROther hits are on main's Transparent Hugepages selects, or shell's LabeledInput (focus drawn on the wrapper, the dark-theme required * at 2.38:1 = theme color)
Nothing reached .105ConfirmedEvery non-GET except login answered locally. The Hugepage CR's spec and spec-owning managedFields entry (2026-04-08) are identical before and after. resourceVersion moves on its own (status updates every ~minute).
Cody's process: demos and acceptance
Before: main on .105
After: PR, Hugepage object stubbed as #251 returns it (1024 pages); Save answered locally
Criterion (source)VerdictHow
AC1The view shows the static allocation (PR body, HEP API changes)Partly metStubbed 1024: shown. 0 pages: empty value.
AC2Edit Config sets the count, Save applies it (#9459 "per-node settings", HEP user experience)MetStubbed; PUT payload above
AC3Going under Longhorn V2's need is refused and the user is told why (HEP goals)MetSimulated webhook 422
AC4The Hugepages tab keeps working on every supported version (implied by the flag)Not metLive on .105: blank tab, view and edit
AC5Meminfo reads in kB and matches the labels (PR body)MetLive

Not in this PR: sizes other than 2 MiB and a cluster-wide setting (both HEP non-goals). Passes with nothing in scope: simplify, component reuse, design fidelity. Preexisting check: all 5 comments introduced (the info link as a new instance of main's pattern).

Our pass: coverage
Filecorrectnesstestsa11yi18nconventionserrorsperf
detail HarvesterHugepages.vuefindingfindingclearedclearedclearedfindingcleared
edit HarvesterHugepages.vuefindingfindingclearedclearedclearedfindingcleared
feature-flags.jsfinding (Q)clearedn-an-aclearedn-an-a
doc-links.js, en-us.yamlclearedn-an-aclearedclearedn-an-a

The tests cells are the blocker folded in: there are no unit tests for either component on main or in the PR, and a mount test with an object that has no spec.static would catch it.

What I'd post (not posted)

Thanks Tim! The kB change and the new field work, but the Hugepages tab goes blank on a v1.10 cluster whose Hugepage object has no static yet. A few questions below too.

Optional for the body: the before/after videos above.

pkg/harvester/detail/harvesterhci.io.host/HarvesterHugepages.vue:140

When the Hugepage object has no spec.static, this throws and the whole Hugepages tab renders empty, Meminfo and Transparent Hugepages included. That's what a v1.10.0-rc1 cluster without node-manager#251 returns today. After #251 it should still happen on clusters upgraded with Longhorn V2 off, since only the status gets updated there. With 0 pages the value is empty too, because nrHugepages is omitempty. Defaulting the count to 0 when it's missing would cover all of these.

Images: main / this PR (host page).

pkg/harvester/edit/harvesterhci.io.host/HarvesterHugepages.vue:108

Same on Edit Config: the spec from the server replaces the static: {} default here, so the template throws and the Transparent Hugepages selects disappear too. With 0 pages the field comes up empty but required. The same 0 default would fix both.

Images: main / this PR (Edit Config).

pkg/harvester/config/feature-flags.js:89

Question: this turns the section on for every v1.10.0 build, rc builds without the backend included, and the field name is still being discussed on node-manager#251. Is the plan to merge this after #251, once the name is settled?

pkg/harvester/detail/harvesterhci.io.host/HarvesterHugepages.vue:138

Question: this shows the requested count. With 2048 requested and only 1536 allocated, the section says 2048 while Total Hugepages above it says 1536. Is Meminfo enough, or should this show status.static too?

Image: the partial-allocation screenshot.

pkg/harvester/edit/harvesterhci.io.host/HarvesterHugepages.vue:177

Question: once a new count is allocated, node-manager restarts rke2 on that node, and the count is kept for boot, where a big number can reserve most of the memory. abonillabeeche asked on #251 for a "this will reserve ~X GiB" hint. Should the field say that, or is it left to the docs?

pkg/harvester/edit/harvesterhci.io.host/HarvesterHugepages.vue:181

Optional: min="0" isn't enforced, so an empty, decimal or negative value is sent as typed ("", 1.5, -3), and the user gets the API's type error instead of a message on the field.

pkg/harvester/l10n/en-us.yaml:667

Optional: like the Transparent Hugepages one, this info link has no accessible name, so a screen reader only announces "link".