mirror of
https://github.com/spantaleev/matrix-docker-ansible-deploy.git
synced 2026-08-29 20:13:13 +00:00
The second scenario, chosen to be a bridge rather than another HTTP component: bridges are roughly 40 of the 70 roles here, and validating the shape on role two is cheaper than discovering it on role forty. It found two things the first role could not. Bridges read a whole family of variables from outside themselves - matrix_bridges_relay_enabled, _encryption_enabled, _encryption_default, _msc4190_enabled, _self_sign_enabled, plus matrix_admin - all defined in matrix-base. Since every bridge reads them, they belong in shared context rather than in each bridge's scenario: molecule-shared/playbook-context.yml now carries them along with the identity and path variables the first scenario had inline. Note it is loaded through vars_files, which outranks inventory group_vars, so it is authoritative; a scenario that wants to prove one of these reaches the rendered configuration should say so explicitly. Appservices call /whoami while starting and refuse to run if the id returned is not the bot user they were configured as - mautrix-whatsapp exits 17 with "Unexpected user ID in whoami call". The shared stub now takes the id it should claim to be. Expect every bridge and bot to need this. What the scenario proves: the bridge starts and stays up, opens its appservice port, its rendered config and appservice registration carry the scenario's tokens and bot user, it created its sqlite database under the role's data path as the role's uid, and it runs the version the role pins. It does not bridge anything and never will. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
178 lines
8.3 KiB
YAML
178 lines
8.3 KiB
YAML
# SPDX-FileCopyrightText: 2026 Slavi Pantaleev
|
|
#
|
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
|
|
---
|
|
- name: Verify matrix-alertmanager-receiver
|
|
hosts: all
|
|
become: true
|
|
vars_files:
|
|
- "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/../../../molecule-shared/vars.yml"
|
|
- "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/../../../molecule-shared/playbook-context.yml"
|
|
gather_facts: false
|
|
|
|
tasks:
|
|
# The version is read out of the role's own defaults rather than pinned in
|
|
# molecule.yml, so that the assertion further down compares the running
|
|
# image against what defaults/main.yml actually ships. Pinning it here
|
|
# would make that assertion compare the scenario with itself.
|
|
- name: Load the role's defaults under a separate name
|
|
ansible.builtin.include_vars:
|
|
file: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/defaults/main.yml"
|
|
name: matrix_alertmanager_receiver_role_defaults
|
|
|
|
- name: Wait for the matrix-alertmanager-receiver service to become active
|
|
ansible.builtin.systemd_service:
|
|
name: matrix-alertmanager-receiver.service
|
|
register: matrix_alertmanager_receiver_service
|
|
until: matrix_alertmanager_receiver_service.status.ActiveState == 'active'
|
|
retries: 30
|
|
delay: 5
|
|
failed_when: false
|
|
|
|
# `Restart=always` means a crash-looping container still reports `active`,
|
|
# so the restart counter is checked alongside it. Asserted as `is defined`
|
|
# too, because `| int` turns a missing property into 0 and would pass
|
|
# vacuously on a systemd that does not expose it.
|
|
- name: Assert the service is active and has not been restarting
|
|
ansible.builtin.assert:
|
|
that:
|
|
- matrix_alertmanager_receiver_service.status.ActiveState == 'active'
|
|
- matrix_alertmanager_receiver_service.status.NRestarts is defined
|
|
- matrix_alertmanager_receiver_service.status.NRestarts | int == 0
|
|
fail_msg: >-
|
|
matrix-alertmanager-receiver.service is
|
|
{{ matrix_alertmanager_receiver_service.status.ActiveState | default('unknown') }}
|
|
after {{ matrix_alertmanager_receiver_service.status.NRestarts | default('?') }}
|
|
automatic restart(s)
|
|
success_msg: "matrix-alertmanager-receiver.service is active and has not restarted"
|
|
|
|
# Probed from inside the container network rather than from the host: the
|
|
# role publishes no host port, exactly as it does in a real deployment,
|
|
# where Traefik reaches it over the network instead.
|
|
- name: Wait for matrix-alertmanager-receiver to answer on the port the role configured
|
|
ansible.builtin.command:
|
|
argv:
|
|
- docker
|
|
- run
|
|
- --rm
|
|
- --network={{ matrix_alertmanager_receiver_container_network }}
|
|
- "{{ molecule_shared_image_curl }}"
|
|
- --silent
|
|
- --show-error
|
|
- --write-out
|
|
- "\nHTTP_STATUS=%{http_code}"
|
|
- "http://matrix-alertmanager-receiver:{{ matrix_alertmanager_receiver_config_http_port }}{{ matrix_alertmanager_receiver_config_http_metrics_path }}"
|
|
register: matrix_alertmanager_receiver_metrics
|
|
changed_when: false
|
|
until: "'HTTP_STATUS=200' in matrix_alertmanager_receiver_metrics.stdout"
|
|
retries: 24
|
|
delay: 5
|
|
failed_when: false
|
|
|
|
# The port and the metrics path are both non-default in this scenario, so a
|
|
# 200 here is only reachable if the configuration the role rendered is what
|
|
# the process is actually running on.
|
|
- name: Assert the configured port and metrics path reached the process
|
|
ansible.builtin.assert:
|
|
that:
|
|
- "'HTTP_STATUS=200' in matrix_alertmanager_receiver_metrics.stdout"
|
|
fail_msg: >-
|
|
matrix-alertmanager-receiver did not serve metrics on port
|
|
{{ matrix_alertmanager_receiver_config_http_port }} at
|
|
{{ matrix_alertmanager_receiver_config_http_metrics_path }}
|
|
({{ matrix_alertmanager_receiver_metrics.stdout | default('no output') }})
|
|
success_msg: >-
|
|
matrix-alertmanager-receiver serves metrics on the configured port and path
|
|
|
|
- name: Assert the metrics endpoint is really Prometheus metrics
|
|
ansible.builtin.assert:
|
|
that:
|
|
- "'# HELP' in matrix_alertmanager_receiver_metrics.stdout"
|
|
fail_msg: >-
|
|
The metrics endpoint answered, but did not return Prometheus metrics
|
|
success_msg: "The metrics endpoint returns Prometheus metrics"
|
|
|
|
# A negative control for the assertion above: the role's own default metrics
|
|
# path must NOT answer, or a 200 on the configured path would prove nothing
|
|
# about the configuration having been applied.
|
|
- name: Ask for the role's default metrics path, which this scenario moved away from
|
|
ansible.builtin.command:
|
|
argv:
|
|
- docker
|
|
- run
|
|
- --rm
|
|
- --network={{ matrix_alertmanager_receiver_container_network }}
|
|
- "{{ molecule_shared_image_curl }}"
|
|
- --silent
|
|
- --output
|
|
- /dev/null
|
|
- --write-out
|
|
- "HTTP_STATUS=%{http_code}"
|
|
- "http://matrix-alertmanager-receiver:{{ matrix_alertmanager_receiver_config_http_port }}/metrics"
|
|
register: matrix_alertmanager_receiver_default_path
|
|
changed_when: false
|
|
failed_when: false
|
|
|
|
- name: Assert the default metrics path does not answer
|
|
ansible.builtin.assert:
|
|
that:
|
|
- "'HTTP_STATUS=200' not in matrix_alertmanager_receiver_default_path.stdout"
|
|
fail_msg: >-
|
|
/metrics answered as well, so serving on
|
|
{{ matrix_alertmanager_receiver_config_http_metrics_path }} does not
|
|
prove the role's configuration reached the process
|
|
({{ matrix_alertmanager_receiver_default_path.stdout | default('no output') }})
|
|
success_msg: "Only the configured metrics path answers"
|
|
|
|
- name: Read the configuration file the role rendered
|
|
ansible.builtin.slurp:
|
|
src: "{{ matrix_alertmanager_receiver_config_path }}/config.yml"
|
|
register: matrix_alertmanager_receiver_config_file
|
|
|
|
- name: Assert the rendered configuration carries this scenario's values
|
|
ansible.builtin.assert:
|
|
that:
|
|
- matrix_alertmanager_receiver_config_matrix_user_id in matrix_alertmanager_receiver_config_rendered
|
|
- matrix_alertmanager_receiver_config_matrix_access_token in matrix_alertmanager_receiver_config_rendered
|
|
- "'molecule-room' in matrix_alertmanager_receiver_config_rendered"
|
|
fail_msg: "The rendered configuration does not carry the scenario's Matrix settings"
|
|
success_msg: "The rendered configuration carries the scenario's Matrix settings"
|
|
vars:
|
|
matrix_alertmanager_receiver_config_rendered: "{{ matrix_alertmanager_receiver_config_file.content | b64decode }}"
|
|
|
|
- name: Assert the running container is the image the role pins
|
|
ansible.builtin.command:
|
|
argv:
|
|
- docker
|
|
- container
|
|
- inspect
|
|
- matrix-alertmanager-receiver
|
|
- --format
|
|
- "{{ '{{' }} .Config.Image {{ '}}' }}"
|
|
register: matrix_alertmanager_receiver_image
|
|
changed_when: false
|
|
|
|
- name: Assert the image carries the version defaults/main.yml pins
|
|
ansible.builtin.assert:
|
|
that:
|
|
- matrix_alertmanager_receiver_role_defaults.matrix_alertmanager_receiver_version in matrix_alertmanager_receiver_image.stdout
|
|
fail_msg: >-
|
|
The running container is {{ matrix_alertmanager_receiver_image.stdout }},
|
|
which does not carry the pinned version {{ matrix_alertmanager_receiver_role_defaults.matrix_alertmanager_receiver_version }}
|
|
success_msg: "The running container is the version defaults/main.yml pins"
|
|
|
|
- name: Read the labels the role rendered
|
|
ansible.builtin.slurp:
|
|
src: "{{ matrix_alertmanager_receiver_base_path }}/labels"
|
|
register: matrix_alertmanager_receiver_labels
|
|
|
|
- name: Assert no Traefik labels are emitted while Traefik support is disabled
|
|
ansible.builtin.assert:
|
|
that:
|
|
- "'traefik.' not in (matrix_alertmanager_receiver_labels.content | b64decode)"
|
|
fail_msg: >-
|
|
Traefik labels were emitted even though
|
|
matrix_alertmanager_receiver_container_labels_traefik_enabled is false
|
|
success_msg: "No Traefik labels are emitted while Traefik support is disabled"
|