Files
matrix-docker-ansible-deploy/roles/custom/matrix-alertmanager-receiver/molecule/default/verify.yml
T
Slavi PantaleevandClaude Opus 5 ebe13bc360 Add a Molecule scenario for matrix-alertmanager-receiver
Not for merging as-is. One role's scenario plus the workflow that would
make per-role testing affordable here, so we can see how it behaves before
deciding whether to do the other 69.

Unlike the MASH repositories, one repository holds every role, so running
everything on every push is not an option. The workflow's first job works
out which roles a push touched and builds the matrix from that; a change to
docs, or to a role with no scenario yet, runs nothing.

Three things this role needed that a MASH role does not:

- The variables matrix-base would supply (matrix_base_data_path,
  matrix_domain, matrix_user_name, matrix_group_name and the uid/gid) have
  to be provided by the scenario, and the user and group have to exist
  before the role's file tasks run.
- The service contacts the homeserver while starting up - it fetches
  /_matrix/client/v3/joined_rooms to resolve its room mapping and exits 1
  if that fails - so prepare.yml stands up a stub homeserver. Most roles
  here are bridges and bots, so this is likely the rule rather than the
  exception.
- verify.yml runs as its own play, where role defaults are out of scope, so
  the paths it reads are pinned in molecule.yml. The version is deliberately
  NOT pinned: it is read from defaults/main.yml so the assertion compares
  the running image against what the role ships.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-27 18:02:53 +03:00

175 lines
8.1 KiB
YAML

# SPDX-FileCopyrightText: 2026 Slavi Pantaleev
#
# SPDX-License-Identifier: AGPL-3.0-or-later
---
- name: Verify matrix-alertmanager-receiver
hosts: all
become: true
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 }}
- docker.io/curlimages/curl:8.11.1
- --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 }}
- docker.io/curlimages/curl:8.11.1
- --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"