feat: per-Backend timeout server + Alerts-Deeplink + Retention + Cert-Prune — v1.3.4
- feat(backends): per-Backend `server_timeout_seconds` (nullable, Default 60s). Rendert `timeout server <N>s` im HAProxy-Backend-Block — für langsam antwortende Upstreams (KI-/Inferenz-Server mit gepufferter Antwort). Migration 0044 (+CHECK 1..86400), Model/Repo/Template/UI + Render-Test. - fix(ui): Dashboard-Alert-Karte verlinkt auf /alerts?tab=events; Alerts-Seite respektiert ?tab= (Deeplink landete bisher auf leerem Channels-Tab). - feat(scheduler): alert_events-Retention (90d) im täglichen Cleanup-Tick — Schutz vor unbounded growth der node-lokalen Health-Event-History. - fix(cluster): Cert-Sync prunt jetzt lokale .pem die der Primary nicht mehr hat (Waisen gelöschter Domains); schützt _default.pem + eigenen Node-Cert. - fix(security): x/text v0.37→v0.39 (GO-2026-5970, Infinite-Loop; via ACME+goose aktiv aufgerufen — govulncheck-Release-Gate). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -226,6 +226,11 @@ backend eg_backend_{{$b.ID}}
|
||||
{{- if $b.WebSocket}}
|
||||
timeout tunnel 1h
|
||||
{{- end}}
|
||||
{{- if $b.ServerTimeoutSeconds}}
|
||||
# Override des defaults-`timeout server 60s` für langsame Upstreams
|
||||
# (z. B. KI-Server mit gepufferter Antwort). Wert per Backend gepflegt.
|
||||
timeout server {{$b.ServerTimeoutSeconds}}s
|
||||
{{- end}}
|
||||
{{- if $b.HealthCheckPath}}
|
||||
option httpchk
|
||||
http-check send meth GET uri {{$b.HealthCheckPath}}
|
||||
|
||||
@@ -554,6 +554,44 @@ func TestRender_WebSocketEmitsTunnelTimeout(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRender_ServerTimeoutOverride(t *testing.T) {
|
||||
tmo := 300
|
||||
v := View{
|
||||
Backends: []BackendView{
|
||||
{
|
||||
Backend: models.Backend{ID: 11, Name: "ai", Scheme: "http",
|
||||
LBAlgorithm: "roundrobin", ServerTimeoutSeconds: &tmo, Active: true},
|
||||
Servers: []models.BackendServer{
|
||||
{BackendID: 11, Name: "ai-1", Address: "10.0.5.30", Port: 8000, Weight: 100, Active: true},
|
||||
},
|
||||
},
|
||||
{
|
||||
Backend: models.Backend{ID: 12, Name: "web", Scheme: "http",
|
||||
LBAlgorithm: "roundrobin", Active: true},
|
||||
Servers: []models.BackendServer{
|
||||
{BackendID: 12, Name: "web-1", Address: "10.0.5.31", Port: 8080, Weight: 100, Active: true},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
out := renderView(t, v)
|
||||
idxAI := strings.Index(out, "backend eg_backend_11")
|
||||
idxWeb := strings.Index(out, "backend eg_backend_12")
|
||||
if idxAI < 0 || idxWeb < 0 {
|
||||
t.Fatalf("backend sections missing in output:\n%s", out)
|
||||
}
|
||||
aiBlock := out[idxAI:idxWeb]
|
||||
webBlock := out[idxWeb:]
|
||||
// ai (nil-Override gesetzt) soll `timeout server 300s` bekommen …
|
||||
if !strings.Contains(aiBlock, "timeout server 300s") {
|
||||
t.Errorf("ai-Block sollte `timeout server 300s` enthalten:\n%s", aiBlock)
|
||||
}
|
||||
// … web (kein Override) soll KEINE eigene timeout-server-Zeile bekommen.
|
||||
if strings.Contains(webBlock, "timeout server") {
|
||||
t.Errorf("web-Block soll KEIN eigenes `timeout server` enthalten:\n%s", webBlock)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRender_MultiServerPool(t *testing.T) {
|
||||
v := View{
|
||||
Backends: []BackendView{
|
||||
|
||||
Reference in New Issue
Block a user