Skip to content

feat(k3s): per-pool nodePrep (cloud-init packages/runcmd) (E2)#146

Merged
astrojerms merged 1 commit into
mainfrom
feat/e2-k3s-nodeprep
Jul 20, 2026
Merged

feat(k3s): per-pool nodePrep (cloud-init packages/runcmd) (E2)#146
astrojerms merged 1 commit into
mainfrom
feat/e2-k3s-nodeprep

Conversation

@astrojerms

Copy link
Copy Markdown
Owner

E2 — k3s node-pool node prep

Surfaces E1's cloud-init packages/runcmd through the k3s Cluster spec as a per-pool nodePrep block, so a worker pool installs its own host prerequisites — e.g. open-iscsi for a Longhorn storage pool. This is what makes F1 (Longhorn) usable.

nodes:
  workers:
    - name: storage
      count: 3
      nodePrep:
        packages: [open-iscsi]
        runcmd: [systemctl enable --now iscsid]

Mirrors the A3 GPU-per-pool pattern exactly

  • NodePrepSpec on ControlPlaneSpec + WorkerSpec
  • parseNodePrepSpec (untyped map → struct)
  • NodePrepForNode(i, cpCount, spec) — resolves the pool for a node across the flat control-plane-then-workers ordering
  • ApplyNodePrepToVMSpec(vmSpec, np) — writes packages/runcmd into the existing cloudInit map (preserving user/sshKeys/ipConfig); no-op when nil/empty
  • #NodePrep CUE def referenced on both pools

Both build paths stay in sync

Wired into both VM-build paths via the shared helper — the operative pkg/k3s/cluster/create.go and the Plan mirror internal/controller/providers/k3s/cluster_plan.go — same as GPU (the K1 "keep these identical" debt; this change keeps them identical). No struct/signature changes, stdlib-only, so no module churn.

Tests

  • nodeprep_test.go: parse + NodePrepForNode resolution across the flat ordering; ApplyNodePrepToVMSpec (nil/empty no-op, preserves siblings, creates cloudInit if absent).
  • TestGenerateDispatchRequests_NodePrep: drives the operative create path, asserts only the target pool's VMs carry cloudInit.packages/runcmd (CP + other workers untouched, user preserved).

Local CI: build, go test ./... (+-race on affected), gofmt, vet, staticcheck, golangci-lint (0 issues), modernize — root and the k3s plugin module — all green.

Surface E1's cloud-init packages/runcmd through the k3s Cluster spec as a
per-pool `nodePrep: {packages, runcmd}` block on the control-plane and each
worker pool, so a pool installs its own host prerequisites — e.g. open-iscsi
for a Longhorn storage pool.

Mirrors the A3 GPU-per-pool shape field-for-field: NodePrepSpec +
parseNodePrepSpec + NodePrepForNode(i, cpCount) + ApplyNodePrepToVMSpec,
wired into BOTH VM-build paths (operative create.go and the Plan mirror
cluster_plan.go) via the shared resources helper so nodes come out
identical. ApplyNodePrepToVMSpec writes into the existing cloudInit map,
preserving user/sshKeys/ipConfig. #NodePrep CUE def on both pools.

Makes F1 (Longhorn) usable — the tier that needs open-iscsi on nodes.

Tests: parse+resolve across the flat control-plane-then-workers ordering,
apply-into-cloudInit (nil/empty no-op, preserves siblings, creates map if
absent), and an operative GenerateDispatchRequests test asserting only the
target pool's VMs get the prereqs.
@astrojerms
astrojerms merged commit fc6ad0a into main Jul 20, 2026
6 checks passed
@astrojerms
astrojerms deleted the feat/e2-k3s-nodeprep branch July 20, 2026 00:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant