153 lines
5.0 KiB
Go
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)
|
|
}
|
|
})
|
|
}
|