Adds the kvm block to the providers config (SSH access, images map, machine-type profiles, hosts list) with validation and table tests. validate() now rejects mismatched type-specific blocks generically via setBlocks() instead of pairwise checks. Step 1 of docs/plans/2026-09-04-1404-kvm-provider.md. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2.5 KiB
Execution: KVM/libvirt provider
Plan: 2026-09-04-1404-kvm-provider.md
- Step 0 — Save plan + branch
feat/kvm-provider - Step 1 — Config (
KVMConfig& friends,validate()case, tests) - Step 2 — Pure builders (
domainxml.go,volume.go,seed.go,id.go,metadata.go) - Step 3 — Transport + errors (
conn.go,errors.go) - Step 3b — Image cache (
image.go) - Step 4 — Provider methods (
kvm.go) over a fake API - Step 5 — Wiring, manifests, samples, docs, CHANGELOG
- Step 6 — Integration test against a real hypervisor
- Step 7 — Manual validation + MR
Step 0 — Save plan + branch
Plan committed alone on feat/proxy-operator, then branched:
git checkout -b feat/kvm-provider
Step 1 — Config
The new structs (KVMConfig, KVMHost, KVMMachineType, KVMImage) and
validateKVM went into their own file, internal/provider/config_kvm.go,
rather than being appended to config.go as the plan said: the KVM block has
far more fields and rules than the other two providers, and keeping it
separate leaves config.go readable. Tests likewise live in
config_kvm_test.go instead of extending the table in config_test.go; the
invalid-config table covers every required field, duplicate host names, image
URL scheme, sha256 shape, and the disk format enum.
While adding the kvm case to validate(), the pairwise "type is X but a Y
block is set" checks (which would have needed six branches with three types)
were replaced by a setBlocks() helper that lists the non-nil blocks and
rejects any that don't match type. The error strings are unchanged, so the
existing GCP/kubernetes tests still pass.
Dependencies were fetched up front so the API signatures in the plan could be
checked with go doc; they stay // indirect in go.mod until Step 2 imports
them:
go get github.com/digitalocean/go-libvirt@latest github.com/kdomanski/iso9660@v0.4.0
# go-libvirt resolved to v0.0.0-20260814190004-1a83157e1858 (no tagged release)
# side effect: golang.org/x/crypto 0.54.0 → 0.56.0, x/mod, x/text, x/tools bumped
Worth noting: go doc showed that go-libvirt's New(net.Conn) is deprecated
in favour of NewWithDialer(socket.Dialer), where Dialer is a one-method
Dial() (net.Conn, error) interface. Step 3 should wrap the SSH client's unix
forward in that interface instead of using New; the plan's transport design
is otherwise unaffected.