proxmox: Delete with seed cleanup, health Checker, CRD type comment

Delete stops and destroys a VM of ours under the per-name lock, then
deletes its seed on the VM's node, or on every node when no VM of ours
was left. A hibernated VM of ours is Permanent, as in Create. The
Checker verifies each node concurrently (version, node status, ISO
storage, templates) and adds CertificateInvalid, CredentialsUnavailable,
StorageNotFound and TemplateNotFound to the shared reasons. The
ProviderHealth type comment gains proxmox.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-23 20:14:14 +00:00
co-authored by Claude Opus 5.5
parent 34f132d421
commit 2f89c1fffb
13 changed files with 1936 additions and 46 deletions
+48 -20
View File
@@ -381,22 +381,35 @@ the seed ISO; the VM's disks are removed by the destroy call itself, with
`purge=1&destroy-unreferenced-disks=1`. Under the per-name lock:
1. Parse the ID (`ErrPermanent` if malformed).
2. `/cluster/resources`. VMID absent: skip to step 7, trying every configured
node because the VM's node is no longer known.
3. `lock` set or `status: unknown`: `ErrTransient`. A clone or another task
still running finishes first; the reconciler calls Delete again after
`DeletionPoll`.
4. Read the config. `does not exist`: skip to step 7. A different name, or
no mark: the VM is not ours; log it and skip to step 7 without touching
it.
5. Status other than `stopped`: `POST …/status/stop`, wait (stop budget).
Stop works on a paused VM too.
2. `/cluster/resources`. VMID absent, or held by a container: skip to
step 7, trying every configured node because the VM's node is no longer
known.
3. `lock` set, other than `suspended`, or `status: unknown`: `ErrTransient`.
A clone or another task still running finishes first; the reconciler
calls Delete again after `DeletionPoll`.
4. Read the config. `does not exist`: skip to step 7 on every node. A
different name, or no mark: the VM is not ours; log it at V(1) and skip
to step 7 on every node without touching it. Our VM with a `lock` other
than `suspended` (taken since the listing): `ErrTransient`. Our VM with
`lock: suspended` (hibernated): `ErrPermanent` asking the admin to resume
or destroy it, the same answer Create's ownership check gives. The
destroy refuses a locked VM, and that lock never clears on its own, so
Transient would retry forever; Permanent puts the admin action in the
Proxy's status, where `reconcileManaged` latches it and the finalizer
path keeps retrying it visibly. A hibernated VM that is not ours is left
alone like any other.
5. Status (from the listing) other than `stopped`: `POST …/status/stop`,
wait (stop budget). Stop works on a paused VM too, which the listing
shows as `running`. `does not exist` means the VM is gone.
6. `DELETE /nodes/{node}/qemu/{vmid}?purge=1&destroy-unreferenced-disks=1`,
wait (destroy budget). `does not exist` is success.
7. `DELETE /nodes/{node}/storage/{isoStorage}/content/<isoStorage>:iso/<name>-seed.iso`,
wait (seed-delete budget). A missing volume is still a successful task,
so no message is matched. On the try-every-node path a node whose
request fails is logged and skipped.
wait (seed-delete budget), on the VM's node when steps 5 and 6 ran on a
VM of ours, and on every configured node in config order otherwise. A
missing volume is still a successful task, so no message is matched. On
the every-node path a node whose request or task fails is logged at V(1)
and skipped, unless ctx ended, which fails the call; on the VM's node a
failure fails the call (a 403 for `Datastore.Allocate` is Permanent).
8. Return nil.
Everything is gone when Delete returns nil, so `Get` afterwards is
@@ -593,18 +606,31 @@ failed for a network reason, or pveproxy answered 595 or 596);
`APIError` (any other failure). Provider-specific, each fixed differently by
an admin:
- `CertificateInvalid`: TLS verification failed. Fix `url` or `caFile`, not
the host.
- `CertificateInvalid`: TLS verification failed, or `caFile` is missing or
holds no certificate. Fix `url` or `caFile`, not the host.
- `CredentialsUnavailable`: the token file is missing or malformed. Fix the
Secret, not Proxmox.
- `StorageNotFound`: `isoStorage` is missing, inactive, or has no `iso`
content. Fix the storage on the node.
content. Fix the storage on the node. Any 500 from the storage status
call counts as missing, whatever its wording (it does not exist, is not
enabled on that node, or is offline; no missing-storage response was
captured, so no text is matched), and the message carries Proxmox's
text. A 401, 403, 595 or 596 there keeps its shared reason.
- `TemplateNotFound`: a configured template VMID does not exist or is not a
template. Fix the template or the catalogue.
Details: `pveVersion`, `kernel`, `tokenID` (the part before `=`, never the
secret), `isoStorage`, `isoStorageAvailGiB`, `templates`, filled as far as
the check got. Cost: four requests per node per round. The `status.type`
the check got; `isoStorage` is always there. `templates` counts catalogue
entries. The Checker reads `tokenFile` itself, before the first request,
with the client's `readToken`: the seam stays one method per endpoint, and
a missing or malformed file answers `CredentialsUnavailable` without a
request. Only the part before `=` is kept.
`Timeout` also covers a request that runs out of its own timeout while the
round is still running. A dial or a connection cut mid-response is
`Unreachable`, even when the dial ran out of its own 10 s budget, because
the node did not answer. Cost: four requests per node per round. The `status.type`
comment in `api/v1alpha1/providerhealth_types.go` gains `proxmox` and the
CRD is regenerated.
@@ -713,14 +739,16 @@ named `proxmox`, with `provider` on every line and `vmid`, `name` and
`node` on every line about an instance.
- V(1): one line per operation outcome (`proxmox vm ensured running`,
`proxmox vm fetched` with state, ip and uid, `proxmox vm deleted`,
`proxmox vm fetched` with state, ip and uid, `proxmox vm deleted` with
`destroyed` (false when no VM of ours was left to destroy),
`proxmox vms listed` with counts, `proxmox node checked` with the reason),
one `proxmox api call` line per seam call from the decorator (`call`,
`node`, `vmid`, `storage`, `upid`, `duration`, plus `newid` and `name`
on a clone, `filename` on an upload and `volid` on a volume delete, and
on failure `httpStatus` and `error`), a `proxmox task failed` line with the UPID and
`exitstatus`, and the no-op lines of Delete and ListByTag (foreign VM
left alone, unreadable candidate skipped).
`exitstatus`, and the no-op lines of Delete and ListByTag (VM already
gone, foreign or unmarked VM left alone, seed delete skipped on a node,
unreadable candidate skipped).
- V(2): request parameters (without `description`'s content at V(1) and
never the upload body), config summaries, agent addresses, the resolved
Create parameters with `cloudInitBytes`.