Skip to content

VM write path: create/setConfig/start/stop/delete + reconciler#14

Merged
ChrisonSimtian merged 3 commits into
mainfrom
feat/vm-write-path
Jun 12, 2026
Merged

VM write path: create/setConfig/start/stop/delete + reconciler#14
ChrisonSimtian merged 3 commits into
mainfrom
feat/vm-write-path

Conversation

@ChrisonSimtian

Copy link
Copy Markdown
Collaborator

What

Extends ProxmoxSharp from a read-only client into a layered, dry-run-by-default VM write path — the foundation for managing Proxmox VMs as IaC (Homelab #115, gaming VM).

Layers

  • CodegenProxmoxSharp.Api.csproj now drives Kiota with --methods GET,POST,PUT,DELETE (new ApiMethods property; path scope unchanged). The read surface is unaffected.
  • L1 QemuWritercreate/setConfig/delete send a form-urlencoded body over the shared Kiota adapter (Proxmox's indexed params hostpci0/scsi0/netN can't be expressed by the generated typed query surface, which collapses them to hostpci[n]); start/stop/shutdown/pci/config-read/task-poll use the generated builders. ProxmoxApi.CreateAdapter shares the exact token-auth + TLS wiring with the read client.
  • L2QemuVmSpec (native desired-state record) + QemuParamEncoder (pure option-string serialization) + VmReconciler with adoption-safe subset diffing: a key is satisfied when every desired comma-token is present in the live value, so Proxmox-added options (size=120G, reordering) don't register as drift.
  • L3 CLIproxmoxsharp vm plan | apply | start | stop | delete | pci | show (--confirm to mutate; dry-run by default).

Tests

19 new unit tests for the encoder + reconciler (full suite 25, green), including the headline case: reconciling a spec against an adopted VM yields exactly the genuine change.

Verified live

  • vm plan against a real VM → only the true diff (+ hostpci0, a rename), all Proxmox-managed keys left untouched.
  • Full create → start → stop → delete(purge) lifecycle exercised on a throwaway VM (every task OK, cleaned up).

Notes

  • Generated client output stays gitignored (produced on build).
  • Versioning left to CI's preview-suffix flow.

🤖 Generated with Claude Code

Extends ProxmoxSharp from read-only into a layered VM write path (dry-run by
default), so callers never hand-write Proxmox's comma-option config strings.

- Codegen now emits write verbs: ProxmoxSharp.Api.csproj drives Kiota with
  --methods GET,POST,PUT,DELETE (new ApiMethods property; scope unchanged).
- L1 QemuWriter: create/setConfig/delete via a form-urlencoded body over the
  shared adapter (Proxmox indexed params hostpci0/scsi0/netN can't be expressed
  by the generated typed query surface); start/stop/shutdown/pci/config-read/
  task-poll via the generated builders. ProxmoxApi.CreateAdapter shares the
  exact auth/TLS wiring with the read client.
- L2 QemuVmSpec + QemuParamEncoder (pure option-string serialization) +
  VmReconciler with adoption-safe subset diffing (Proxmox-added options like
  size=120G don't register as drift).
- L3 CLI: vm plan | apply | start | stop | delete | pci | show
  (--confirm to mutate; dry-run by default).
- 19 new unit tests for the encoder + reconciler (full suite 25, green).

Verified live: `vm plan` vs a real VM yields only the genuine diff; full
create -> start -> stop -> delete lifecycle exercised on a throwaway VM.

Part of Homelab #115 (gaming VM as IaC).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ChrisonSimtian and others added 2 commits June 13, 2026 00:26
A VM created from a fresh-allocation spec (storage:N disk, unpinned NIC) re-planned
as false drift, because Proxmox realizes those as storage:<volid>+size=NG and an
auto-assigned MAC. The reconciler now matches:
  - a bare NIC model (virtio) against the live virtio=<mac>, and
  - a size-allocation (storage:N) against the realized storage:<volid> + size=NG.
Resize, model-change and wrong-storage are still flagged as real drift.

Found by creating a persistent dummy VM (9999) and re-planning it. Adds the
samples/dev-vm.json fixture used to exercise the write path, and 6 reconciler
tests (suite 31, green). Live: VM 9999 now re-plans as Skip.

Part of Homelab #115.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…lake)

CI build broke on SchemaGen with "CreateAppHost task failed / could not find
apphost" (SetUnixFileMode) after the runner's SDK moved to 10.0.301 — unrelated
to any source change. SchemaGen runs via `dotnet run` and the CLI ships as a
`dotnet tool`; neither uses a native apphost. Set UseAppHost=false repo-wide
(no-op for libraries) to skip the buggy task.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@ChrisonSimtian
ChrisonSimtian merged commit 13fe502 into main Jun 12, 2026
1 check passed
@ChrisonSimtian
ChrisonSimtian deleted the feat/vm-write-path branch June 12, 2026 12:32
ChrisonSimtian added a commit to Chrison-Homelab/Homelab that referenced this pull request Jun 12, 2026
Scopes & starts the IaC-managed **Bazzite (SteamOS-style) gaming VM** on
desktop-01 with GPU passthrough. Resolves the schema + stack portions of
#115. Plan:
[`docs/plans/115-gaming-vm-steamos.md`](docs/plans/115-gaming-vm-steamos.md).

## In this PR (hub-side, Phase A)

- **Schema** — `kind: VM` tightened from permissive into a strict
`vmSpec` (requires `vmid`), with the novel **`hostPci[]`** passthrough
def plus `vmDisk`/`vmEfiDisk`/`vmTpmState`/`vmCdrom`/`vmBoot`, and the
`kind: VM` `allOf` branch.
- **Model + tests** — mirrored `VmSpec` C# model; drift tripwire
extended (`vmSpec`/`hostPci`/`vmDisk`); positive/negative validator
tests. **29 engine tests green.**
- **Stack** — `stacks/Gaming/` (VMID block 1000–1099): `stack.yaml` +
`bazzite.vm.yaml` (adopts the existing VM 1003, adds `hostpci0:
0000:09:00`) + `README.md`. Scaffolded locally; promoted to a submodule
later.

Also includes a one-line drive-by: the `/doctor` permission-rule fix
(`mcp__claude-flow__:*` → `mcp__claude-flow__*`) as its own commit.

## Depends on (separate repo)

The **ProxmoxSharp VM write path** that actually provisions these shapes
— Chrison-dev/ProxmoxSharp#14 (built, 25 tests green, live-verified).
The hub consumes ProxmoxSharp as a published package, so this PR is
independent; the `vendor/ProxmoxSharp` submodule pointer should be
bumped once #14 merges and a preview package publishes.

## Not in this PR

- `kind: VM` provisioning wiring into the hub converge engine (uses
ProxmoxSharp#14) — a follow-up.
- Applying `+ hostpci0` to the live 1003 (Phase C) — deliberately
deferred, mutating.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ChrisonSimtian added a commit that referenced this pull request Jun 20, 2026
#17)

Adds PctWriter, the container counterpart to QemuWriter (#14), giving an IaC write
path to tear a CT down instead of root SSH + pct. Motivated by Chrison-Homelab/Homelab#149
(decommissioning Teleport CT 9904 had no sanctioned write path).

- PctWriter: StartAsync / StopAsync (hard kill) / ShutdownAsync (graceful + hard-stop
  fallback) / DeleteAsync (force + purge). Mirrors QemuWriter — raw form requests over
  the shared Kiota adapter (lifecycle ops carry forceStop/timeout/force/purge query
  flags), same token-auth/TLS + error-body surfacing, returns a task UPID; WaitForTaskAsync.
- CLI: `proxmoxsharp lxc <start|stop|shutdown|delete> <node> <vmid>`; delete is gated on
  --confirm with --force/--no-purge, same discipline as `vm delete`.
- Pure query-builder helpers (DeleteQuery/ShutdownQuery) unit-tested (5 new; suite green).

Relates to Chrison-Homelab/Homelab#149 and the converge destroy lifecycle (#101).

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant