3c174bc6f2
Operator ruling 2026-07-13: walk the root's backing device through /sys/block/<dev>/slaves recursively down to physical disks (dm AND md; topology, never VG names); those + any mounted-ESP holder are system; the all-system fail-safe returns to being the WALK-FAILURE error case only. SAFETY DIRECTION: a root-backing disk classified candidate is made impossible — per-branch conservatism (any unresolvable slave fails the WHOLE walk -> ok=false -> the unchanged all-system path). - physicalDisksOf/walkSlaves in role.go (symlink canon -> wholeDiskOf fast path -> recursive slaves walk; cycle/depth guard; non-/dev sources unwalkable) - HostReader.BlockSlaves(name) — the ONE new seam method; ProcHostReader reads /sys/block/<name>/slaves; all four test fakes mirror it - role_walk_test.go: signature table (root-backing disk ALWAYS system across legacy-LVM / md-raid / EFI+raw / EFI+LVM / nested dm-on-md — NEVER weaken) + dead-wizard-lives + dangling-slave fail-safe (real sysKnown=false path) + cycle + empty-slaves; red-proofs A/B/D run->fail->revert (recorded in REPORT) - §3 spike transcripts (drill legacy: dm-1->sda3->sda; felhom-pve: ESP+walk agree on sda -> byte-identical regression); caller audit: none relied on all-system as a feature - format/mkfs paths, data-bearing guards, wizard UI untouched
45 lines
1.7 KiB
Go
45 lines
1.7 KiB
Go
package localapi
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"gitea.dooplex.hu/admin/felhom-agent/internal/storage"
|
|
)
|
|
|
|
// uuidHostReader is a HostReader that resolves specific device fs-UUIDs (fakeHostReader always fails).
|
|
type uuidHostReader struct {
|
|
mounts []storage.Mount
|
|
uuids map[string]string
|
|
}
|
|
|
|
func (u uuidHostReader) Mounts() ([]storage.Mount, error) { return u.mounts, nil }
|
|
func (u uuidHostReader) ResolveUUID(dev string) (string, bool) {
|
|
v, ok := u.uuids[dev]
|
|
return v, ok
|
|
}
|
|
func (u uuidHostReader) DeviceExists(string) bool { return true }
|
|
func (u uuidHostReader) Rotational(string) (bool, bool) { return false, false }
|
|
func (u uuidHostReader) Removable(string) (bool, bool) { return false, false }
|
|
func (u uuidHostReader) BlockSlaves(string) ([]string, bool) { return nil, false }
|
|
|
|
// Impl-2b: a RAW enrolled drive is not a PVE storage (absent from Observe), so its durable-id must be
|
|
// resolved from the mount table's device fs-UUID. RED-PROOF: with the Observe-only resolution (before
|
|
// the host fallback) this returns "" → intent + guest-bind go unrecorded.
|
|
func TestDurableIDForMount_RawFallback(t *testing.T) {
|
|
s := &Server{
|
|
storage: fakeStorage{}, // Observe empty — the raw drive isn't a PVE storage
|
|
host: uuidHostReader{
|
|
mounts: []storage.Mount{{Device: "/dev/sdd", MountPoint: "/mnt/teszt"}},
|
|
uuids: map[string]string{"/dev/sdd": "RAW-UUID"},
|
|
},
|
|
}
|
|
if got := s.durableIDForMount(context.Background(), "/mnt/teszt"); got != "uuid:RAW-UUID" {
|
|
t.Fatalf("raw mount durable-id: got %q want uuid:RAW-UUID", got)
|
|
}
|
|
// An unknown mount still resolves to "" (no false positive).
|
|
if got := s.durableIDForMount(context.Background(), "/mnt/nope"); got != "" {
|
|
t.Fatalf("unknown mount: got %q want empty", got)
|
|
}
|
|
}
|