Files
llmgw/internal/agent/discovery/storage_test.go
T
Ilker ManapandClaude Opus 5 753f4e3265 Let a node say how much room it has left
Disk was the one resource nothing reported. `statfs` was already being called
on every model directory, but only to ask whether the filesystem was a network
one — the free space it returns in the same struct was thrown away. So the
only way to find out why a node was filling up was to ssh to it, which is what
this system exists to avoid.

Reported per filesystem rather than per configured directory. Free space
belongs to the disk, not the path: two model directories on one disk are one
pool of bytes, and counting them separately would let the control plane add
24 GB to 24 GB and place a 40 GB model on a node that has 24.

Shared filesystems are summed separately from local ones and left out of a
node's own total. Space on a filer belongs to everyone mounting it, so twenty
nodes would otherwise each report the same free terabyte, and a scheduler
would place twenty models into room for one.

Nothing reported means nothing is known, everywhere: an agent too old to send
storage and a node with no model directories both send nothing, and both must
read as unknown rather than as a full disk. `ctl nodes` and the dashboard show
a dash, the metric is absent rather than zero, and a missing directory — which
is what an unmounted filer looks like — is skipped rather than reported as
having no space.

Shown as free out of total, because the first is useless without the second:
8 GB free is comfortable on a 10 TB filer and nearly full on a 10 GB root
partition, and only the second stops a model being placed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-18 09:38:57 +02:00

64 lines
1.9 KiB
Go

package discovery
import (
"os"
"path/filepath"
"testing"
)
func TestTwoDirectoriesOnOneDiskAreOnePoolOfBytes(t *testing.T) {
// The reason this matters: free space belongs to the disk, not to the path.
// Counting two directories separately lets the control plane add 24 GB to
// 24 GB and place a 40 GB model on a node that has 24.
base := t.TempDir()
a := filepath.Join(base, "models-a")
b := filepath.Join(base, "models-b")
for _, d := range []string{a, b} {
if err := os.Mkdir(d, 0o755); err != nil {
t.Fatal(err)
}
}
got := ScanStorage([]string{a, b})
if len(got) != 1 {
t.Fatalf("two directories on one filesystem reported as %d pools: %+v", len(got), got)
}
if got[0].Path != a {
t.Errorf("kept %q rather than the first directory configured", got[0].Path)
}
}
func TestStorageReportsSomethingPlausible(t *testing.T) {
got := ScanStorage([]string{t.TempDir()})
if len(got) != 1 {
t.Fatalf("a real directory reported %d filesystems", len(got))
}
s := got[0]
if s.TotalBytes <= 0 {
t.Error("a filesystem with no size at all")
}
if s.FreeBytes < 0 || s.FreeBytes > s.TotalBytes {
t.Errorf("free %d of total %d", s.FreeBytes, s.TotalBytes)
}
}
func TestAMissingDirectoryIsNotAFullDisk(t *testing.T) {
// A mount that has not come back yet looks exactly like a missing
// directory. Reporting it as zero free bytes would take the node out of
// placement for a reason that is not true; reporting nothing says only
// that nothing is known, which is the honest answer.
got := ScanStorage([]string{filepath.Join(t.TempDir(), "never-mounted")})
if len(got) != 0 {
t.Errorf("a missing directory was reported as a filesystem: %+v", got)
}
}
func TestNoDirectoriesConstrainNothing(t *testing.T) {
if got := ScanStorage(nil); len(got) != 0 {
t.Errorf("scanning nothing found %+v", got)
}
if got := ScanStorage([]string{""}); len(got) != 0 {
t.Errorf("an empty path found %+v", got)
}
}