gates / gates (push) Successful in 32s
A count above 0 in a running container's current run fires the existing app_oom line (source cgroup/docker in the details), whatever Docker's OOMKilled says. Old agent: 404 back-off; no agent: Docker's flag only. Tests TestR528_* red first / red-proved. Commit only.
61 lines
2.1 KiB
Go
61 lines
2.1 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"io"
|
|
"log"
|
|
"net/http"
|
|
"testing"
|
|
"time"
|
|
|
|
"gitea.dooplex.hu/admin/felhom-controller/internal/agentapi"
|
|
)
|
|
|
|
// R-528: the scan's host counter source. An old agent (404) is asked again only after hostOOMRetryAfter; a transient
|
|
// failure is retried at the next scan; the counters pass through.
|
|
// RED-PROOF: drop the 404 back-off → an old agent is asked at every 30-s scan (calls 3, not 1) → FAILS.
|
|
func TestR528_HostOOMSourceBacksOffAnOldAgent(t *testing.T) {
|
|
clock := time.Date(2026, 10, 28, 23, 0, 0, 0, time.UTC)
|
|
calls := 0
|
|
var answer error = &agentapi.StatusError{Path: "/guest/oom-kills", Code: http.StatusNotFound}
|
|
src := hostOOMSource(func(context.Context) (agentapi.GuestOOMKillsState, error) {
|
|
calls++
|
|
if answer != nil {
|
|
return agentapi.GuestOOMKillsState{}, answer
|
|
}
|
|
return agentapi.GuestOOMKillsState{Available: true, Containers: map[string]int64{"id": 2}}, nil
|
|
}, log.New(io.Discard, "", 0), func() time.Time { return clock })
|
|
for i := 0; i < 3; i++ {
|
|
if got := src(); got != nil {
|
|
t.Fatalf("an old agent: %v", got)
|
|
}
|
|
clock = clock.Add(30 * time.Second)
|
|
}
|
|
if calls != 1 {
|
|
t.Fatalf("an old agent was asked %d times in 90 s, want 1", calls)
|
|
}
|
|
clock = clock.Add(hostOOMRetryAfter)
|
|
answer = nil
|
|
if got := src(); got["id"] != 2 || calls != 2 {
|
|
t.Fatalf("after the back-off, an upgraded agent: %v (calls %d)", got, calls)
|
|
}
|
|
answer = errors.New("timeout")
|
|
if got := src(); got != nil || calls != 3 {
|
|
t.Fatalf("a transient failure: %v", got)
|
|
}
|
|
answer = nil
|
|
if got := src(); got["id"] != 2 || calls != 4 {
|
|
t.Fatalf("the next scan after a transient failure must ask again: %v (calls %d)", got, calls)
|
|
}
|
|
}
|
|
|
|
// The source is wired into the stack manager at startup (the seam-built-but-never-wired class).
|
|
// RED-PROOF: delete the SetHostOOMKills line in main.go → FAILS.
|
|
func TestR528_HostOOMSourceIsWiredAtStartup(t *testing.T) {
|
|
lines, _, _ := slice4CallLines(t)
|
|
if len(lines["SetHostOOMKills"]) == 0 || len(lines["hostOOMSource"]) == 0 {
|
|
t.Fatal("the host OOM counter is never wired — the 30-s scan would read Docker's flag only (R-528)")
|
|
}
|
|
}
|