From ebe13bc3601437f621954d8ce43812ac80d52d5b Mon Sep 17 00:00:00 2001 From: Slavi Pantaleev Date: Thu, 27 Aug 2026 10:50:19 +0300 Subject: [PATCH] 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) --- .github/workflows/molecule.yml | 150 +++++++++++++++ .../molecule/default/converge.yml | 41 +++++ .../molecule/default/molecule.yml | 89 +++++++++ .../molecule/default/prepare.yml | 126 +++++++++++++ .../molecule/default/requirements.yml | 20 ++ .../molecule/default/requirements.yml.license | 3 + .../molecule/default/verify.yml | 174 ++++++++++++++++++ .../molecule/requirements.txt | 4 + .../molecule/requirements.txt.license | 3 + 9 files changed, 610 insertions(+) create mode 100644 .github/workflows/molecule.yml create mode 100644 roles/custom/matrix-alertmanager-receiver/molecule/default/converge.yml create mode 100644 roles/custom/matrix-alertmanager-receiver/molecule/default/molecule.yml create mode 100644 roles/custom/matrix-alertmanager-receiver/molecule/default/prepare.yml create mode 100644 roles/custom/matrix-alertmanager-receiver/molecule/default/requirements.yml create mode 100644 roles/custom/matrix-alertmanager-receiver/molecule/default/requirements.yml.license create mode 100644 roles/custom/matrix-alertmanager-receiver/molecule/default/verify.yml create mode 100644 roles/custom/matrix-alertmanager-receiver/molecule/requirements.txt create mode 100644 roles/custom/matrix-alertmanager-receiver/molecule/requirements.txt.license diff --git a/.github/workflows/molecule.yml b/.github/workflows/molecule.yml new file mode 100644 index 000000000..034feebbf --- /dev/null +++ b/.github/workflows/molecule.yml @@ -0,0 +1,150 @@ +# SPDX-FileCopyrightText: 2026 Slavi Pantaleev +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +--- +name: Molecule + +# Unlike the MASH role repositories, where one repository holds one role, every +# role here lives in the same repository. Running every scenario on every push +# would be unaffordable, so a first job works out which roles the push actually +# touched and the matrix is built from that. A push that changes documentation, +# or a role with no scenario yet, runs nothing at all. +on: # yamllint disable-line rule:truthy + push: + paths: + - "roles/custom/**" + - ".github/workflows/molecule.yml" + pull_request: + paths: + - "roles/custom/**" + - ".github/workflows/molecule.yml" + workflow_dispatch: + inputs: + role: + description: "Single role to test (directory name under roles/custom), or empty for all roles that have a scenario" + required: false + type: string + +permissions: + contents: read + +jobs: + detect: + name: Work out which roles to test + runs-on: ubuntu-latest + + # Same rule as the MASH repositories: a pull request from a branch of this + # repository would otherwise run everything twice, once for the push and + # once for the pull request. + if: >- + github.event_name != 'pull_request' + || github.event.pull_request.head.repo.full_name != github.repository + + outputs: + roles: ${{ steps.detect.outputs.roles }} + + steps: + - name: Check out + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + with: + fetch-depth: 0 + + - name: Detect roles with a Molecule scenario that this change touches + id: detect + env: + EVENT_NAME: ${{ github.event_name }} + BASE_SHA: ${{ github.event.pull_request.base.sha }} + BEFORE_SHA: ${{ github.event.before }} + INPUT_ROLE: ${{ inputs.role }} + run: | + set -euo pipefail + + have_scenario() { + [ -f "roles/custom/$1/molecule/default/molecule.yml" ] + } + + # An explicit request through workflow_dispatch wins over detection. + if [ -n "${INPUT_ROLE}" ]; then + if have_scenario "${INPUT_ROLE}"; then + printf 'roles=["%s"]\n' "${INPUT_ROLE}" >> "$GITHUB_OUTPUT" + else + echo "No scenario at roles/custom/${INPUT_ROLE}/molecule/default" >&2 + exit 1 + fi + exit 0 + fi + + # A hand-triggered run with no role named, and any run where the diff + # base is unusable (a new branch, a force push, the very first commit), + # falls back to every role that has a scenario. That is the safe + # direction to fail in: too much testing rather than too little. + base="" + case "${EVENT_NAME}" in + pull_request) base="${BASE_SHA}" ;; + push) + if [ -n "${BEFORE_SHA}" ] && [ "${BEFORE_SHA}" != "0000000000000000000000000000000000000000" ] \ + && git cat-file -e "${BEFORE_SHA}^{commit}" 2>/dev/null; then + base="${BEFORE_SHA}" + fi + ;; + esac + + if [ -n "${base}" ]; then + changed="$(git diff --name-only "${base}" HEAD -- 'roles/custom/*' || true)" + candidates="$(printf '%s\n' "${changed}" | awk -F/ 'NF>2 {print $3}' | sort -u)" + echo "Changed roles: ${candidates:-none}" + else + candidates="$(find roles/custom -mindepth 1 -maxdepth 1 -type d -printf '%f\n' | sort)" + echo "No usable diff base; considering every role" + fi + + selected="" + for role in ${candidates}; do + if have_scenario "${role}"; then + selected="${selected} ${role}" + fi + done + + if [ -z "${selected}" ]; then + echo "Nothing to test" + echo 'roles=[]' >> "$GITHUB_OUTPUT" + exit 0 + fi + + echo "Testing:${selected}" + json="$(printf '%s\n' ${selected} | jq -R . | jq -c -s .)" + echo "roles=${json}" >> "$GITHUB_OUTPUT" + + molecule: + name: "Molecule: ${{ matrix.role }}" + runs-on: ubuntu-latest + + needs: detect + if: needs.detect.outputs.roles != '[]' + + strategy: + matrix: + role: ${{ fromJson(needs.detect.outputs.roles) }} + fail-fast: false + + env: + MOLECULE_DISTRO: ubuntu2604 + PY_COLORS: "1" + ANSIBLE_FORCE_COLOR: "1" + + steps: + - name: Check out + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + + - name: Set up Python + uses: actions/setup-python@v6 + with: + python-version: "3.x" + + - name: Install test dependencies + run: python3 -m pip install -r roles/custom/${{ matrix.role }}/molecule/requirements.txt + + - name: Run Molecule + working-directory: roles/custom/${{ matrix.role }} + run: molecule test --scenario-name default diff --git a/roles/custom/matrix-alertmanager-receiver/molecule/default/converge.yml b/roles/custom/matrix-alertmanager-receiver/molecule/default/converge.yml new file mode 100644 index 000000000..6f31cdbb9 --- /dev/null +++ b/roles/custom/matrix-alertmanager-receiver/molecule/default/converge.yml @@ -0,0 +1,41 @@ +# SPDX-FileCopyrightText: 2026 Slavi Pantaleev +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +--- +# The devture base roles carry the variables this role reads +# (`devture_systemd_docker_base_*`, `devture_playbook_help_*`), the same way +# they do when the playbook runs. `matrix-base` is deliberately NOT included: +# it does far more than this role needs, and the two variables it would supply +# are set directly in molecule.yml instead. +- name: Include roles for matrix-alertmanager-receiver Molecule tests + hosts: all + become: true + gather_facts: true + tasks: + - name: Include roles for matrix-alertmanager-receiver Molecule tests + ansible.builtin.include_role: + name: "{{ role_name }}" + public: true + loop: + - com.devture.ansible.role.playbook_help + - com.devture.ansible.role.systemd_docker_base + - "custom/{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') | basename }}" + loop_control: + loop_var: role_name + +# The role installs the unit but does not start it - in the playbook that is +# `systemd_service_manager`'s job - so the scenario starts it here. +- name: Ensure matrix-alertmanager-receiver is started + hosts: all + become: true + gather_facts: false + tasks: + - name: Ensure systemd daemon is reloaded + ansible.builtin.systemd_service: + daemon_reload: true + + - name: Ensure matrix-alertmanager-receiver systemd service is started + ansible.builtin.systemd_service: + name: matrix-alertmanager-receiver.service + state: started diff --git a/roles/custom/matrix-alertmanager-receiver/molecule/default/molecule.yml b/roles/custom/matrix-alertmanager-receiver/molecule/default/molecule.yml new file mode 100644 index 000000000..f4cc17671 --- /dev/null +++ b/roles/custom/matrix-alertmanager-receiver/molecule/default/molecule.yml @@ -0,0 +1,89 @@ +# SPDX-FileCopyrightText: 2026 Slavi Pantaleev +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +--- +dependency: + name: galaxy + options: + requirements-file: requirements.yml + force: true +driver: + name: docker +platforms: + - name: matrix-alertmanager-receiver-${MOLECULE_DISTRO:-ubuntu2604}-default + image: "geerlingguy/docker-${MOLECULE_DISTRO:-ubuntu2604}-ansible:latest" + command: ${MOLECULE_DOCKER_COMMAND:-""} + volumes: + - /sys/fs/cgroup:/sys/fs/cgroup:rw + cgroupns_mode: host + privileged: true + pre_build_image: true +provisioner: + name: ansible + config_options: + defaults: + callback_result_format: yaml + inventory: + group_vars: + all: + # This role lives inside the playbook, so the variables the playbook + # would normally supply from `matrix-base` and from group_vars have to + # be provided here instead. These are the ones the role actually reads. + matrix_base_data_path: /matrix + matrix_domain: molecule.local + matrix_user_name: matrix + matrix_group_name: matrix + # Deliberately not 1000: the ubuntu2604 image already has a user there, + # so a distinct id proves the role used the one it was given. + matrix_user_uid: 1234 + matrix_user_gid: 1234 + + matrix_alertmanager_receiver_hostname: alertmanager-receiver.molecule.local + matrix_alertmanager_receiver_path_prefix: / + matrix_alertmanager_receiver_container_network: matrix-alertmanager-receiver-molecule + + # verify.yml runs as its own play, where the role's defaults are out + # of scope, so the paths it reads are pinned here as literals. They + # match what the role derives from matrix_base_data_path above. + matrix_alertmanager_receiver_base_path: /matrix/alertmanager-receiver + matrix_alertmanager_receiver_config_path: /matrix/alertmanager-receiver/config + + # Traefik is not deployed in this scenario, so the labels the role would + # render for it are switched off and their absence is asserted instead. + matrix_alertmanager_receiver_container_labels_traefik_enabled: false + + # Deliberately different from the role's own defaults (port 12345, + # metrics disabled, alerts under /alerts), so that `verify.yml` can tell + # what the role rendered apart from what the application would have done + # on its own. + matrix_alertmanager_receiver_config_http_port: 12399 + matrix_alertmanager_receiver_config_http_metrics_enabled: true + matrix_alertmanager_receiver_config_http_metrics_path: /molecule-metrics + matrix_alertmanager_receiver_config_http_alerts_path_prefix: /molecule-alerts + + # The homeserver IS reached at startup - the service fetches its joined + # rooms and exits 1 if that fails - so prepare.yml stands up a stub for it + matrix_alertmanager_receiver_config_matrix_homeserver_url: http://matrix.molecule.local:8008 + matrix_alertmanager_receiver_config_matrix_user_id: "@alertmanager:molecule.local" + matrix_alertmanager_receiver_config_matrix_access_token: molecule_access_token_4f2a91 + matrix_alertmanager_receiver_config_matrix_room_mapping: + molecule-room: "!molecule-room-id:molecule.local" + env: + # Workaround for https://github.com/ansible/molecule/issues/4391 + ANSIBLE_ROLES_PATH: ${MOLECULE_PROJECT_DIRECTORY}/../..:/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles:~/.ansible/roles +scenario: + test_sequence: + - dependency + - cleanup + - destroy + - syntax + - create + - prepare + - converge + - idempotence + - verify + - cleanup + - destroy +verifier: + name: ansible diff --git a/roles/custom/matrix-alertmanager-receiver/molecule/default/prepare.yml b/roles/custom/matrix-alertmanager-receiver/molecule/default/prepare.yml new file mode 100644 index 000000000..6d749fac1 --- /dev/null +++ b/roles/custom/matrix-alertmanager-receiver/molecule/default/prepare.yml @@ -0,0 +1,126 @@ +# SPDX-FileCopyrightText: 2026 Slavi Pantaleev +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +--- +- name: Prepare matrix-alertmanager-receiver Molecule tests + hosts: all + become: true + gather_facts: true + tasks: + - name: Ensure apt cache is updated + ansible.builtin.apt: + update_cache: true + cache_valid_time: 600 + when: ansible_os_family == 'Debian' + + - name: Ensure required packages are installed + ansible.builtin.package: + name: + - python3-requests + - fuse-overlayfs + state: present + + - name: Ensure Docker is installed + ansible.builtin.include_role: + name: ansible-role-docker + vars: + docker_daemon_options: + storage-driver: fuse-overlayfs + + # The role's file tasks set owner/group by name, and Ansible resolves those + # through the passwd database - so they have to exist before it runs. In a + # real deployment `matrix-base` creates them. + - name: Ensure the matrix group exists + ansible.builtin.group: + name: "{{ matrix_group_name }}" + gid: "{{ matrix_user_gid }}" + state: present + + - name: Ensure the matrix user exists + ansible.builtin.user: + name: "{{ matrix_user_name }}" + uid: "{{ matrix_user_uid }}" + group: "{{ matrix_group_name }}" + create_home: false + system: true + state: present + + - name: Ensure the base data path exists + ansible.builtin.file: + path: "{{ matrix_base_data_path }}" + state: directory + owner: "{{ matrix_user_name }}" + group: "{{ matrix_group_name }}" + mode: "0750" + + - name: Ensure the container network the role attaches to exists + ansible.builtin.command: + argv: + - docker + - network + - create + - "{{ matrix_alertmanager_receiver_container_network }}" + register: matrix_alertmanager_receiver_molecule_network + changed_when: matrix_alertmanager_receiver_molecule_network.rc == 0 + failed_when: + - matrix_alertmanager_receiver_molecule_network.rc != 0 + - "'already exists' not in matrix_alertmanager_receiver_molecule_network.stderr" + + # matrix-alertmanager-receiver 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 a homeserver has to exist for the service + # to come up at all. A stub is enough: the scenario is testing this role, + # not Synapse, and it keeps the run offline and fast. + - name: Ensure the Matrix homeserver stub script exists + ansible.builtin.copy: + dest: /root/matrix-homeserver-stub.py + mode: "0755" + content: | + import json + from http.server import BaseHTTPRequestHandler, HTTPServer + + ROOMS = {"joined_rooms": ["{{ matrix_alertmanager_receiver_config_matrix_room_mapping['molecule-room'] }}"]} + + class Handler(BaseHTTPRequestHandler): + def _send(self, payload): + body = json.dumps(payload).encode() + self.send_response(200) + self.send_header("Content-Type", "application/json") + self.send_header("Content-Length", str(len(body))) + self.end_headers() + self.wfile.write(body) + + def do_GET(self): + if self.path.endswith("/joined_rooms"): + self._send(ROOMS) + else: + self._send({}) + + def do_POST(self): + self._send({"event_id": "$molecule-event-id"}) + + def log_message(self, *args): + pass + + HTTPServer(("0.0.0.0", 8008), Handler).serve_forever() + + - name: Ensure the Matrix homeserver stub is running on the role's network + ansible.builtin.command: + argv: + - docker + - run + - --detach + - --rm + - --name=matrix-homeserver-stub + - --network={{ matrix_alertmanager_receiver_container_network }} + - --network-alias=matrix.molecule.local + - --volume=/root/matrix-homeserver-stub.py:/stub.py:ro + - docker.io/library/python:3.13-alpine + - python3 + - /stub.py + register: matrix_alertmanager_receiver_molecule_stub + changed_when: matrix_alertmanager_receiver_molecule_stub.rc == 0 + failed_when: + - matrix_alertmanager_receiver_molecule_stub.rc != 0 + - "'already in use' not in matrix_alertmanager_receiver_molecule_stub.stderr" diff --git a/roles/custom/matrix-alertmanager-receiver/molecule/default/requirements.yml b/roles/custom/matrix-alertmanager-receiver/molecule/default/requirements.yml new file mode 100644 index 000000000..013cbca29 --- /dev/null +++ b/roles/custom/matrix-alertmanager-receiver/molecule/default/requirements.yml @@ -0,0 +1,20 @@ +--- +roles: + - name: ansible-role-docker + src: https://github.com/geerlingguy/ansible-role-docker + scm: git + version: 8.0.0 + + - name: com.devture.ansible.role.playbook_help + src: https://github.com/devture/com.devture.ansible.role.playbook_help + scm: git + version: main + + - name: com.devture.ansible.role.systemd_docker_base + src: https://github.com/devture/com.devture.ansible.role.systemd_docker_base + scm: git + version: v1.5.0-0 + +collections: + - name: community.docker + version: 5.2.2 diff --git a/roles/custom/matrix-alertmanager-receiver/molecule/default/requirements.yml.license b/roles/custom/matrix-alertmanager-receiver/molecule/default/requirements.yml.license new file mode 100644 index 000000000..dbb307901 --- /dev/null +++ b/roles/custom/matrix-alertmanager-receiver/molecule/default/requirements.yml.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Slavi Pantaleev + +SPDX-License-Identifier: AGPL-3.0-or-later diff --git a/roles/custom/matrix-alertmanager-receiver/molecule/default/verify.yml b/roles/custom/matrix-alertmanager-receiver/molecule/default/verify.yml new file mode 100644 index 000000000..ce01ecb2c --- /dev/null +++ b/roles/custom/matrix-alertmanager-receiver/molecule/default/verify.yml @@ -0,0 +1,174 @@ +# 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" diff --git a/roles/custom/matrix-alertmanager-receiver/molecule/requirements.txt b/roles/custom/matrix-alertmanager-receiver/molecule/requirements.txt new file mode 100644 index 000000000..d1e469764 --- /dev/null +++ b/roles/custom/matrix-alertmanager-receiver/molecule/requirements.txt @@ -0,0 +1,4 @@ +ansible==14.1.0 +molecule==26.6.0 +molecule-plugins==26.7.8 +docker==7.2.0 diff --git a/roles/custom/matrix-alertmanager-receiver/molecule/requirements.txt.license b/roles/custom/matrix-alertmanager-receiver/molecule/requirements.txt.license new file mode 100644 index 000000000..dbb307901 --- /dev/null +++ b/roles/custom/matrix-alertmanager-receiver/molecule/requirements.txt.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Slavi Pantaleev + +SPDX-License-Identifier: AGPL-3.0-or-later