multica_agent_host: daemon lifecycle via systemd; fix login guard (WDWL-22)
Found provisioning spaceship-earth: - Login was not idempotent: `multica auth status` prints to stderr (0.6.5), and the guard read stdout only, so every run re-logged in and restarted the daemon. The guard now reads both streams; the status read is no_log because it echoes a token prefix. - `multica daemon start` / `daemon restart` fork the daemon inside the caller's session (Ansible's SSH session scope), outside the systemd user unit, which stays inactive: the same orphan seen on haunted-mansion on 2026-10-21. Start/restart now go through `systemctl --user`, after a daemon-reload when autostart was (re)registered. A daemon found running outside its unit is stopped first. The unit name is read from `autostart status`, so --profile hosts resolve their own unit. spaceship-earth: second run changed=0, daemon in multica-daemon.service. Not yet run against haunted-mansion. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
61dba61025
commit
94c77daf13
4 files changed
+89
-10
No files matched your search
@@ -43,8 +43,61 @@
|
||||
- multica_autostart | bool
|
||||
- _multica_autostart_status.rc != 0
|
||||
or 'enabled' not in (_multica_autostart_status.stdout | default('') | lower)
|
||||
register: _multica_autostart_enable
|
||||
changed_when: true
|
||||
|
||||
# Lifecycle goes through systemd, never `multica daemon start|restart`. Those
|
||||
# fork the daemon inside the CALLER's session (here, Ansible's SSH session
|
||||
# scope), outside the unit's cgroup: the unit stays inactive, nothing
|
||||
# supervises the process, and the next `systemctl --user start` collides with
|
||||
# it on daemon.lock. Seen on haunted-mansion (2026-10-21) and spaceship-earth
|
||||
# (2026-10-22).
|
||||
#
|
||||
# The unit name comes from `autostart status` ("Location: .../<name>.service")
|
||||
# so a --profile host resolves its own unit.
|
||||
- name: Daemon | Re-read the autostart registration
|
||||
ansible.builtin.command:
|
||||
argv: "{{ _multica_cmd + ['daemon', 'autostart', 'status'] }}"
|
||||
become: true
|
||||
become_user: "{{ multica_user }}"
|
||||
environment: "{{ _multica_user_env }}"
|
||||
register: _multica_autostart_reread
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
check_mode: false
|
||||
when: _multica_autostart_enable is changed
|
||||
|
||||
- name: Daemon | Resolve the systemd user unit
|
||||
ansible.builtin.set_fact:
|
||||
_multica_unit: >-
|
||||
{{ ((_s.stdout | default('')) ~ '\n' ~ (_s.stderr | default('')))
|
||||
| regex_search('Location:\s*\S*/([^/\s]+\.service)', '\1')
|
||||
| default(['multica-daemon.service'], true) | first }}
|
||||
vars:
|
||||
_s: "{{ _multica_autostart_reread if _multica_autostart_enable is changed else _multica_autostart_status }}"
|
||||
|
||||
# `autostart enable` may rewrite the unit file while the user manager still
|
||||
# holds the old definition.
|
||||
- name: Daemon | Reload the user manager after a (re)registration
|
||||
ansible.builtin.command:
|
||||
argv: [systemctl, --user, daemon-reload]
|
||||
become: true
|
||||
become_user: "{{ multica_user }}"
|
||||
environment: "{{ _multica_user_env }}"
|
||||
when: _multica_autostart_enable is changed
|
||||
changed_when: true
|
||||
|
||||
- name: Daemon | Read the unit state
|
||||
ansible.builtin.command:
|
||||
argv: [systemctl, --user, is-active, "{{ _multica_unit }}"]
|
||||
become: true
|
||||
become_user: "{{ multica_user }}"
|
||||
environment: "{{ _multica_user_env }}"
|
||||
register: _multica_unit_state
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
check_mode: false
|
||||
|
||||
- name: Daemon | Read daemon status
|
||||
ansible.builtin.command:
|
||||
argv: "{{ _multica_cmd + ['daemon', 'status', '--output', 'json'] }}"
|
||||
@@ -56,15 +109,27 @@
|
||||
failed_when: false
|
||||
check_mode: false
|
||||
|
||||
- name: Daemon | Start it if it is not already running
|
||||
# Running, but not under the unit: an orphan. Stop it so the unit can own the
|
||||
# lock and the process.
|
||||
- name: Daemon | Stop a daemon running outside its unit
|
||||
ansible.builtin.command:
|
||||
argv: "{{ _multica_cmd + ['daemon', 'start'] }}"
|
||||
argv: "{{ _multica_cmd + ['daemon', 'stop'] }}"
|
||||
become: true
|
||||
become_user: "{{ multica_user }}"
|
||||
environment: "{{ _multica_user_env }}"
|
||||
when: >-
|
||||
_multica_daemon_status.rc != 0
|
||||
or ((_multica_daemon_status.stdout | from_json).status | default('')) != 'running'
|
||||
when:
|
||||
- (_multica_unit_state.stdout | trim) != 'active'
|
||||
- _multica_daemon_status.rc == 0
|
||||
- ((_multica_daemon_status.stdout | from_json).status | default('')) == 'running'
|
||||
changed_when: true
|
||||
|
||||
- name: Daemon | Start the unit if it is not active
|
||||
ansible.builtin.command:
|
||||
argv: [systemctl, --user, start, "{{ _multica_unit }}"]
|
||||
become: true
|
||||
become_user: "{{ multica_user }}"
|
||||
environment: "{{ _multica_user_env }}"
|
||||
when: (_multica_unit_state.stdout | trim) != 'active'
|
||||
changed_when: true
|
||||
|
||||
# Anything that changed the binary, the config or the credentials restarts the
|
||||
|
||||
Reference in new issue
Block a user