Files
egress-proxies-operator/internal/provider/kvm2/volume_test.go
T

153 lines
5.0 KiB
Go

package kvm2
import (
"encoding/xml"
"errors"
"regexp"
"testing"
"gitea.home.hrajfrisbee.cz/kacerr/egress-proxies-operator/internal/provider"
)
type checkedVolume struct {
Type string `xml:"type,attr"`
Name string `xml:"name"`
Capacity struct {
Unit string `xml:"unit,attr"`
Value int64 `xml:",chardata"`
} `xml:"capacity"`
Target struct {
Format struct {
Type string `xml:"type,attr"`
} `xml:"format"`
} `xml:"target"`
BackingStores []struct {
Path string `xml:"path"`
Format struct {
Type string `xml:"type,attr"`
} `xml:"format"`
} `xml:"backingStore"`
}
func mustBuildVolume(t *testing.T, v volumeSpec) checkedVolume {
t.Helper()
out, err := buildVolumeXML(v)
if err != nil {
t.Fatalf("buildVolumeXML: %v", err)
}
var c checkedVolume
if err := xml.Unmarshal([]byte(out), &c); err != nil {
t.Fatalf("unmarshalling produced XML: %v\n%s", err, out)
}
return c
}
func TestVolumeXML_overlayBackingStore(t *testing.T) {
t.Parallel()
c := mustBuildVolume(t, volumeSpec{
Name: "proxy-abc.qcow2",
CapacityBytes: 10 << 30,
Format: provider.KVMFormatQcow2,
BackingPath: "/var/lib/libvirt/images/base-debian-13.qcow2",
BackingFormat: provider.KVMFormatQcow2,
})
if c.Type != "file" || c.Name != "proxy-abc.qcow2" {
t.Errorf("type=%q name=%q, want file/proxy-abc.qcow2", c.Type, c.Name)
}
if c.Capacity.Unit != "bytes" || c.Capacity.Value != 10<<30 {
t.Errorf("capacity = %d %s, want %d bytes", c.Capacity.Value, c.Capacity.Unit, 10<<30)
}
if c.Target.Format.Type != "qcow2" {
t.Errorf("target format = %q, want qcow2", c.Target.Format.Type)
}
if len(c.BackingStores) != 1 {
t.Fatalf("got %d backingStore elements, want 1", len(c.BackingStores))
}
bs := c.BackingStores[0]
if bs.Path != "/var/lib/libvirt/images/base-debian-13.qcow2" || bs.Format.Type != "qcow2" {
t.Errorf("backingStore = %+v, want the base path with qcow2 format", bs)
}
}
func TestVolumeXML_rawWithoutBacking(t *testing.T) {
t.Parallel()
c := mustBuildVolume(t, volumeSpec{Name: "proxy-abc-seed.iso", CapacityBytes: 385024, Format: provider.KVMFormatRaw})
if c.Target.Format.Type != "raw" {
t.Errorf("target format = %q, want raw", c.Target.Format.Type)
}
if c.Capacity.Value != 385024 || c.Capacity.Unit != "bytes" {
t.Errorf("capacity = %d %s, want 385024 bytes", c.Capacity.Value, c.Capacity.Unit)
}
if len(c.BackingStores) != 0 {
t.Errorf("got backingStore %+v, want none", c.BackingStores)
}
}
func TestVolumeXML_rejectsBadFormat(t *testing.T) {
t.Parallel()
tests := []struct {
name string
spec volumeSpec
}{
{name: "vmdk format", spec: volumeSpec{Name: "x", CapacityBytes: 1, Format: "vmdk"}},
{name: "empty format", spec: volumeSpec{Name: "x", CapacityBytes: 1}},
{name: "bad backing format", spec: volumeSpec{Name: "x", CapacityBytes: 1, Format: "qcow2", BackingPath: "/b", BackingFormat: "vdi"}},
{name: "backing path without format", spec: volumeSpec{Name: "x", CapacityBytes: 1, Format: "qcow2", BackingPath: "/b"}},
{name: "empty name", spec: volumeSpec{CapacityBytes: 1, Format: "qcow2"}},
{name: "zero capacity", spec: volumeSpec{Name: "x", Format: "qcow2"}},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
out, err := buildVolumeXML(tc.spec)
if !errors.Is(err, provider.ErrPermanent) {
t.Errorf("err = %v, want ErrPermanent", err)
}
if out != "" {
t.Errorf("got XML %q alongside the error", out)
}
})
}
}
func TestVolumeNames(t *testing.T) {
t.Parallel()
tests := []struct {
name string
got string
want string
}{
{name: "overlay", got: overlayVolumeName("proxy-abc"), want: "proxy-abc.qcow2"},
{name: "seed", got: seedVolumeName("proxy-abc"), want: "proxy-abc-seed.iso"},
{name: "base qcow2", got: baseVolumeName("debian-13", provider.KVMFormatQcow2), want: "base-debian-13.qcow2"},
{name: "base raw", got: baseVolumeName("alpine", provider.KVMFormatRaw), want: "base-alpine.raw"},
{name: "marker", got: markerVolumeName("base-debian-13.qcow2"), want: "base-debian-13.qcow2.sha256"},
{name: "marker of url base", got: markerVolumeName(baseVolumeName(urlImageKey("https://x/y.img"), "qcow2")), want: "base-" + urlImageKey("https://x/y.img") + ".qcow2.sha256"},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
if tc.got != tc.want {
t.Errorf("got %q, want %q", tc.got, tc.want)
}
})
}
t.Run("url key", func(t *testing.T) {
t.Parallel()
hex16 := regexp.MustCompile(`^[0-9a-f]{16}$`)
a := urlImageKey("https://cloud.debian.org/images/cloud/trixie/latest/debian-13-generic-amd64.qcow2")
b := urlImageKey("https://cloud.debian.org/images/cloud/trixie/latest/debian-13-generic-amd64.qcow2")
c := urlImageKey("https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-generic-amd64.qcow2")
if !hex16.MatchString(a) {
t.Errorf("urlImageKey = %q, want 16 lowercase hex chars", a)
}
if a != b {
t.Errorf("urlImageKey not deterministic: %q vs %q", a, b)
}
if a == c {
t.Errorf("urlImageKey gives %q for two different URLs", a)
}
})
}