Commit Graph

10 Commits

Author SHA1 Message Date
goodolclint-claude[bot] db416a3f03 fix: retry the qemu-server flock instead of predicting it
WaitForStatusTransition refused to return while snapshot.Locked, and its
comment quoted the exact error it was meant to prevent. Locked reads the
guest config's lock: property; the failure is the flock on
/var/lock/qemu-server/lock-<vmid>.conf, which PVE exposes nowhere.

The flock cannot be observed, so it is retried. GuestLockRetry reissues an
operation for a bounded 45s while PVE reports failing to enter lock_config
for a guest, which it raises before doing any work.

Two seams, because the failure has two surfaces. PveHttpClient.SendAsync
retries the request for operations PVE serialises in the API handler; it
takes a request factory because an HttpRequestMessage cannot be resent.
PveCmdletBase.InvokeGuestTask reissues the call and re-waits its task for
operations serialised in the forked worker, where the POST returns 200 and
only the task fails. WaitForStatusTransition routes through the latter,
hence Func<PveTask>.

The predicate is path-specific and anchored at the start of what PVE said:
lock_file uses identical wording for storage, LVM and HA locks, and a
qmclone that fails after allocating disks must not be reissued into
"VM already exists". That requires the raw text, so it reads
PveTaskFailedException.ExitStatus and PveApiException.ApiMessage.

The Locked check stays — it is correct for the config lock — with a comment
that says so.

Closes #113
2026-09-01 21:19:05 -05:00
goodolclint-claude[bot] fc073e7e2a ci: pin Pester by exact version everywhere it is installed or imported
Pester was installed with -MinimumVersion 5.0 and no ceiling in the CI job
image, both install steps in unit-tests.yml, and both Import-Module calls, plus
the suite's own import inside the container. The image is rebuilt on every CI
run and Pester is installed fresh on every unit-test run, so PSGallery chose the
version — a new major could reach the required PR checks with no commit here,
surfacing as unexplained test breakage on whichever PR ran next.

It had already happened. Steps named "Install Pester 5" were resolving 6.1.0 on
both legs, because Pester 6 declares PowerShellVersion 5.1 and so installs on
Windows PowerShell too. Nothing broke — the suite uses only constructs common to
5 and 6, and runs 1566/0 under 6.1.0 with no deprecation warnings — but nobody
chose it. The step names are corrected; they had been describing an install that
stopped happening some time ago.

Pinning the install alone is not enough, in two ways review found:

An unset variable does not fail. -RequiredVersion accepts an empty value and
degrades to "latest" for Install-Module and to "any" for Import-Module, both
exiting 0, so a renamed or dropped env key would silently restore the float this
commit removes. A guard step now fails the job instead.

The point of use was still floored. run-integration.sh imported the suite's
Pester with -MinimumVersion 5.0, so a second Pester reaching PSModulePath would
win regardless of what was installed. The Dockerfile now promotes the ARG to ENV
so the version is discoverable at runtime, and that import is pinned to it.

The pin lives in two files, so shell-selfchecks asserts they agree — split-brain
between the workflow and the image is precisely the unexplained breakage this is
meant to prevent. CONTRIBUTING.md and CLAUDE.md are updated too; the contributor
instructions were a third floating install site.

Recorded as an amendment to D017 — the same principle as the nested PVE package
pin, applied to the lane's own tooling.
2026-09-01 18:08:02 -05:00
goodolclint-claude[bot] 08ee3ae249 chore: repair the local dev path and delete its dead scaffolding
The local dev environment had drifted badly from CI. Remove the parts that no
longer describe anything real, and make the rest match how CI actually runs.

Delete tests/dev.ps1. It wrapped run-integration.sh, which CI calls directly,
and duplicated the module build that script already performs internally. As a
second entry point it drifted: it still offered the PVE 8 leg retired in #88,
mounted the Docker socket for storage containers replaced by the storage VM in
#87, and pointed its remote-host examples at a runner decommissioned in the ARC
migration. All four documents describing it used a positional syntax that bound
the bare word to -Tests and then fell through to -Shell, so every documented
command silently opened a container shell. Recorded as D019.

