mirror of
https://github.com/lukaszraczylo/traefik-llmgateway.git
synced 2026-10-17 05:37:43 +00:00
fix(target-health): scrub lastError URLs and fix unknown-state gaps
Combines three closely-coupled target_health.go fixes that all touch targetHealthTracker.record/stateForLocked in the same commit, since splitting them would leave an intermediate commit that fails to compile or a test asserting behaviour its own commit does not yet implement. F1: record gains a rawURL parameter and scrubs err's text through sanitizeProviderErr before storing lastError, so a target URL configured with an embedded credential can never leak through GET /admin/api/targets. The scrub runs before truncateRunes so a long embedded URL cannot survive truncation unscrubbed. Every probe, the per-server proxy hook (mcp_a2a.go), and both federation record sites (mcp_federation.go) now pass their target's configured URL through. F3: maybeSweepTargetHealth re-checks the elapsed interval after winning the single-flight CAS, closing a window where a caller's own stale interval read could still win the CAS once a separate, faster sweep had already started and finished within the same interval. F4: targetHealthEntry gains everOK, set on record's success branch. stateForLocked now reads a target that has never once succeeded as unknown rather than healthy, even below failureThreshold — the threshold still protects against a single transient first failure, it just does not promote a target this tracker has never seen answer to healthy. targetHealthSnapshot gains observed so the admin view can still tell apart never-observed (omit every other field) from observed-but-never-succeeded (keep them) despite both reading state unknown. The now-redundant lastOK field is removed. Also included: target_health_test.go's own test-hygiene fixes (F6) — an idempotent, t.Cleanup-registered unblock ahead of srv.Close so a failing assertion cannot leave a released server handler blocked until the test binary's own timeout, and an atomic hit counter in the disabled-sweep test — since they land in the same file as the tests above. Claude-Session: https://claude.ai/code/session_01EebV5jyWvYocByNQ3oss1w
This commit is contained in:
@@ -1252,11 +1252,20 @@ type adminTargetView struct {
|
||||
// adminTargetHealthView is one target's feat/target-health readout in
|
||||
// GET /admin/api/targets — the exact JSON contract the webui's MCP &
|
||||
// Agents panel codes against. State and ConsecutiveFailures are always
|
||||
// present; every other field is omitted while State is
|
||||
// targetHealthUnknown (this tracker has never observed the target),
|
||||
// since none of them carry a meaningful value yet. LatencyMs is a
|
||||
// pointer so a genuine 0ms observation still serializes as
|
||||
// "latencyMs":0 rather than being indistinguishable from "omitted".
|
||||
// present. State "unknown" (targetHealthUnknown) covers two different
|
||||
// situations (F4, feat/target-health review) — see that constant's own
|
||||
// doc comment — and this view tells them apart by whether the tracker
|
||||
// has ever observed the target at all, NOT by State alone:
|
||||
// - never observed: every other field is omitted, since none of them
|
||||
// carry a meaningful value yet.
|
||||
// - observed, but never once succeeded (still below
|
||||
// targetHealth.failureThreshold): every field below is still
|
||||
// present, exactly as for "healthy"/"unhealthy" — there IS a real
|
||||
// lastCheck/lastError/source/latencyMs to show, the tracker simply
|
||||
// has not seen this target succeed yet.
|
||||
//
|
||||
// LatencyMs is a pointer so a genuine 0ms observation still serializes
|
||||
// as "latencyMs":0 rather than being indistinguishable from "omitted".
|
||||
// Field order is fieldalignment-derived, the same convention
|
||||
// adminTargetView's own doc comment above explains.
|
||||
type adminTargetHealthView struct {
|
||||
@@ -1269,10 +1278,13 @@ type adminTargetHealthView struct {
|
||||
}
|
||||
|
||||
// targetHealthView converts one targetHealthTracker.snapshot result
|
||||
// (target_health.go) into its JSON view.
|
||||
// (target_health.go) into its JSON view. Whether to omit the non-always-
|
||||
// present fields is decided by snap.observed, NOT snap.state ==
|
||||
// targetHealthUnknown (adminTargetHealthView's own doc comment explains
|
||||
// why those are different questions since F4).
|
||||
func targetHealthView(snap targetHealthSnapshot) adminTargetHealthView {
|
||||
view := adminTargetHealthView{State: snap.state, ConsecutiveFailures: snap.consecutiveFailures}
|
||||
if snap.state == targetHealthUnknown {
|
||||
if !snap.observed {
|
||||
return view
|
||||
}
|
||||
view.LastCheck = snap.lastCheck.UTC().Format(time.RFC3339)
|
||||
|
||||
Reference in New Issue
Block a user