Commit Graph

406 Commits

Author SHA1 Message Date
goodolclint-claude[bot] bdbd5eeb4d Revert CLAUDE.md push-convention note (claim was unverified) 2026-09-01 18:38:15 +00:00
goodolclint-claude[bot] 959b62c86f test: assert node B joined by name, not an online-node count
Closes #94
2026-09-01 18:37:22 +00:00
GoodOlClint f96511bbe0 Merge pull request #101 from GoodOlClint/ci/review-fail-closed
ci: fail the review job when no review actually ran
2026-09-01 13:35:21 -05:00
Clint Branham ae3b14fe47 ci: fail the review job when no review actually ran
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 13:33:06 -05:00
goodolclint-claude[bot] f6a0d547b3 Merge pull request #100 from GoodOlClint/ci/iso-name-hash-followup
ci: hash first-boot.sh into the cached auto-install ISO name
2026-09-01 18:29:51 +00:00
Clint Branham c15820b5aa ci: hash first-boot.sh into the cached auto-install ISO name
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 13:24:36 -05:00
GoodOlClint 5b1493e968 Merge pull request #99 from GoodOlClint/ci/review-bot-submits-verdict
ci: review bot submits a formal review verdict
2026-09-01 13:24:17 -05:00
Clint Branham 1edb9d6d74 ci: review bot submits a formal review verdict
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 13:20:52 -05:00
GoodOlClint 2c9f3d45a3 Merge pull request #98 from GoodOlClint/fix/restart-uses-native-reboot
fix: Restart-PveVm uses PVE's native reboot endpoint
2026-09-01 12:57:30 -05:00
goodolclint-claude[bot] d94cebae14 feat: VmService.RebootVm calls PVE's native reboot endpoint 2026-09-01 17:52:19 +00: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] b78d2423ff docs: changelog for the native reboot fix; correct the D015 entry 2026-09-01 17:47:17 +00:00
goodolclint-claude[bot] c5e73f4fc9 fix: Restart-PveVm uses PVE's native reboot endpoint (cmdlet + tests) 2026-09-01 17:46:21 +00:00
GoodOlClint 2846395662 Merge pull request #97 from GoodOlClint/fix/vm-config-lock-wait
fix: lifecycle -Wait blocks until the guest config lock clears
2026-09-01 11:54:18 -05:00
GoodOlClint fc5b2eb46b Merge branch 'main' into fix/vm-config-lock-wait 2026-09-01 11:53:12 -05: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] 70c147b47d fix: post-loop fallback tests the latest poll, not "ever matched"
Review finding on #97: `statusReached` was sticky, so a guest that reached the
expected status once and then drifted away from it would still be reported as a
success at the deadline, contradicting the fallback's own meaning.

Track the most recent observation instead. A guest that drifts away and never
re-matches now raises PveTaskTimeoutException as it should. A failed poll leaves
the previous observation standing, so an API blip is not read as divergence.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 16:47:41 +00:00
goodolclint-claude[bot] 56578e7d1c Merge pull request #96 from GoodOlClint/ci/pve-9.2-iso
ci: test against the PVE 9.2 ISO

The nested nodes were pinned to proxmox-ve_9.1-1.iso while production runs 9.2.
Validated by runs 183/184; the cluster suite passed in both, and run 184 was
fully green at 148 passed / 0 failed / 5 skipped.
2026-09-01 16:37:31 +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] 8cb1457ff2 docs: changelog entry and D015 for the config-lock wait 2026-09-01 16:35:36 +00:00
goodolclint-claude[bot] 5f1feb8b32 fix: wait for the config lock in WaitForStatusTransition; record D015 2026-09-01 16:34:27 +00:00
goodolclint-claude[bot] 3f63ddcad8 fix: lifecycle -Wait blocks until the guest config lock clears
PVE publishes a guest's new status while the operation still holds
/var/lock/qemu-server/lock-<vmid>.conf, so WaitForStatusTransition could return
while the guest was still locked and the caller's next request would fail with
"can't lock file ... got timeout".

Integration run 183 failed four tests from this one cause: Restart-PveVm -Wait
returned after 4.1s having seen "running", the following Stop-PveVm spent exactly
10.0s failing to take the lock, and that cascaded into the template convert,
clone, and remove tests. Run 184 - same commit, re-run - passed because its status
poll happened to take 10.1s, by which point the lock had cleared. The same
settling happens either way; the only variable is whether the wait absorbs it or
the next caller does.

The check goes in WaitForStatusTransition because all nine lifecycle call sites
(Start/Stop/Restart/Reset/Resume across VMs and containers) route through it.
`lock` comes from the status/current response the poll already fetches - present
on both qemu and lxc since PVE 5.4, below the module's 7.0 floor - so it costs no
extra request.

If the status is reached but the lock outlasts -Timeout the cmdlet still returns
success, so a call that succeeded before this change cannot become an exception
after it.

