fix(maintenance): a late return, once back, stays back, and every reader agrees (#6175)
18032a4 re-checked a "not back in service" job every minute with no end,
so any later swarm wobble on the host (radarr's failed task during the
downloads move, say) flipped maintenance_failed back to 1: five false
firings and ten emails for Docker02 overnight on 10-19. And the overview's
finding never learned the rule at all, so it still said Docker02's
maintenance failed three days on.
The job now keeps came_back_at, set the first time its host is found back.
maintenance.still_failed(job) is the one answer: the figure, the overview,
the cadence page's run rows and the host panel all ask it. A failed update
or a timed-out job still flags its host until a job succeeds.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -20,7 +20,8 @@ def _job(**kw):
|
||||
base = dict(id="job-1", host_id="h1", state="queued", state_at=T0, created_at=T0,
|
||||
reboot_policy="if_required", step_id=None, step_seen=False, node_id=None,
|
||||
drain_host_id=None, packages=None, reboot_needed=None, back_at=None,
|
||||
detail=None, finished_at=None, drained_at=None, docker_update=None, checked=None)
|
||||
detail=None, finished_at=None, drained_at=None, docker_update=None, checked=None,
|
||||
came_back_at=None)
|
||||
base.update(kw)
|
||||
return SimpleNamespace(**base)
|
||||
|
||||
@@ -269,3 +270,18 @@ def test_the_agent_reads_stewards_answer_when_asked(monkeypatch):
|
||||
answer: dict = {}
|
||||
assert a.post_payload("https://x", "tok", {}, answer) == (True, 200)
|
||||
assert answer["maintenance"] == [{"id": "job-1-reboot", "action": "reboot"}]
|
||||
|
||||
|
||||
def test_a_late_return_stops_flagging_its_host_once_it_came_back():
|
||||
"""#6175: one answer for the figure, the overview and the pages."""
|
||||
late = _job(state="failed", finished_at=T0,
|
||||
detail=f"{m.NOT_BACK} 30 min: services below their replicas: portal_app")
|
||||
assert m.still_failed(late) and m.summary(late) == "Failed"
|
||||
late.came_back_at = T0 + timedelta(minutes=45)
|
||||
assert not m.still_failed(late)
|
||||
assert m.summary(late) == "Back in service late, 2026-10-14 23:45Z"
|
||||
# An update that failed, or a job that timed out, flags its host whatever came back.
|
||||
for job in (_job(state="failed", detail="dpkg was interrupted", came_back_at=T0),
|
||||
_job(state="timed_out", detail="no answer within 60 min", came_back_at=T0)):
|
||||
assert m.still_failed(job)
|
||||
assert not m.still_failed(_job(state="done"))
|
||||
|
||||
@@ -34,7 +34,7 @@ def test_a_job_row_says_what_it_did_and_when_it_gives_up():
|
||||
reboot_policy="if_required", packages=12, reboot_needed=True, back_at=None,
|
||||
node_id="n1", drain_host_id="h2", detail=None, created_by=None, run_id="r1",
|
||||
created_at=NOW - timedelta(minutes=20), finished_at=None,
|
||||
drained_at=NOW - timedelta(minutes=11), docker_update=False,
|
||||
drained_at=NOW - timedelta(minutes=11), docker_update=False, came_back_at=None,
|
||||
checked={"pending": 12, "docker_packages": [], "containers": []})
|
||||
row = _job_row(job, {"h1": "web-1", "h2": "mgr-1"}, NOW)
|
||||
assert (row["host"], row["drained_by"], row["label"], row["active"]) == (
|
||||
|
||||
Reference in New Issue
Block a user