Skip to content

Arbitrary cloud-init user-data via a generated NoCloud ISO - #17

Merged
prop4n merged 4 commits into
mainfrom
feat/cloudinit-userdata
Aug 31, 2026
Merged

prop4n merged 4 commits into
mainfrom
feat/cloudinit-userdata

Conversation

@prop4n

@prop4n prop4n commented Aug 31, 2026

Copy link
Copy Markdown
Owner

Adds arbitrary cloud-init user-data, delivered 100% over the API (no SSH, no
snippets). Proxmox's native cloud-init options can't carry raw user-data, and
snippets need file access to the node; instead proxmops generates a NoCloud
CIDATA seed ISO from spec.userData, uploads it, and attaches it as a cdrom.
Any NoCloud consumer (cloud-init, guix-metadata) reads it at boot.

Manifest

spec:
  fromTemplate: guix-homelab-template
  userData: |
    #cloud-config
    ((system-file . "systems/web01.scm"))
  applyMode: reboot
  state: running

userData is mutually exclusive with cloudInit and iso (all three drive the
cidata disk / ide0).

How

  • internal/proxmox/cidata.go: build the CIDATA ISO with go-diskfs; upload via a
    raw multipart POST (the go-proxmox SDK's Upload mis-formats the body); resolve an
    iso-content storage; delete old seeds.
  • The ISO is named proxmops-cidata-<vmid>-<hash8>.iso, so the attached ide0
    encodes which user-data is live.
  • Create paths (blank/image/clone) provision it; the Proxmox cloud-init drive
    (ide2) is skipped when userData is set (would be a second cidata disk).

Re-sync (scope B)

  • The reconciler compares the desired user-data hash to the hash in the observed
    ide0. On a change it re-provisions (upload new, attach, delete old).
  • A stopped VM reads the new seed on next boot, so it is re-provisioned right away.
  • A running VM needs a reboot to re-read it: done when applyMode: reboot, else
    reported as "reboot required".
  • The seed ISO is deleted when the VM is deleted, so none accumulate.

Verification

  • Unit: ISO build + read-back (label CIDATA, payload), deterministic hash,
    parse helpers, manifest exclusivity, and the reconciler drift branches
    (running+reboot, running+informational, stopped+reprovision, no-drift).
  • End-to-end on the live cluster, driven by the daemon: create attaches the
    right cidata ISO (hash matches content, no ide2); changing userData
    re-provisions the new ISO, deletes the old one, and reboots only a running VM.
  • The upload API and go-diskfs generation were proven with a spike before design.

golangci-lint (v2), gofmt, Go + vitest suites green. Promotes go-diskfs to a
direct dependency.

@prop4n
prop4n merged commit a9afd55 into main Aug 31, 2026
3 checks passed
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