Recorded as D015, the guest-lock sibling of D014.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 16:33:47 +00:00
GoodOlClint 845628711b Merge pull request #95 from GoodOlClint/ci/pin-nested-pve-packages
ci: stop half-upgrading the nested PVE nodes
2026-09-01 10:14:30 -05:00
goodolclint-claude[bot] e549052348 Merge pull request #93 from GoodOlClint/test/cluster-join-link0
fix: New-PveCluster -Wait blocks until quorate; make cluster failures diagnosable

Adds ClusterConfigService.WaitForQuorum with a -Timeout parameter (D014), so
Add-PveClusterMember no longer hits "cluster not ready - no quorum?" after a
completed create task. Adds diagnose-cluster.sh, which captures corosync and
pmxcfs state plus PVE task logs from both nodes before cleanup destroys them.
2026-09-01 14:57:59 +00:00
goodolclint-claude[bot] e2bbe1224f ci: test against the PVE 9.2 ISO
The nested nodes were pinned to proxmox-ve_9.1-1.iso while production runs
9.2, so the suite was validating a PVE nobody uses. run-integration.sh already
reads PVE9_ISO with the 9.1 name only as a fallback, so this needs no script
change; setting it at the workflow level keeps provision and cleanup agreeing
on the ISO name, which force-cleanup needs to find the right file to delete.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 14:32:33 +00:00
goodolclint-claude[bot] 5e046e3d31 ci: stop half-upgrading the nested nodes
`apt-get upgrade` holds back any package whose upgrade needs new dependencies,
so it neither pins nor updates — it produces whatever partial set the
dependency graph allows that day. On these nodes it upgraded pve-cluster to
9.1.6 while leaving libpve-cluster-api-perl at 9.1.0.

Those two ship the halves of the join: cfs_backup_database() in
PVE/Cluster.pm (pve-cluster) and finish_join() in PVE/Cluster/Setup.pm
(libpve-cluster-api-perl). Upstream removed `return $dbfile` from the former
and stopped relying on it in the latter, both at 9.1.1 — 9.1.6's finish_join
calls cfs_unlink_db_unsafe() instead. The 9.1.0 caller against the 9.1.6
callee unlinks an empty string, so the standalone config.db survives the join,
pmxcfs restarts in local mode, and the node reports online=0 forever while
corosync forms a healthy 2-node membership. That is the "2 nodes online"
failure, and it is not reachable on any coherent install.

The ISO is the pin, so drop the upgrade and install only what the harness
needs. Upgrades belong in a separate currency lane that records the package
set it tested.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 14:25:50 +00:00
goodolclint-claude[bot] 1eb5b0f28d docs: changelog entry for the New-PveCluster quorum fix
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 13:59:38 +00:00
goodolclint-claude[bot] 5c6cd7f40f ci: probe whether pmxcfs came up clustered or in local mode
Run 173 left node B with healthy corosync (2-member primary component, both
links connected) but no /etc/pve/corosync.conf, no dcdb/status journal lines,
and pvecm status reporting it is not part of a cluster. That file is
database-backed: pmxcfs creates it only when it starts with no config.db and
imports /etc/corosync/corosync.conf, so a surviving standalone config.db would
mean silent local mode.

Capture the package versions, pmxcfs command line, /etc/pve mount, .members,
the config.db and its backup dir, whether the database holds a corosync.conf
row, and the CPG group membership. Read-only; the sqlite3 CLI is not guaranteed
on a PVE node, so fall back to strings.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 07:27:31 +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
goodolclint-claude[bot] 1df1b05876 test: drop the fixed sleep and the link0 pin from the cluster tests
New-PveCluster -Wait now guarantees quorum, so the Start-Sleep -Seconds 5
that lost the race is replaced by an assertion on the new contract.

The link0 pin is dropped: the join-abort and the never-a-member modes both
occurred with and without it, so it was never implicated, and -Links is
already covered at the service level.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 06:24:11 +00:00
goodolclint-claude[bot] 2df9f47e2d test: cover ClusterConfigService.WaitForQuorum
Returns once quorate, throws TimeoutException when quorum never arrives,
and keeps polling through a PveApiException from the restarting API.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 06:22:55 +00:00
goodolclint-claude[bot] 6881e5c53c feat: add ClusterConfigService.WaitForQuorum
Polls GET /cluster/status for the cluster entry with quorate = 1, bounded
by a timeout (60 s default) and tolerating PveApiException while pmxcfs
and corosync restart during cluster formation.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 06:20:55 +00:00
goodolclint-claude[bot] a4ebcec753 fix: New-PveCluster -Wait blocks until the cluster is quorate
PVE's cluster-create task completes before corosync converges. Until the
node is quorate it rejects a join with "cluster not ready - no quorum?",
so New-PveCluster -Wait followed by Add-PveClusterMember failed for every
caller. Wait for quorum after the task, bounded by -Timeout (default 60 s,
following the -Wait timeout convention used by Stop-PveContainer and
Reset-PveVm). See DECISIONS.md D014.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 06:19:08 +00:00
goodolclint-claude[bot] db93af27b2 ci: capture cluster task logs in the diagnostics
"Cluster join aborted!" is PVE's generic wrapper; the reason lives only in
the task log on the joining node. Run 172's log said "An error occurred on
the cluster node: cluster not ready - no quorum?", which is what identified
the race. Capture it so the evidence survives cleanup.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 06:18:48 +00:00
goodolclint-claude[bot] 4294407d80 ci: run cluster diagnostics on test failure, add keep_vms dispatch input
The diagnostic step runs before cleanup so it reaches the nodes while
they still exist. keep_vms skips cleanup entirely for a dispatch run,
leaving the nested nodes up for hands-on inspection.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 05:38:56 +00:00
goodolclint-claude[bot] 44101e39c7 ci: dump corosync state from both nested nodes on cluster test failure
The API reports a joined-but-offline node as online=0 with no further
detail, and the cleanup job destroys the nodes minutes later, so the
reason corosync membership never forms has never reached a log. Read
corosync.conf, corosync-cfgtool, pvecm status and the corosync journal
off both nodes while they are still alive. Best-effort: never fails the
caller.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 05:38:08 +00:00
goodolclint-claude[bot] e0960d9c18 test: pin node B ring0 at join and dump cluster status on the online check
The "2 nodes online" check has never passed — run 159 shows the identical
failure while the job reported success (swallowed Pester exit code), and
run 170's 60 s poll expires with node B still online=0. Node B reaches
corosync.conf (Get-PveClusterConfigNode passes) but never becomes a
corosync member, and the suite captures nothing about why.

