93 lines
3.2 KiB
Go
93 lines
3.2 KiB
Go
|
|
package service
|
||
|
|
|
||
|
|
import (
|
||
|
|
"encoding/json"
|
||
|
|
"net/http"
|
||
|
|
"net/http/httptest"
|
||
|
|
"strings"
|
||
|
|
"testing"
|
||
|
|
"time"
|
||
|
|
|
||
|
|
"metis/pkg/facts"
|
||
|
|
"metis/pkg/sentinel"
|
||
|
|
)
|
||
|
|
|
||
|
|
func TestScratchHealthAnnotations(t *testing.T) {
|
||
|
|
observed := time.Date(2026, 4, 22, 6, 45, 0, 0, time.UTC)
|
||
|
|
annotations := scratchHealthAnnotations(&facts.USBScratch{
|
||
|
|
Mountpoint: "/mnt/astraios",
|
||
|
|
UUID: "usb-1",
|
||
|
|
FS: "ext4",
|
||
|
|
MountHealthy: true,
|
||
|
|
UUIDHealthy: true,
|
||
|
|
BindHealthy: true,
|
||
|
|
BindTargets: []facts.USBBindTarget{{Path: "/var/log/pods", Healthy: true}, {Path: "/var/tmp", Healthy: true}},
|
||
|
|
}, observed)
|
||
|
|
if annotations["maintenance.bstein.dev/usb-scratch-status"] != "ok" || annotations["maintenance.bstein.dev/astraios-detail"] != "healthy" {
|
||
|
|
t.Fatalf("unexpected healthy annotations: %#v", annotations)
|
||
|
|
}
|
||
|
|
if annotations["maintenance.bstein.dev/usb-scratch-selector"] != "UUID=usb-1" {
|
||
|
|
t.Fatalf("selector annotation missing: %#v", annotations)
|
||
|
|
}
|
||
|
|
if annotations["maintenance.bstein.dev/astraios-managed-paths"] != "/var/log/pods_/var/tmp" {
|
||
|
|
t.Fatalf("managed paths annotation mismatch: %#v", annotations)
|
||
|
|
}
|
||
|
|
|
||
|
|
status, detail := scratchStatusDetail(&facts.USBScratch{MountHealthy: false, BindHealthy: false})
|
||
|
|
if status != "error" || !strings.Contains(detail, "mount-unhealthy") || !strings.Contains(detail, "bind-mount-incomplete") {
|
||
|
|
t.Fatalf("unexpected unhealthy detail: %s %s", status, detail)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestStoreSnapshotPatchesNodeAnnotations(t *testing.T) {
|
||
|
|
var patchPath string
|
||
|
|
var patchContentType string
|
||
|
|
var patchBody map[string]any
|
||
|
|
kube := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||
|
|
if r.Method != http.MethodPatch || r.URL.Path != "/api/v1/nodes/titan-04" {
|
||
|
|
http.NotFound(w, r)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
patchPath = r.URL.Path
|
||
|
|
patchContentType = r.Header.Get("Content-Type")
|
||
|
|
if err := json.NewDecoder(r.Body).Decode(&patchBody); err != nil {
|
||
|
|
t.Fatalf("decode patch body: %v", err)
|
||
|
|
}
|
||
|
|
_ = json.NewEncoder(w).Encode(map[string]any{"status": "ok"})
|
||
|
|
}))
|
||
|
|
defer kube.Close()
|
||
|
|
|
||
|
|
origFactory := kubeClientFactory
|
||
|
|
kubeClientFactory = func() (*kubeClient, error) {
|
||
|
|
return kubeClientFactoryForURL(kube.URL, kube.Client()), nil
|
||
|
|
}
|
||
|
|
t.Cleanup(func() { kubeClientFactory = origFactory })
|
||
|
|
|
||
|
|
app := newTestApp(t)
|
||
|
|
if err := app.StoreSnapshot(SnapshotRecord{
|
||
|
|
Node: "titan-04",
|
||
|
|
CollectedAt: time.Date(2026, 4, 22, 6, 50, 0, 0, time.UTC),
|
||
|
|
Snapshot: sentinel.Snapshot{
|
||
|
|
Hostname: "titan-04",
|
||
|
|
USBScratch: &facts.USBScratch{
|
||
|
|
Mountpoint: "/mnt/astraios",
|
||
|
|
UUID: "usb-1",
|
||
|
|
MountHealthy: true,
|
||
|
|
UUIDHealthy: true,
|
||
|
|
BindHealthy: true,
|
||
|
|
BindTargets: []facts.USBBindTarget{{Path: "/var/log/pods", Healthy: true}},
|
||
|
|
},
|
||
|
|
},
|
||
|
|
}); err != nil {
|
||
|
|
t.Fatalf("StoreSnapshot: %v", err)
|
||
|
|
}
|
||
|
|
if patchPath != "/api/v1/nodes/titan-04" || patchContentType != "application/merge-patch+json" {
|
||
|
|
t.Fatalf("patch request mismatch: path=%q content-type=%q", patchPath, patchContentType)
|
||
|
|
}
|
||
|
|
metadata := patchBody["metadata"].(map[string]any)
|
||
|
|
annotations := metadata["annotations"].(map[string]any)
|
||
|
|
if annotations["maintenance.bstein.dev/usb-scratch-status"] != "ok" || annotations["maintenance.bstein.dev/astraios-selector"] != "UUID=usb-1" {
|
||
|
|
t.Fatalf("annotation patch mismatch: %#v", annotations)
|
||
|
|
}
|
||
|
|
}
|