proxmox: review fixes, real-host validation record, changelog
Phase 10. Two review passes (go-operator-reviewer, /code-review high) over the branch, recorded in docs/reviews/2026-09-23-proxmox-provider-review/: 9 findings fixed, 3 accepted as documented limits, 7 rejected with a reason. - Delete removes the seed between the stop and the destroy, so a failed seed delete leaves the VM for the next Delete instead of leaking the ISO; a Permanent refusal on the every-node path fails the call. - Create skips a probe VMID whose listed name is someone else's without waiting on its lock. - The three answers Phase 0 never captured are fixtures now: a missing storage is a 400 (the Checker reports StorageNotFound), a hibernated VM is destroyable (Delete destroys ours), a paused VM stops. - TestProxmox_lifecycle and TestProxmoxCheck_realHost passed on pv1-1 (PVE 9.2.20); the docs record the run and drop "not yet run". Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -14,7 +14,7 @@ Pairs with [docs/plans/2026-09-18-1502-proxmox-provider.md](../plans/2026-09-18-
|
||||
- [x] Phase 7 — Delete, Checker, CRD comment
|
||||
- [x] Phase 8 — Contract and observability sweep, integration test, testing.md
|
||||
- [x] Phase 9 — Deployment and documentation
|
||||
- [ ] Phase 10 — Review, real-host validation, changelog, MR
|
||||
- [x] Phase 10 — Review, real-host validation, changelog, MR
|
||||
|
||||
## Phase 0 — Plan file, branch, host recon, fixtures
|
||||
|
||||
@@ -1298,3 +1298,144 @@ Worth noting:
|
||||
guide's "Not yet proven end to end" gap and the README's matching
|
||||
clause come out once they pass. Then the CHANGELOG entry after the user
|
||||
confirms, and the MR.
|
||||
|
||||
## Phase 10 — Review, real-host validation, changelog, MR
|
||||
|
||||
Where the plan's Phase 10 text and the spec disagreed, the spec won, as in
|
||||
every phase since Phase 1. The host is pv1-1 at `https://192.168.0.8:8006`,
|
||||
not 192.168.2.8, the template is 9001 and the ISO storage `proxy-seeds`.
|
||||
|
||||
**Review.** Two read-only passes over `git diff main...HEAD`: the
|
||||
`go-operator-reviewer` agent, briefed with the spec and the contract
|
||||
(P1 to P6, S1 to S3), then `/code-review high` on the branch (CR1 to
|
||||
CR10). Of the 19 findings (two found by both passes), 9 were fixed, 3
|
||||
accepted as documented limits and 7 rejected with a reason, all recorded in
|
||||
[docs/reviews/2026-09-23-proxmox-provider-review/REPORT.md](../reviews/2026-09-23-proxmox-provider-review/REPORT.md).
|
||||
The one that mattered was CR1/P1. Delete destroyed the VM and then deleted
|
||||
the seed, so a failed seed delete left a seed nothing would ever remove:
|
||||
the next `Get` is NotFound, the finalizer goes, and GC lists only VMs.
|
||||
Delete now removes the seed between the stop and the destroy, so the VM
|
||||
`Get` looks at goes last. A 403 on the every-node path now fails instead
|
||||
of being skipped. P2 (CR3) was the other code change: a probe VMID whose
|
||||
listed name is someone else's is skipped at once, instead of waiting up to
|
||||
the clone budget on its lock on every retry.
|
||||
|
||||
**Lint.** `make lint` still panics inside staticcheck on Go 1.27, so, as
|
||||
in Phases 8 and 9:
|
||||
|
||||
```bash
|
||||
bin/golangci-lint run --disable staticcheck --max-same-issues 0 --max-issues-per-linter 0 ./internal/provider/proxmox/... ./internal/provider/
|
||||
bin/golangci-lint run --build-tags integration --disable staticcheck --max-same-issues 0 --max-issues-per-linter 0 ./internal/provider/proxmox/... ./internal/provider/
|
||||
```
|
||||
|
||||
Each run was diffed against the pre-fix baseline by file and message,
|
||||
with line numbers and occurrence counts stripped. Three new goconst hits
|
||||
came in along the way, and all three were fixed rather than accepted. The
|
||||
P2 test's lock value made `"backup"`, then `"migrate"`, a repeated
|
||||
literal; it now uses `"snapshot-delete"`. The fake's missing-storage error
|
||||
repeated `"Parameter verification failed."`; it is now built from the raw
|
||||
captured body. Final counts: goconst 224 (231 with the tag), logcheck 12,
|
||||
modernize 2, unparam 3, gocyclo 1.
|
||||
|
||||
**Real host.** The user ran the host checklist on pv1-1 and then:
|
||||
|
||||
```bash
|
||||
export PROXMOX_TEST_URL=https://192.168.0.8:8006
|
||||
export PROXMOX_TEST_TOKEN_FILE=$HOME/.config/proxmox-token
|
||||
export PROXMOX_TEST_CA_FILE=$HOME/.config/proxmox-ca.pem
|
||||
export PROXMOX_TEST_NODE=pv1-1
|
||||
export PROXMOX_TEST_TEMPLATE_VMID=9001
|
||||
export PROXMOX_TEST_ISO_STORAGE=proxy-seeds
|
||||
export PROXMOX_TEST_VLAN_TAG=4
|
||||
make test-integration-proxmox
|
||||
# --- PASS: TestProxmox_lifecycle (59.68s)
|
||||
# --- PASS: TestProxmoxCheck_realHost (0.13s)
|
||||
```
|
||||
|
||||
It ran the review fixes, uncommitted, on the first try. Clone task about
|
||||
3 s, Create 6.2 s, address 50.2 s after Create returned, Delete plus Get
|
||||
plus a second Delete 3.1 s. The Delete deleted the seed while the stopped
|
||||
VM's `ide2` still referenced it (imgdel `OK`), then destroyed the VM, and
|
||||
step 10 found neither left. That settles the one assumption behind the
|
||||
reorder that the fake could not prove. The run confirmed P6 as well: the
|
||||
first `Get`, about 6 s after the clone, met `status: unknown` in the
|
||||
listing and answered Transient, blaming the node, which was up. The
|
||||
message now names both causes.
|
||||
|
||||
**The three answers Phase 0 never captured.** Captured with a scratch
|
||||
helper that passes the token to curl through a process substitution and
|
||||
writes the Phase 0 fixture shape. Its first offline test caught a bug:
|
||||
Python's text mode folds `\r\n`, so the header split found nothing, and
|
||||
the file is now opened with `newline=""`. The calls were:
|
||||
|
||||
```bash
|
||||
pvecap storage_status_missing GET /nodes/pv1-1/storage/proxy-missing/status
|
||||
pve POST /nodes/pv1-1/qemu/9001/clone -d newid=199999 -d name=proxy-reconaaaaaaaaaaa -d full=1
|
||||
pve POST /nodes/pv1-1/qemu/199999/status/start
|
||||
pve POST /nodes/pv1-1/qemu/199999/status/suspend # pause
|
||||
pvecap stop_paused_response POST /nodes/pv1-1/qemu/199999/status/stop
|
||||
pvecap task_status_stop_paused GET /nodes/pv1-1/tasks/<upid>/status
|
||||
pvecap status_current_after_stop_paused GET /nodes/pv1-1/qemu/199999/status/current
|
||||
pve POST /nodes/pv1-1/qemu/199999/status/start
|
||||
pve POST /nodes/pv1-1/qemu/199999/status/suspend -d todisk=1 # hibernate: stopped, lock suspended
|
||||
pvecap err_destroy_hibernated DELETE '/nodes/pv1-1/qemu/199999?purge=1&destroy-unreferenced-disks=1'
|
||||
pvecap task_status_destroy_hibernated GET /nodes/pv1-1/tasks/<upid>/status
|
||||
```
|
||||
|
||||
Two of the three contradicted the code:
|
||||
|
||||
- A missing storage is a **400** with the field error
|
||||
`storage: No such storage.`, not a 500, so the Checker reported
|
||||
`APIError`. `isNoSuchStorage` matches that exact field error, and a 500
|
||||
is still taken as missing (not captured). The matcher matrix caught a
|
||||
first version that fired on any `storage` field error, which a clone
|
||||
onto a storage without image support also has.
|
||||
- The destroy **accepts a hibernated VM**. Its task ended `OK`, removing
|
||||
both disks and the `vm-199999-state-suspend-…` volume. Delete refused
|
||||
such a VM of ours as Permanent, believing the destroy would, so it now
|
||||
destroys it like a stopped one. Create's adoption still refuses one it
|
||||
finds before it was ever recorded. The capture was renamed
|
||||
`destroy_hibernated_response.json`, because the client test globs
|
||||
`err_*.json` as errors.
|
||||
- A stop on a paused VM ends `OK` with the VM stopped, as assumed.
|
||||
|
||||
The destroy also cleaned up the scratch VM, and a listing of local-lvm
|
||||
content for VMID 199999 came back empty.
|
||||
|
||||
**Kind pass.** The user built and loaded the image, deployed, applied a
|
||||
scratch providers config with only `kubernetes` and `proxmox-home`, ran
|
||||
the sample Proxy to Ready, ran a `curl -x` through it, deleted it and
|
||||
checked pv1-1 for leftovers. Everything passed. The first attempt at the
|
||||
override went to a ConfigMap named `providers-config`. `config/default`'s
|
||||
`namePrefix` makes the mounted one `egress-proxies-operator-providers-config`,
|
||||
so the pod kept the shipped config: no `proxmox-home` ProviderHealth, and
|
||||
`kvm-home` and `kvm2-home` failing on the missing SSH key. The corrected
|
||||
command replaced the prefixed ConfigMap and deleted the stray one. The
|
||||
Secret keeps its plain name (`proxmox-token`), since kustomize renames
|
||||
only resources it builds. `make deploy`'s image edit to
|
||||
`config/manager/kustomization.yaml` was reverted, not committed.
|
||||
|
||||
Worth noting:
|
||||
|
||||
- **Behaviour changes from Phase 9.** Delete's order is now stop, seed,
|
||||
destroy. A hibernated VM of ours is destroyed rather than latched. The
|
||||
Checker reports a missing ISO storage as `StorageNotFound`. A
|
||||
foreign-named VM on a probe VMID is skipped without a config read.
|
||||
- **Accepted, not fixed** (guide Known gaps, spec entry 24):
|
||||
- P3: Create adopts a leftover stopped VM whatever the current
|
||||
`placement` says.
|
||||
- P4: a retry can clone a second VM in a narrow crash window.
|
||||
- **Still unproven:**
|
||||
- A storage that exists but cannot be used on a node.
|
||||
- Every cross-node path. The host has one node.
|
||||
- **Flakes.** kvm's `TestKeepalive_keepsHealthyClientOpen` failed once
|
||||
under `-race` ("unexpected packet in response to channel open") and
|
||||
passed 5 of 5 on rerun. This branch does not touch `internal/provider/kvm`.
|
||||
The parked kvm2 items (S5.1, the 200 ms `TestCheck_hostsCheckedConcurrently`)
|
||||
did not bite, though the reviewer agent saw the latter fail once.
|
||||
- **Docs.** The "not yet run" wording is gone from the README, the
|
||||
guide, `testing.md`, spec entry 21 and `providers-existing.md`.
|
||||
`testing.md` gained a timings table and the `PROXMOX_TEST_VLAN_TAG`
|
||||
export line. The link sweep script from the Phase 9 entry found 0
|
||||
broken links over the seven edited docs, and the guide has no `—`.
|
||||
- `.claude/settings.json` stays out of the commit.
|
||||
|
||||
Reference in New Issue
Block a user