kvm2: base image cache with a streamed upload and the pool-space precheck

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
2026-09-16 18:19:05 +00:00
co-authored by Claude Fable 5.1
parent b45f83327a
commit 6b3c985b4a
5 changed files with 1605 additions and 11 deletions
@@ -7,7 +7,7 @@ Plan: [docs/plans/2026-09-16-1333-kvm2-provider.md](../plans/2026-09-16-1333-kvm
- [x] Step 2 — Config wiring, IDs, state mapping, classify
- [x] Step 3 — Pure builders: domain and volume XML, metadata, seed
- [x] Step 4 — Transport, seam, decorator, fakes
- [ ] Step 5 — Image cache and pool-space precheck
- [x] Step 5 — Image cache and pool-space precheck
- [ ] Step 6 — The four contract methods
- [ ] Step 7 — Checker and the CRD comment
- [ ] Step 8 — Deployment files, Makefile, integration test
@@ -171,3 +171,42 @@ grep -n 'context.Background()' internal/provider/kvm2/*.go | grep -v _test.go
```
All `ok`; the agent also ran the timing-sensitive tests at `-count=8`.
## Step 5 — Image cache and pool-space precheck
2026-09-16 18:11. Fourth subagent step: `image.go` and `image_test.go`
(27 tests), plus additive changes to `fake_test.go` (a `twin()` sharing
state with its own call log, so a test can tell the pooled connection from
the dedicated one) and `kvm2.go` (`http` client, budgets and tracing
options behind functional options, so `New` is unchanged). The pipeline:
marker lookup and bounded read on the pooled connection, then a GET bound to
ctx plus the 20-minute cap under a `kvm2.download` span, `StorageVolCreateXML`
and a streamed `StorageVolUpload` through sha256 on a fresh connection,
`StoragePoolRefresh`, and the marker written last; every failure after the
volume exists discards it with the cancellation stripped from ctx so the
cleanup RPC still runs.
Streaming is asserted by counting: a hook on the dedicated fake's upload
snapshots how many bytes the 64 MiB test server had written when the upload
began, and asserts under 16 MiB. The agent chose that over a process-wide
`MemStats` delta because the tests run in parallel.
Decisions the agent made, accepted: the base volume is created on the
dedicated connection (nothing exists if the dial fails); `Accept-Encoding:
identity` is sent so transparent gunzip cannot hide the Content-Length; a
marker whose content is not 64 hex characters counts as debris;
`Content-Length: 0` is its own Permanent error; the image lock is a
one-slot channel so a waiter gives up with its ctx instead of sitting out a
transfer; the fetch logger is derived after the span starts so its lines
carry the span ID (the agent's first test run caught that).
Commands (agent and orchestrator):
```sh
gofmt -l ./internal/provider/kvm2 ; go vet ./internal/provider/kvm2/ && go test -race -count=2 ./internal/provider/kvm2/
go build ./... && go test -short ./...
grep -n 'io.ReadAll\|bytes.Buffer' internal/provider/kvm2/image.go # empty
grep -n 'context.Background()' internal/provider/kvm2/*.go | grep -v _test.go # empty
```
All `ok`. Attestation: "files read outside the allowed set: none".