Repository navigation
self-update fails at the backend systemd unit: the Play 1 include runs without become #14693
Description
Activity
Fixed and merged.
PR: #14694 —
fix(ansible): escalate in service_units.yml instead of relying on the caller (#14693)
Merge commit:0cd371047onDev_new_gui
CI: 10/10 required checks green, 12/12 python shards green at merge.What landed: task-level
become: trueon the 2 tasks in
roles/slm_manager/tasks/service_units.ymlthat write
/etc/systemd/system/…and run systemd enable/daemon-reload, plus all 6
handlers inroles/slm_manager/handlers/main.yml— 8/8 in scope, none
broadened to play or block level.Guard:
autobot-slm-backend/tests/test_service_units_self_sufficient_14693.py,
which walkspre_tasks/post_tasks/block/rescue/alwaysas well as
tasks, so a task added without escalation fails rather than passing on a
partial walk.Root cause confirmed statically in review:
update-all-nodes.yml:301-305
includes this task file viainclude_role … tasks_from: service_units.yml, and
neither that include nor its play (update-all-nodes.yml:222-223) sets
become. A barebecome:oninclude_roleis rejected by ansible — the
playbook's own comment at:1580-1582records this — so the call site could
not have fixed it. That is why the escalation belongs in the task file. The
failure was silent becausefailed_when: falseon the restart handler (kept,
for the legitimate "unit not yet deployed" case) masked the unprivileged
restart.Not fixed here, filed instead:
- slm_manager tasks still rely on caller escalation — the class #14693 fixed one instance of #14730 — the rest of the
slm_managerrole has the same shape: ~11 tasks
intasks/main.ymlandtasks/grafana.ymlwrite privileged paths without
their ownbecome:. They work only because both current callers escalate at
play level, which is exactly the state this file was in before this fix. - ansible.cfg puts become under [defaults], where ansible ignores it — the global escalation default has never applied #14699 —
ansible.cfgputsbecomeunder[defaults], where ansible
ignores it. Deliberately kept separate: moving it to
[privilege_escalation]would silently escalate every task in the tree,
which is a far broader change than making privileged tasks declare their own
requirement.
Closing on merge evidence. The host-behaviour half — that a self-update run now
gets past the backend systemd unit — is only observable on a real run and is
tracked by the deploy chain, not asserted here.- slm_manager tasks still rely on caller escalation — the class #14693 fixed one instance of #14730 — the rest of the
Recording the closure evidence, which the closing comment did not carry.
Delivered in PR #14694, merged to
Dev_new_gui. Verified against the base tree, not the PR:roles/slm_manager/tasks/service_units.yml— 2 tasks declaringbecome: trueroles/slm_manager/handlers/main.yml— 6 handlers declaringbecome: true
Host evidence that it actually fixed the failure
Before, on the test deployment:
TASK [slm_manager : SLM | Deploy backend systemd service] fatal: FAILED! => "Destination /etc/systemd/system not writable" PLAY RECAP <slm node> : ok=7 changed=4 failed=1After:
PLAY RECAP 00-SLM-Manager : ok=235 changed=47 unreachable=0 failed=0 skipped=49 localhost : ok=21 changed=3 unreachable=0 failed=0 skipped=2Zero
fatal:lines in the run. The decisive independent signal is the systemd unit itself:StartLimitIntervalUSecmoved from the stale10sto2min, the template's value. That render task had been failing since #14668, so the unit refresh #14624 and #14668 existed to perform had never once run on the self-update path — this is what made those fixes real rather than nominal.Fleet effect:
outdated_nodes2 → 0,stale_components5 → 0,self_update_incompletetrue → false.Follow-ups filed from this work and still open: #14699 (the config-level
becomedefault has never applied), #14700 (same shape, latent, in the admin-ui tasks).Merge commit for the record: PR #14694 squash-merged to
Dev_new_guias0cd371047ab0e58351ac63cb61a88fa74c3b26ab.- added 4 commits that reference this issue
on Sep 6, 2026
The self-update play fails one task on the SLM node, which stops it before the service restart. That is what leaves the update-all job wedged (#14683 is the wedge; this is why the play failed in the first place).
Evidence
From the self-update log on the test deployment:
Cause
A regression from #14668.
roles/slm_manager/tasks/service_units.ymlholds two tasks that both need root — a template into/etc/systemd/systemand asystemdenable withdaemon_reload— and neither declaresbecome.That worked for its original caller:
slm_manager/tasks/main.ymlincludes it from a context that escalates. It does not work for the caller added in #14668, the Play 1 include inplaybooks/update-all-nodes.yml:No play in
update-all-nodes.ymlsets a play-levelbecome— checked all five, every task declares its own. The task immediately above this include setsbecome: trueindividually. The include did not, so the template ran as the unprivileged user and could not write the unit.Fix
Declare
become: trueon the two tasks insideservice_units.ymlrather than on the one call site. Both genuinely require root regardless of who includes them, and a task file whose correctness depends on the caller supplying escalation is the thing that broke here — fixing only the call site leaves the next caller to rediscover it.Impact
The failing task aborts Play 1 before the SLM restarts, so: