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:
@@ -2,6 +2,8 @@ package kvm2
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -18,7 +20,9 @@ import (
|
||||
// libvirt host for the provider's Create, Get, Delete and ListByTag, with
|
||||
// per-method error hooks and blocking, call counters and an ordered log.
|
||||
type fakeBackend struct {
|
||||
mu sync.Mutex
|
||||
// mu, domains and pools are the host's state; a twin shares them, so
|
||||
// two connections to the same fake host see the same volumes.
|
||||
mu *sync.Mutex
|
||||
domains map[string]*fakeDomain
|
||||
pools map[string]*fakePool
|
||||
hostname string
|
||||
@@ -28,6 +32,10 @@ type fakeBackend struct {
|
||||
hooks map[string]func() error
|
||||
// block makes a method wait on its channel (or ctx) before running.
|
||||
block map[string]chan struct{}
|
||||
// discardUploads makes StorageVolUpload stream the reader through a
|
||||
// sha256 in fixed chunks instead of holding the bytes, for tests that
|
||||
// push more through the fake than they want in memory.
|
||||
discardUploads bool
|
||||
|
||||
calls map[string]int
|
||||
log []string
|
||||
@@ -55,12 +63,16 @@ type fakePool struct {
|
||||
type fakeVol struct {
|
||||
content []byte
|
||||
capacity uint64
|
||||
// uploaded and sha256 record a discarded upload (see discardUploads).
|
||||
uploaded int64
|
||||
sha256 string
|
||||
}
|
||||
|
||||
const fakePoolName = "default"
|
||||
|
||||
func newFakeBackend() *fakeBackend {
|
||||
return &fakeBackend{
|
||||
mu: &sync.Mutex{},
|
||||
domains: make(map[string]*fakeDomain),
|
||||
pools: map[string]*fakePool{fakePoolName: newFakePool()},
|
||||
hostname: "fake-host",
|
||||
@@ -72,6 +84,39 @@ func newFakeBackend() *fakeBackend {
|
||||
}
|
||||
}
|
||||
|
||||
// twin returns a second connection to the same fake host: it shares the
|
||||
// domains and pools (and their lock) but has its own hooks, counters, log,
|
||||
// close count and liveness, so a test can tell which connection did what.
|
||||
func (f *fakeBackend) twin() *fakeBackend {
|
||||
return &fakeBackend{
|
||||
mu: f.mu,
|
||||
domains: f.domains,
|
||||
pools: f.pools,
|
||||
hostname: f.hostname,
|
||||
version: f.version,
|
||||
hooks: make(map[string]func() error),
|
||||
block: make(map[string]chan struct{}),
|
||||
discardUploads: f.discardUploads,
|
||||
calls: make(map[string]int),
|
||||
deadCh: make(chan struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
// vol returns the volume by name in the default pool, or nil.
|
||||
func (f *fakeBackend) vol(name string) *fakeVol {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
return f.pools[fakePoolName].vols[name]
|
||||
}
|
||||
|
||||
// closeCount is closed read under the lock, for twins closed by another
|
||||
// goroutine.
|
||||
func (f *fakeBackend) closeCount() int {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
return f.closed
|
||||
}
|
||||
|
||||
func newFakePool() *fakePool {
|
||||
return &fakePool{state: libvirt.StoragePoolRunning, capacity: 100 << 30, available: 50 << 30, vols: make(map[string]*fakeVol)}
|
||||
}
|
||||
@@ -422,10 +467,20 @@ func (f *fakeBackend) StorageVolUpload(ctx context.Context, vol libvirt.StorageV
|
||||
if err := f.enter(ctx, "StorageVolUpload"); err != nil {
|
||||
return err
|
||||
}
|
||||
var content []byte
|
||||
var (
|
||||
content []byte
|
||||
uploaded int64
|
||||
digest string
|
||||
)
|
||||
if r != nil {
|
||||
var err error
|
||||
if content, err = io.ReadAll(r); err != nil {
|
||||
if f.discardUploads {
|
||||
h := sha256.New()
|
||||
if uploaded, err = io.CopyBuffer(h, r, make([]byte, 64<<10)); err != nil {
|
||||
return err
|
||||
}
|
||||
digest = hex.EncodeToString(h.Sum(nil))
|
||||
} else if content, err = io.ReadAll(r); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -436,6 +491,8 @@ func (f *fakeBackend) StorageVolUpload(ctx context.Context, vol libvirt.StorageV
|
||||
return err
|
||||
}
|
||||
v.content = content
|
||||
v.uploaded = uploaded
|
||||
v.sha256 = digest
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user