Pass link0 explicitly so ring0 is the address the harness verified node B
answers on, rather than whatever node B resolves its own DHCP-assigned
.test.local hostname to. Dump quorate and each node's ring0/online/local
unconditionally so the next run distinguishes a wrong ring0 address from
a working address with no corosync transport.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 05:02:53 +00:00
GoodOlClint f40324b948 Merge pull request #91 from GoodOlClint/test/cluster-status-poll 2026-08-31 19:21:00 -05:00
goodolclint-claude[bot] f758275668 test: poll cluster status for node B online instead of asserting instantly
The join task completing does not mean corosync membership has reached
the status endpoint; the instant assertion failed intermittently (runs
159 and 169) while every other cluster check passed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-31 23:00:49 +00:00
GoodOlClint 341f79e7ac Merge pull request #90 from GoodOlClint/ci/storage-vm-diagnostics
ci: storage VM on DHCP + FQDN addressing, self-diagnosing setup
2026-08-31 16:18:54 -05:00
goodolclint-claude[bot] 59a47e3fab ci: storage VM moves to DHCP and is addressed by FQDN (2/2)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-31 20:54:14 +00:00
goodolclint-claude[bot] 25746a94e3 ci: storage VM moves to DHCP and is addressed by FQDN (1/2)
The repo stops holding opinions about CI VLAN addressing: the VM boots
via DHCP with hostname pvetest-storage (deterministic MAC for an
optional reservation), registers in the operator's CI DNS zone, and
everything addresses it by STORAGE_VM_FQDN. Replaces the static-IP +
explicit-DNS variables.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-31 20:49:48 +00:00
goodolclint-claude[bot] 7872f08102 ci: storage VM uses public DNS — the CI VLAN gateway runs no resolver
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-31 20:42:07 +00:00
goodolclint-claude[bot] 5d0b384508 ci: storage VM setup diagnoses its own network and logs phases
Run 168 burned 13 minutes to report 'unable to locate package tgt' when
the real failure was dead egress from the CI VLAN (apt lists never
updated). The setup script now probes gateway/internet/DNS from inside
the VM and prints the verdicts, fails apt fast with the real error, and
marks each phase.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-31 20:39:02 +00:00
GoodOlClint e219766452 Merge pull request #88 from GoodOlClint/ci/retire-pve8-leg
ci: retire the PVE 8 integration leg
2026-08-31 15:18:59 -05:00
goodolclint-claude[bot] 77c6510788 ci: default PVE_VERSIONS to 9, count storage VM in headroom message
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-31 20:15:50 +00:00
goodolclint-claude[bot] 932cc0dcf5 ci: retire the PVE 8 integration leg
PVE 8 reached EOL 2026-08-31. The full matrix needs 42GiB free on one
parent node; no node has that after the fleet rebuild, so the headroom
guard blocks every run. Dropping the 8a/8b pair brings the requirement
to 26GiB. The module keeps PVE 8 runtime support and the script still
accepts PVE_VERSIONS='9 8' for local runs; the PVE-8-only Pester
contexts stay for SKIP_PROVISION runs against real PVE 8 hosts. Also
counts the storage VM in the headroom error message.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-31 20:13:33 +00:00
GoodOlClint 5564453177 Merge pull request #87 from GoodOlClint/ci/pester-exit-sandbox-storage
ci: storage and answer services move to a VM inside the CI sandbox
2026-08-31 15:03:54 -05:00
goodolclint-claude[bot] 4d3ea81bd4 ci: remove docker-compose storage definition (moved to storage VM) 2026-08-31 19:56:16 +00:00
goodolclint-claude[bot] 999bac6bdc ci: remove runner-side docker storage resources (moved to storage VM) 2026-08-31 19:56:10 +00:00