Delete tests/infrastructure/runner/, a self-hosted-runner-in-Docker superseded
by Actions Runner Controller.

Make disk_storage and iso_storage required. Their defaults named a NAS that the
lab replaced with Ceph, and CI overrides both from repository variables, so the
defaults only ever misled local runs. require_env now fails at the top of a run
rather than at terraform apply, and the descriptions point at tests/.env.test
because cmd_provision deletes terraform.tfvars before applying.

preflight-cleanup.sh no longer falls back to the literal "local" storage. An
unset TF_VAR_iso_storage now skips only the ISO branch, leaving VM destroy and
state cleanup intact, and emits a workflow annotation: force-cleanup is the
only cleanup CI runs and it wipes Terraform state, so a silent skip strands the
uploaded ISO with nothing left to reclaim it.

Drop docker-ce-cli and the /var/run/docker.sock mount. Nothing in the container
has called docker since #87 moved storage into a VM; the remaining docker calls
run inside that VM over SSH. The CI job image is built from the same target, so
this also removes a third-party apt repository from its supply chain.

Rewrite tests/.env.test.example against what the code now requires, and fix the
documented commands in CLAUDE.md, README.md, copilot-instructions.md and the
integration README.
2026-09-01 16:27:10 -05:00
goodolclint-claude[bot] c115863e07 docs: record D017 and D018 for the two-lane CI split 2026-09-01 15:29:32 -05:00
goodolclint-claude[bot] b51a5a9352 docs: supersede D015, record D016 - native reboot avoids the cleanup race 2026-09-01 17:48:52 +00:00
goodolclint-claude[bot] 27d12d8736 docs: D015 states the fallback tests the latest observation 2026-09-01 16:49:02 +00:00
goodolclint-claude[bot] 7e8cda9328 docs: record D015 - lifecycle -Wait blocks until the guest config lock clears 2026-09-01 16:36:51 +00:00
goodolclint-claude[bot] 73d7978c64 docs: record D014 — New-PveCluster -Wait blocks until quorate
Also documents the two distinct timeout conventions in the module so the
-Wait vs HTTP-client distinction does not have to be re-derived.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 06:25:42 +00:00
Clint Branham 3ce19a721c feat: emit native types instead of Newtonsoft JObject/JArray in public APIs
Replace all Newtonsoft.Json.Linq types (JObject, JArray, JToken) exposed
in public service methods, model properties, and cmdlet OutputTypes with
native .NET types (Dictionary<string, object?>, List<Dictionary<string,
object?>>, PSObject).

New utilities:
- JsonHelper: ToNative, ToDictionary, ToListOfDictionaries for recursive
  JToken→native conversion
- NativeListConverter / NativeDictionaryConverter: JsonConverters for
  model properties that deserialize JArray/JObject to native types

Services updated (8 methods):
- NodeService: GetNodeConfig, GetNodeDns → Dictionary<string, object?>
- BackupService: GetNotBackedUp → List<Dictionary<string, object?>>
- ClusterConfigService: GetClusterConfig, GetTotem, GetQdevice → Dictionary
- HaService: GetManagerStatus → Dictionary<string, object?>
- VmService: GetGuestExecStatus → Dictionary<string, object?>

Models updated (2 properties):
- PvePool.Members: JArray → List<Dictionary<string, object?>>
- PveHaRule.Properties: JObject → Dictionary<string, object?>

Cmdlets updated (5):
- GetPveClusterConfigCmdlet: OutputType JToken → Dictionary<string, object>
- GetPveNodeConfigCmdlet: iteration updated for Dictionary
- GetPveNodeDnsCmdlet: iteration updated for Dictionary
- GetPveBackupInfoCmdlet: iteration updated for List<Dictionary>
- InvokePveVmGuestExecCmdlet: polling updated for Dictionary

Added D013 to DECISIONS.md: cmdlets must emit only native or module-defined types.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 11:12:57 -05:00
Clint Branham 0fa92d689f docs(review): scan-4 review report and findings update
Add scan-4 full review: 1 resolved (F040), 6 new findings (F071-F076).
27 open findings, 49 resolved. No regressions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 09:07:51 -05:00