72 lines
2.5 KiB
Go
72 lines
2.5 KiB
Go
package localapi
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"io"
|
|
"log/slog"
|
|
"testing"
|
|
|
|
"gitea.dooplex.hu/admin/felhom-agent/internal/proxmox"
|
|
)
|
|
|
|
// R-1192: the freeze measured on the minimum machine (felhom.eu audits/fixes-2026-10-21/P4-freeze/) — a guest whose
|
|
// swap is capped below its RAM evicts program code instead of idle memory. EnsureGuestSwap raises every pool guest's
|
|
// swap to its RAM, never lowers one, skips non-LXC members, and survives one guest's failure.
|
|
// COMPANION RED-PROOF: return 0 before the SetConfig call → „raised 0, want 2".
|
|
type fakeSwapOps struct {
|
|
cfg map[int]proxmox.GuestConfig
|
|
failSet map[int]bool
|
|
set map[int]map[string]string
|
|
}
|
|
|
|
func (f *fakeSwapOps) Pool(context.Context, string) (proxmox.PoolInfo, error) {
|
|
return proxmox.PoolInfo{Members: []proxmox.PoolMember{{VMID: 9201, Type: "lxc"}, {VMID: 9202, Type: "lxc"}, {VMID: 300, Type: "qemu"},
|
|
{Type: "storage"}, {VMID: 9203, Type: "lxc"}, {VMID: 9204, Type: "lxc"}}}, nil
|
|
}
|
|
func (f *fakeSwapOps) GuestConfig(_ context.Context, vmid int) (proxmox.GuestConfig, error) {
|
|
c, ok := f.cfg[vmid]
|
|
if !ok {
|
|
return c, errors.New("no such guest")
|
|
}
|
|
return c, nil
|
|
}
|
|
func (f *fakeSwapOps) SetConfig(_ context.Context, vmid int, p map[string]string) (string, error) {
|
|
if f.failSet[vmid] {
|
|
return "", errors.New("pve said no")
|
|
}
|
|
if f.set == nil {
|
|
f.set = map[int]map[string]string{}
|
|
}
|
|
f.set[vmid] = p
|
|
return "", nil
|
|
}
|
|
func (f *fakeSwapOps) WaitTask(context.Context, string, proxmox.WaitOptions) (proxmox.TaskStatus, error) {
|
|
return proxmox.TaskStatus{}, nil
|
|
}
|
|
|
|
func TestR1192_EnsureGuestSwapRaisesEveryLowSwapToItsRAM(t *testing.T) {
|
|
f := &fakeSwapOps{cfg: map[int]proxmox.GuestConfig{
|
|
9201: {Memory: 4096, Swap: 512}, // the measured box: raised
|
|
9202: {Memory: 2048, Swap: 4096}, // already higher: never lowered
|
|
9203: {Memory: 6144, Swap: 0}, // none: raised
|
|
9204: {Memory: 4096, Swap: 512}, // the write fails: logged, the others still done
|
|
}, failSet: map[int]bool{9204: true}}
|
|
n, err := EnsureGuestSwap(context.Background(), f, "felhom", slog.New(slog.NewTextHandler(io.Discard, nil)))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if n != 2 {
|
|
t.Fatalf("raised %d, want 2 (9201, 9203)", n)
|
|
}
|
|
if f.set[9201]["swap"] != "4096" || f.set[9203]["swap"] != "6144" {
|
|
t.Fatalf("writes = %v", f.set)
|
|
}
|
|
if _, touched := f.set[9202]; touched {
|
|
t.Fatal("a swap above the RAM was lowered")
|
|
}
|
|
if len(f.set[9201]) != 1 {
|
|
t.Fatalf("the swap raise wrote more than the swap: %v", f.set[9201])
|
|
}
|
|
}
|