fix(docker): dedup widget counts across swarm managers; show failures not stopped
The running count was inflated: swarm-aware agents on every manager list cluster-wide tasks, so the same container (identical container_id) was reported once per manager and the dashboard widgets summed them. The earlier swarm dedup only covered the Swarm topology page — the widgets read raw rows. - _dedup_by_container_id(): collapse rows sharing a non-empty container_id (globally unique, so only true duplicates merge); first occurrence wins under the running-first ordering. Applied in both the containers and resources widgets before counting/limiting. Rows without a container_id (older agents) are kept as-is. - Replaced the static "stopped" tally — not actionable — with "failed (24h)": distinct containers with a die/oom event in the last 24h (DISTINCT so the managers' duplicate events don't double it), rendered red when non-zero. - widget.html empty-state now keys off total_count. - Regression test: same container_id from two managers counts once; id-less rows all kept. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016Jg27rgypiW2efULXJDtMC
This commit is contained in:
@@ -449,3 +449,23 @@ def test_retention_rollup_and_prune(app):
|
||||
assert events_left == 1 # only the in-window event survives
|
||||
assert counts["buckets_rolled"] == 1 and counts["raw_rows_rolled"] == 3
|
||||
assert counts["events_pruned"] == 1 and counts["rollup_pruned"] == 1
|
||||
|
||||
|
||||
def test_widget_dedup_collapses_cross_manager_duplicates():
|
||||
"""The same swarm task is reported by every manager (identical container_id);
|
||||
the dashboard widget must count it once. Older agents send no container_id,
|
||||
so those rows can't be matched and are all kept."""
|
||||
from types import SimpleNamespace
|
||||
from plugins.docker.routes import _dedup_by_container_id
|
||||
|
||||
def c(cid, name, status="running"):
|
||||
return SimpleNamespace(container_id=cid, name=name, status=status)
|
||||
|
||||
rows = [c("abc", "web@m1"), c("abc", "web@m2"), # same task, two managers
|
||||
c("def", "db"), c("", "noid-1"), c("", "noid-2")]
|
||||
out = _dedup_by_container_id(rows)
|
||||
|
||||
assert len(out) == 4 # one "abc" dropped
|
||||
abc = [r for r in out if r.container_id == "abc"]
|
||||
assert len(abc) == 1 and abc[0].name == "web@m1" # first occurrence wins
|
||||
assert sum(1 for r in out if r.container_id == "") == 2 # id-less rows all kept
|
||||
|
||||
Reference in New Issue
Block a user