feat: add proxmox reconciliation job

This commit is contained in:
Philipp
2026-06-11 14:48:50 +02:00
parent d08e41b013
commit 137c13fa98
10 changed files with 487 additions and 2 deletions
@@ -0,0 +1,124 @@
package tasks
import (
"context"
"log/slog"
"testing"
)
func TestProxmoxReconcileHandlerWritesAuditForDrift(t *testing.T) {
client := &stubReconcileClient{status: ProxmoxVMStatus{Status: "stopped"}}
auditWriter := &stubAuditWriter{}
handler := NewProxmoxReconcileHandler(
&stubReconcileClusterStore{clusterIDs: []string{"cluster-1"}},
&stubReconcileVMStore{vms: []ReconcileVM{{
ID: "vm-1",
TenantID: "tenant-1",
Node: "pve",
ProxmoxVMID: 100,
Status: "running",
}}},
&stubReconcileClientResolver{client: client},
auditWriter,
slog.Default(),
)
if err := handler.ProcessTask(context.Background(), NewProxmoxReconcileAllTask()); err != nil {
t.Fatalf("ProcessTask() error = %v", err)
}
if client.node != "pve" {
t.Fatalf("client node = %q, want pve", client.node)
}
if auditWriter.event.Action != "vm.reconcile.drift" {
t.Fatalf("audit action = %q, want vm.reconcile.drift", auditWriter.event.Action)
}
if auditWriter.event.Metadata["db_status"] != "running" {
t.Fatalf("audit db_status = %v, want running", auditWriter.event.Metadata["db_status"])
}
if auditWriter.event.Metadata["proxmox_status"] != "stopped" {
t.Fatalf("audit proxmox_status = %v, want stopped", auditWriter.event.Metadata["proxmox_status"])
}
}
func TestProxmoxReconcileHandlerSkipsMatchingStatus(t *testing.T) {
auditWriter := &stubAuditWriter{}
handler := NewProxmoxReconcileHandler(
&stubReconcileClusterStore{clusterIDs: []string{"cluster-1"}},
&stubReconcileVMStore{vms: []ReconcileVM{{
ID: "vm-1",
TenantID: "tenant-1",
Node: "pve",
ProxmoxVMID: 100,
Status: "running",
}}},
&stubReconcileClientResolver{client: &stubReconcileClient{status: ProxmoxVMStatus{Status: "running"}}},
auditWriter,
slog.Default(),
)
if err := handler.ProcessTask(context.Background(), NewProxmoxReconcileAllTask()); err != nil {
t.Fatalf("ProcessTask() error = %v", err)
}
if auditWriter.event.Action != "" {
t.Fatalf("audit action = %q, want empty", auditWriter.event.Action)
}
}
func TestNormalizeProxmoxVMStatus(t *testing.T) {
tests := []struct {
name string
status string
want string
}{
{name: "running", status: "running", want: "running"},
{name: "stopped", status: "stopped", want: "stopped"},
{name: "paused", status: "paused", want: "suspended"},
{name: "unknown", status: "unknown", want: ""},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
if got := normalizeProxmoxVMStatus(test.status); got != test.want {
t.Fatalf("normalizeProxmoxVMStatus() = %q, want %q", got, test.want)
}
})
}
}
type stubReconcileClusterStore struct {
clusterIDs []string
}
func (s *stubReconcileClusterStore) ListActiveClusterIDs(context.Context) ([]string, error) {
return s.clusterIDs, nil
}
type stubReconcileVMStore struct {
vms []ReconcileVM
}
func (s *stubReconcileVMStore) ListClusterVMs(context.Context, string) ([]ReconcileVM, error) {
return s.vms, nil
}
type stubReconcileClientResolver struct {
client ProxmoxReconcileClient
}
func (s *stubReconcileClientResolver) ResolveReconcileClient(context.Context, string) (ProxmoxReconcileClient, error) {
return s.client, nil
}
type stubReconcileClient struct {
node string
vmid int
status ProxmoxVMStatus
}
func (s *stubReconcileClient) GetVMStatus(_ context.Context, node string, vmid int) (ProxmoxVMStatus, error) {
s.node = node
s.vmid = vmid
return s.status, nil
}