proxmox: config block, IDs, marks, state mapping, classify, registration

Phase 2 of the proxmox provider plan. Adds the proxmox: config block with
its validation, registers the type in the config allow-list and the
constructor map, and starts internal/provider/proxmox: the api seam and
its data types, providerIDs (vms/<vmid>/<name>) with the UID-derived VMID,
the description mark, state mapping, and error classification.

Tests read the responses captured from pv1-1 in Phase 0 rather than
repeating Proxmox's wording, and every matcher is run against every
captured error so none fires on the wrong message. The contract methods
are Transient stubs until Phases 5 to 7.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-23 09:42:46 +00:00
co-authored by Claude Opus 5.5
parent 4e974f30f6
commit 6e996ae2c7
19 changed files with 1856 additions and 4 deletions
@@ -6,7 +6,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`)
- [ ] Phase 2 — Config block, registration, package skeleton
- [x] Phase 2 — Config block, registration, package skeleton
- [ ] Phase 3 — Extract `nocloud` and `keyedlock` from kvm2
- [ ] Phase 4 — Seam, HTTP client, decorator, fake
- [ ] Phase 5 — Get, ListByTag, task helpers
@@ -329,3 +329,55 @@ listed as `unknown` is Proxmox's documented design, unverifiable on a single
node; entry 13 now says so. And the matcher for `VM <vmid> is not running`
is a whole-message regexp, because ` is not running` alone also matches
`QEMU guest agent is not running`.
## Phase 2 — Config block, registration, package skeleton
Added the `proxmox:` block (`internal/provider/config_proxmox.go`), wired it
into `config.go` (the field, `setBlocks`, the allow-list and the dispatch:
three `"proxmox"` sites) and registered `"proxmox": proxmox.New` in
`cmd/main.go`, so the two type lists changed in one commit. The package
`internal/provider/proxmox` now holds the seam interface and its data types
(`api.go`), `id.go`, `mark.go`, `state.go`, `errors.go` and the constructor
(`proxmox.go`). The four contract methods are stubs that answer
`ErrTransient`, so a Proxy on a configured `proxmox` entry retries instead of
latching until Phases 5 to 7 land. `New` passes a nil `api` until Phase 4
builds the HTTP client.
Every test that depends on Proxmox's wording or JSON shape reads the Phase 0
captures through `fixtures_test.go` (`readCapture`, `loadAPIError`,
`decodeData`), so no message string is repeated in a test. The one exception
is `TestClassify_table`'s synthetic rows (a 404, 502, 595, 596, a storage
message), for statuses recon never produced.
Deviations from the plan's Phase 2 list, both making it smaller:
- **No temporary `locks.go`.** Only Create and Delete take the per-name lock,
and they arrive after Phase 3 extracts `internal/provider/keyedlock`, so
copying kvm2's lock now would only be deleted next phase.
- **No `provider.Checker` assertion yet.** `cmd/main.go` type-asserts the
Checker and would call a stub; the assertion comes with the real `Check`
in Phase 7.
Added beyond the plan: `api_test.go` decodes the captured responses into the
seam's types, so a misspelled JSON tag fails now rather than in Phase 5, and
`TestParseDescription_fromClone` reads the mark back from the description of
the VM cloned in Phase 0.
A mutation check confirmed the suite can fail. Matching `VM <vmid> is not
running` as a plain suffix (the first spec draft's mistake) failed
`TestAPIError_matchers` on the agent-not-running capture; treating a paused
VM as running failed `TestMapState_table` and `TestMapState_fromCaptures`.
Both mutations were reverted and the files compared identical to backups.
```bash
gofmt -w cmd/main.go # realigned the constructor map for the longer key
grep -c '"proxmox"' internal/provider/config.go # 3
go build ./... && go vet ./...
go test -race -count=1 ./internal/provider/... ./cmd/... # all ok
go test -short -count=1 ./... # all ok
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).