---
# The unit is RemainAfterExit=yes, so success is 'active'; 'inactive' must NOT count as
# terminal (it is also the queued-not-yet-started state behind After=docker.service, and
# it is where the unit STAYS when its docker.service dependency fails - hence the
# docker= probe, without which a dead inner daemon burns the whole wait budget).
# ActiveState matched via stdout_lines: 'ActiveState=active' is a substring of 'ActiveState=activating'.
- name: "π ansible-flavor: rerun a matrix-mdad bootstrap a prior deploy left failed"
ansible.builtin.shell:
cmd: >-
container exec {{ MATRIX_MDAD_CONTAINER_ADDRESS }}
sh -c 'systemctl is-failed --quiet matrix-mdad-bootstrap.service || exit 0;
systemctl reset-failed matrix-mdad-bootstrap.service;
systemctl start --no-block matrix-mdad-bootstrap.service;
echo restarted'
register: matrix_mdad_bootstrap_rerun
changed_when: "'restarted' in matrix_mdad_bootstrap_rerun.stdout"
- name: "β³ ansible-flavor: await matrix-mdad bootstrap oneshot (cold ~10-15min)"
ansible.builtin.shell:
cmd: >-
container exec {{ MATRIX_MDAD_CONTAINER_ADDRESS }}
sh -c 'systemctl show -p LoadState,ActiveState matrix-mdad-bootstrap.service;
printf "docker=%s\n" "$(systemctl is-failed docker.service)"'
register: matrix_mdad_bootstrap_state
retries: 360
delay: 5
until: >-
'docker=failed' in matrix_mdad_bootstrap_state.stdout_lines | default([])
or (
'LoadState=loaded' in matrix_mdad_bootstrap_state.stdout_lines | default([])
and (
'ActiveState=failed' in matrix_mdad_bootstrap_state.stdout_lines | default([])
or 'ActiveState=active' in matrix_mdad_bootstrap_state.stdout_lines | default([])
)
)
changed_when: false
ignore_errors: true
- name: "π ansible-flavor: probe Synapse client API on loopback"
uri_retry:
url: "http://127.0.0.1:{{ MATRIX_SYNAPSE_PORT }}/_matrix/client/versions"
status_code: 200
follow_redirects: none
register: matrix_synapse_loopback_probe
retries: 60
delay: 5
until: matrix_synapse_loopback_probe.status == 200
ignore_errors: true
when:
- matrix_mdad_bootstrap_state is succeeded
- "'ActiveState=failed' not in matrix_mdad_bootstrap_state.stdout_lines | default([])"
- "'docker=failed' not in matrix_mdad_bootstrap_state.stdout_lines | default([])"
- name: "π ansible-flavor: matrix-mdad container/systemd/bootstrap status on failure"
ansible.builtin.script:
cmd: "{{ role_path }}/files/flavor/ansible/diagnostics.sh"
environment:
MATRIX_CONTAINER: "{{ MATRIX_MDAD_CONTAINER }}"
register: matrix_mdad_diagnostics
changed_when: false
failed_when: false
when: >-
matrix_mdad_bootstrap_state is failed
or 'ActiveState=failed' in matrix_mdad_bootstrap_state.stdout_lines | default([])
or 'docker=failed' in matrix_mdad_bootstrap_state.stdout_lines | default([])
or (matrix_synapse_loopback_probe is not skipped and matrix_synapse_loopback_probe is failed)
- name: "π ansible-flavor: report the matrix-mdad diagnostics and fail"
when: matrix_mdad_diagnostics is not skipped
block:
- name: "πΎ ansible-flavor: dump matrix-mdad diagnostics"
ansible.builtin.debug:
var: matrix_mdad_diagnostics
- name: "π ansible-flavor: fail after diagnostics if Synapse never became reachable"
ansible.builtin.fail:
msg: >-
{% if 'docker=failed' in matrix_mdad_bootstrap_state.stdout_lines | default([]) -%}
the docker daemon inside matrix-mdad failed to start - see matrix_mdad_diagnostics above.
{%- elif 'ActiveState=failed' in matrix_mdad_bootstrap_state.stdout_lines | default([]) -%}
matrix-mdad bootstrap failed inside the container, in its galaxy install or its playbook - see matrix_mdad_diagnostics above.
{%- elif matrix_mdad_bootstrap_state is failed -%}
matrix-mdad bootstrap never reached a terminal state within the wait budget - see matrix_mdad_diagnostics above.
{%- else -%}
Synapse loopback probe never reached 200 - see matrix_mdad_diagnostics above.
{%- endif %}
- name: "π ansible-flavor: probe Element web client on host loopback"
uri_retry:
url: "http://127.0.0.1:{{ MATRIX_ELEMENT_PORT }}/"
status_code: 200
follow_redirects: none
register: matrix_element_loopback_probe
retries: 30
delay: 2
until: matrix_element_loopback_probe.status == 200
- name: "π ansible-flavor: converge the native Synapse admin account"
when: IS_STACK_HOST | bool
block:
- name: "π ansible-flavor: create native Synapse admin (DiD, idempotent shared-secret)"
ansible.builtin.shell:
cmd: >- # nocheck: raw-docker, container-exec-resolver, container-address-quote inner docker CLI talks to the DiD daemon inside matrix-mdad; no host docker involved; ansible flavor forces compose mode
container exec -e MATRIX_ADMINISTRATOR_PASSWORD {{ MATRIX_MDAD_CONTAINER_ADDRESS | quote }} sh -lc 'docker exec matrix-synapse register_new_matrix_user
-u "{{ MATRIX_ADMINISTRATOR_USERNAME }}"
-p "$MATRIX_ADMINISTRATOR_PASSWORD"
-a -c {{ lookup('volume', 'web-app-matrix', 'synapse_config').target }} http://localhost:8008'
environment:
MATRIX_ADMINISTRATOR_PASSWORD: "{{ MATRIX_ADMINISTRATOR_PASSWORD }}"
register: matrix_admin_create
changed_when: matrix_admin_create.rc == 0 and 'User ID already taken' not in (matrix_admin_create.stdout + matrix_admin_create.stderr)
failed_when: matrix_admin_create.rc != 0 and 'User ID already taken' not in (matrix_admin_create.stdout + matrix_admin_create.stderr)
no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"
- name: "π ansible-flavor: realign the admin password Synapse stored"
when: not matrix_admin_create.changed
no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"
block:
- name: "π ansible-flavor: hash the declared admin password"
ansible.builtin.shell:
cmd: >- # nocheck: raw-docker, container-exec-resolver inner docker CLI talks to the DiD daemon inside matrix-mdad; no host docker involved
container exec -i {{ MATRIX_MDAD_CONTAINER_ADDRESS }} docker exec -i matrix-synapse
hash_password -c {{ MATRIX_SYNAPSE_CONFIG_PATH_CONTAINER | quote }}
stdin: "{{ MATRIX_ADMINISTRATOR_PASSWORD }}"
executable: /bin/bash
register: matrix_mdad_password_hash
changed_when: false
- name: "π ansible-flavor: point the admin at the declared password"
ansible.builtin.shell:
cmd: >-
container exec -i -e PGPASSWORD
{{ lookup('database', application_id, 'address') }}
psql -v ON_ERROR_STOP=1 -U {{ MATRIX_POSTGRES_USER | quote }}
-d {{ MATRIX_POSTGRES_DATABASE | quote }}
-v hash={{ (matrix_mdad_password_hash.stdout_lines | last | trim) | quote }}
-v name={{ lookup('mxid', MATRIX_ADMINISTRATOR_USERNAME) | quote }}
-f -
stdin: "{{ lookup('file', 'sql/realign_user_password.sql') }}"
executable: /bin/bash
environment:
PGPASSWORD: "{{ MATRIX_POSTGRES_PASSWORD }}"
register: matrix_mdad_password_update
changed_when: "'UPDATE 1' in matrix_mdad_password_update.stdout"
failed_when: >-
matrix_mdad_password_update.rc != 0
or 'UPDATE 1' not in matrix_mdad_password_update.stdout
- name: "π ansible-flavor: align the Jitsi component accounts with their passwords"
when:
- IS_STACK_HOST | bool
- MATRIX_JITSI_INTERNAL | bool
ansible.builtin.shell:
cmd: >- # nocheck: raw-docker, container-exec-resolver, container-address-quote inner docker CLI talks to the DiD daemon inside matrix-mdad; Prosody keeps an account's first password, so a rotated secret needs the account recreated
container exec -i {{ MATRIX_MDAD_CONTAINER_ADDRESS | quote }} docker exec -i matrix-jitsi-prosody sh -c
'IFS= read -r p; prosodyctl --config /run/prosody/config/prosody.cfg.lua deluser "$1@auth.meet.jitsi"
&& prosodyctl --config /run/prosody/config/prosody.cfg.lua register "$1" auth.meet.jitsi "$p"'
_ {{ item.user | quote }}
stdin: "{{ item.password }}"
loop: "{{ MATRIX_JITSI_COMPONENT_ACCOUNTS }}"
loop_control:
label: "{{ item.user }}"
register: matrix_jitsi_component_accounts
retries: 12
delay: 5
until: matrix_jitsi_component_accounts.rc == 0
changed_when: true
no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"
- name: "π front-proxy: Element web client"
ansible.builtin.include_role:
name: sys-stk-front-proxy
vars:
domain: "{{ MATRIX_ELEMENT_DOMAIN }}"
http_port: "{{ MATRIX_ELEMENT_PORT }}"
proxy_internal_port: 8080
- name: "π front-proxy: Synapse client/federation"
ansible.builtin.include_role:
name: sys-stk-front-proxy
vars:
domain: "{{ MATRIX_SYNAPSE_DOMAIN }}"
http_port: "{{ MATRIX_SYNAPSE_PORT }}"
proxy_internal_port: "{{ MATRIX_SYNAPSE_CONTAINER_PORT }}"
- name: "π front-proxy: federation delegation"
ansible.builtin.include_tasks: "{{ role_path }}/tasks/utils/well_known.yml"
- name: "π front-proxy: Jitsi web"
ansible.builtin.include_role:
name: sys-stk-front-proxy
vars:
domain: "{{ MATRIX_JITSI_DOMAIN }}"
http_port: "{{ MATRIX_JITSI_PORT }}"
proxy_internal_port: "{{ MATRIX_JITSI_CONTAINER_PORT }}"
when: MATRIX_JITSI_INTERNAL | bool