provider: extract nocloud seed builder and keyedlock from kvm2

kvm2's NoCloud seed builder moves to internal/provider/nocloud (exported,
with an ErrEmptyName sentinel instead of permanentf) and its keyed locks
to internal/provider/keyedlock, tests moved under their existing names.
kvm2 behaviour is unchanged. proxmox gains the per-name lock and the pure
seed helpers (seedISOName, seedVolID, buildSeed); unlike kvm2 (review
finding S5.1), a seed staging failure there is Transient, not Permanent.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-23 14:01:27 +00:00
co-authored by Claude Opus 5.5
parent 6e996ae2c7
commit 0a6c8624f3
18 changed files with 504 additions and 185 deletions
@@ -7,7 +7,7 @@ Pairs with [docs/plans/2026-09-18-1502-proxmox-provider.md](../plans/2026-09-18-
- [x] Phase 0 — Plan file, branch, host recon, fixtures
- [x] Phase 1 — The spec (`docs/proxmox-provider-spec.md`)
- [x] Phase 2 — Config block, registration, package skeleton
- [ ] Phase 3 — Extract `nocloud` and `keyedlock` from kvm2
- [x] Phase 3 — Extract `nocloud` and `keyedlock` from kvm2
- [ ] Phase 4 — Seam, HTTP client, decorator, fake
- [ ] Phase 5 — Get, ListByTag, task helpers
- [ ] Phase 6 — Create
@@ -381,3 +381,69 @@ go test -count=1 -v ./internal/provider/proxmox/ | grep -c '^=== RUN' # 152
Worth noting: the `validProxmoxYAML` constant is the shape Phase 9's commented
sample entry should take, with the user's values (`192.168.0.8`, `pv1-1`,
`proxy-seeds`, `vlanTag: 4`, template 9001).
## Phase 3 — Extract `nocloud` and `keyedlock` from kvm2
kvm2's `seed.go` and `seed_test.go` moved to `internal/provider/nocloud`
with `git mv`, so history follows them. The package exports `BuildSeedISO`,
`BuildVendorData`, `GuestAgentScript`, `SeedLabel` and a new `ErrEmptyName`
sentinel in place of kvm2's `permanentf`. kvm2's `keyedLocks` became
`internal/provider/keyedlock` (`Locks`, `New`, `Acquire`), and its three
`TestKeyedLocks_*` tests moved with it under their old names. kvm2 now
imports both packages, and its `create.go` still wraps every seed-build error
with `permanentf`, so kvm2's behaviour is unchanged. proxmox gained the
`nameLocks` field and `lockName`.
The test counts match the plan's Verify clause: kvm2 dropped from 465 to 447
`=== RUN` entries, which is the 12 moved tests plus the 6 subtests of
`TestSeedISO_userDataByteForByte`. `nocloud` (15) and `keyedlock` (3) carry
exactly those 18.
```bash
git mv internal/provider/kvm2/seed.go internal/provider/nocloud/seed.go
git mv internal/provider/kvm2/seed_test.go internal/provider/nocloud/seed_test.go
go test -count=1 -v ./internal/provider/kvm2/ | grep -c '^=== RUN' # 465 before, 447 after
grep -rn 'iso9660' internal/provider/kvm2/ # nothing
go build ./... && go vet ./... && go vet -tags=integration ./...
go test -race -count=1 ./internal/provider/... # all ok, run three times
go test -short -count=1 ./... # all ok
```
**Pulled forward from Phase 6 so proxmox imports `nocloud` now**, as the
"Done when" line requires: `seed.go` with `seedISOName`, `seedVolID` and
`buildSeed`, the pure part of the seed step. `uploadSeed` still belongs to
Phase 6, which calls `buildSeed`. The new tests are `TestSeedNames_table`,
`TestBuildSeed_table` (4 rows),
`TestBuildSeed_emptyNameIsPermanentWithoutPayload`,
`TestBuildSeed_stagingFailureIsTransient` and
`TestLockName_givesUpWithContextAsTransient`. proxmox went from 152 to 161
test entries.
**proxmox does not copy kvm2's open review finding S5.1**
(`docs/reviews/2026-09-17-kvm2-provider-review/sweep.md`). In kvm2, a
staging failure such as a missing `/tmp` or a full emptyDir becomes
Permanent, and that latches the Proxy. `buildSeed` makes only
`ErrEmptyName` Permanent and wraps any other error with `%w`, so
`classify` reads it as Transient. The sentinel makes the same three-line
fix possible in kvm2, but this phase leaves kvm2's behaviour unchanged.
Worth noting:
- The older `kvm` provider has its own `seed.go` with a different
signature (`buildSeedISO` returns `[]byte`). The plan scopes the
extraction to kvm2, so `kvm` is untouched.
- `GuestAgentScript` now says the operator reads the address "from the
hypervisor" instead of "through libvirt", in both the Go comment and the
script's comment line inside the guest. The script's commands are
unchanged.
- The docs now name the new packages: `kvm2-provider.md`,
`kvm2-provider-spec.md` entry 19, the kvm2 columns and section of
`providers-existing.md`, plus a "Shared provider helpers" row in the
`architecture.md` component table. `docs/reviews/` keeps the old names,
because it records the code as it was reviewed.
- `TestCheck_hostsCheckedConcurrently` in kvm2 failed once under the full
`-race` suite (latency 199.96 ms against a 200 ms floor). It passed 20
runs out of 20 in isolation, with and without this phase's changes. The
test starts its 200 ms release timers before `Check` starts its clock, so
under load the measured latency can come in just under 200 ms. The flake
predates this phase, and the test is not touched here.