fix(host_agent): a node the operator keeps drained stays drained through its maintenance (#5777)
CI / lint (push) Successful in 2s
CI / integration (push) Successful in 1m23s
CI / unit (push) Successful in 1m28s
CI / publish (push) Successful in 17s

#5777 "Maintenance would make a node the operator keeps drained active after
its reboot". A swarm node already set to drain or pause, such as a manager
kept free of work, is no longer drained before its reboot or made active
after it. Its job only updates and reboots it, and back-in-service needs it
ready but not active.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-14 20:45:22 -04:00
co-authored by Claude Opus 5.5
parent c6b15b1cc6
commit 31a6cf09a9
4 changed files with 40 additions and 13 deletions
+5
View File
@@ -66,6 +66,11 @@ def test_a_host_is_back_once_posting_its_node_active_and_its_services_at_replica
services=[_svc("web", 1, 2), _svc("agent", 2, 3, mode="global")],
**kw) == "services below their replicas: web"
assert c.back_in_service(last_seen_at=NOW, node=_node(), services=[_svc("web", 2, 2)], **kw) is None
# A node the job didn't drain stays drained, as the operator keeps it.
assert c.back_in_service(last_seen_at=NOW, node=_node(availability="drain"),
services=[_svc("web", 1, 1)], expect_active=False, **kw) is None
assert "down" in c.back_in_service(last_seen_at=NOW, node=_node(status="down", availability="drain"),
services=[_svc("web", 1, 1)], expect_active=False, **kw)
def _run(**kw):
@@ -121,6 +121,16 @@ def test_a_manager_drains_itself_and_a_worker_is_drained_by_a_manager():
m.pick_drainer(nodes, managers, "Docker03", "h3", {"h3"})
def test_a_node_the_operator_keeps_drained_is_left_drained():
# A manager kept free of work (availability drain) is neither drained
# before nor made active after: the job only updates and reboots it.
nodes = {"util01": "c" * 25, "docker02": "a" * 25}
managers = {"h1": "c" * 25, "h2": "a" * 25}
assert m.pick_drainer(nodes, managers, "Util01", "h1", {"h1", "h2"}, "drain") == ("c" * 25, None)
assert m.pick_drainer(nodes, managers, "Util01", "h1", {"h1", "h2"}, "pause") == ("c" * 25, None)
assert m.pick_drainer(nodes, managers, "Util01", "h1", {"h1", "h2"}, "active") == ("c" * 25, "h1")
# ── the agent's side ─────────────────────────────────────────────────────────
def test_the_agent_leaves_each_handed_step_once(tmp_path):