mirror of
https://github.com/spantaleev/matrix-docker-ansible-deploy.git
synced 2026-09-12 10:53:14 +00:00
They were hard-wrapped at 80 characters, broke mid-parenthesis, and spent lines restating what the code below them does. Rewrapped at natural boundaries instead, with the narration dropped and only the reasons, gotchas and surprises kept. Section dividers stay - they delineate long plays rather than narrate them. Comments only; no scenario behaviour changes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SEH3vxYSQ5SV4N5z61eyGT
230 lines
13 KiB
YAML
230 lines
13 KiB
YAML
# SPDX-FileCopyrightText: 2026 Slavi Pantaleev
|
|
#
|
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
|
|
---
|
|
# Proves matrix-reminder-bot starts on the configuration the role rendered, logs in as the
|
|
# user the role gave it, opens the database at the path the role gave it, and is the version
|
|
# the role pins.
|
|
#
|
|
# The bot has no HTTP surface to probe, so the evidence is what it says about itself in the
|
|
# journal plus what it left on disk. It does NOT set real reminders. See docs/molecule-testing.md.
|
|
- name: Verify matrix-reminder-bot
|
|
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
|
|
|
|
vars:
|
|
matrix_bot_matrix_reminder_bot_molecule_user_id: "@{{ matrix_bot_matrix_reminder_bot_matrix_user_id_localpart }}:{{ matrix_domain }}"
|
|
matrix_bot_matrix_reminder_bot_molecule_container_user: "{{ matrix_user_uid }}:{{ matrix_user_gid }}"
|
|
|
|
tasks:
|
|
# Read from the role's own defaults rather than pinned in molecule.yml, so the version
|
|
# assertion compares the running image against what defaults/main.yml 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_bot_matrix_reminder_bot_role_defaults
|
|
|
|
- name: Wait for the matrix-reminder-bot service to become active
|
|
ansible.builtin.systemd_service:
|
|
name: matrix-bot-matrix-reminder-bot.service
|
|
register: matrix_bot_matrix_reminder_bot_service
|
|
until: matrix_bot_matrix_reminder_bot_service.status.ActiveState == 'active'
|
|
retries: 30
|
|
delay: 5
|
|
failed_when: false
|
|
|
|
# `Restart=always` means a bot crash-looping on unreadable config still reports `active`,
|
|
# so the restart counter is checked too. The config file is parsed before the bot's own
|
|
# catch-all retry loop starts, so anything wrong in what the role rendered shows up here
|
|
# as restarts. Asserted `is defined` because `| int` turns a missing property into 0.
|
|
- name: Assert the service is active and has not been restarting
|
|
ansible.builtin.assert:
|
|
that:
|
|
- matrix_bot_matrix_reminder_bot_service.status.ActiveState == 'active'
|
|
- matrix_bot_matrix_reminder_bot_service.status.NRestarts is defined
|
|
- matrix_bot_matrix_reminder_bot_service.status.NRestarts | int == 0
|
|
fail_msg: >-
|
|
matrix-bot-matrix-reminder-bot.service is
|
|
{{ matrix_bot_matrix_reminder_bot_service.status.ActiveState | default('unknown') }}
|
|
after {{ matrix_bot_matrix_reminder_bot_service.status.NRestarts | default('?') }}
|
|
automatic restart(s)
|
|
success_msg: "matrix-bot-matrix-reminder-bot.service is active and has not restarted"
|
|
|
|
# The unit runs `docker start --attach`, so the container's output is in the journal
|
|
# despite `--log-driver=none`. It is the only thing this bot reports about itself.
|
|
#
|
|
# Filtered rather than tailed: startup lines are the OLDEST in the journal, so a
|
|
# `--lines=N` tail loses them behind anything logged later, and reading it whole pulls
|
|
# unbounded text into a variable. The filter keeps the failure line too, so the
|
|
# "did not fail to log in" assertion below still has something to see.
|
|
- name: Wait for the bot to report that it finished starting up
|
|
ansible.builtin.shell:
|
|
cmd: >-
|
|
set -o pipefail;
|
|
journalctl --unit=matrix-bot-matrix-reminder-bot.service --no-pager --output=cat --lines=all
|
|
| grep -E 'Logged in as|Startup complete|Failed to login|Database initialization' | head -n 50 || true
|
|
executable: /bin/bash
|
|
register: matrix_bot_matrix_reminder_bot_journal
|
|
changed_when: false
|
|
until: "'Startup complete' in matrix_bot_matrix_reminder_bot_journal.stdout"
|
|
retries: 24
|
|
delay: 5
|
|
failed_when: false
|
|
|
|
# "Logged in as ..." is only reached once the login call returned something other than a
|
|
# LoginError, so this covers the whole chain at once: homeserver URL, user ID and password
|
|
# were all good enough for a real login round-trip.
|
|
- name: Assert the bot logged in as the user the role configured
|
|
ansible.builtin.assert:
|
|
that:
|
|
- "'Startup complete' in matrix_bot_matrix_reminder_bot_journal.stdout"
|
|
- "'Logged in as ' + matrix_bot_matrix_reminder_bot_molecule_user_id in matrix_bot_matrix_reminder_bot_journal.stdout"
|
|
- "'Failed to login' not in matrix_bot_matrix_reminder_bot_journal.stdout"
|
|
fail_msg: >-
|
|
The bot did not log in as {{ matrix_bot_matrix_reminder_bot_molecule_user_id }}
|
|
and reach startup
|
|
success_msg: >-
|
|
The bot logged in as {{ matrix_bot_matrix_reminder_bot_molecule_user_id }} and finished starting up
|
|
|
|
# The role picks the storage engine by building the connection string the bot parses,
|
|
# and the bot names the type it settled on once the database is open.
|
|
- name: Assert the bot opened the database engine the role selected
|
|
ansible.builtin.assert:
|
|
that:
|
|
- "\"Database initialization of type 'sqlite' complete\" in matrix_bot_matrix_reminder_bot_journal.stdout"
|
|
fail_msg: "The bot did not report a completed SQLite database initialization"
|
|
success_msg: "The bot initialized the SQLite database the role pointed it at"
|
|
|
|
- name: Read the configuration file the role rendered
|
|
ansible.builtin.slurp:
|
|
src: "{{ matrix_bot_matrix_reminder_bot_config_path }}/config.yaml"
|
|
register: matrix_bot_matrix_reminder_bot_config_file
|
|
|
|
# Every one differs from both the role's defaults and the bot's own fallbacks, so their
|
|
# presence means the role rendered this file rather than coinciding with it.
|
|
- name: Assert the rendered configuration carries this scenario's values
|
|
ansible.builtin.assert:
|
|
that:
|
|
- matrix_bot_matrix_reminder_bot_molecule_user_id in matrix_bot_matrix_reminder_bot_config_rendered
|
|
- matrix_bot_matrix_reminder_bot_matrix_user_password in matrix_bot_matrix_reminder_bot_config_rendered
|
|
- matrix_bot_matrix_reminder_bot_matrix_homeserver_url in matrix_bot_matrix_reminder_bot_config_rendered
|
|
- matrix_bot_matrix_reminder_bot_reminders_timezone in matrix_bot_matrix_reminder_bot_config_rendered
|
|
- matrix_bot_matrix_reminder_bot_command_prefix in matrix_bot_matrix_reminder_bot_config_rendered
|
|
- "'sqlite://' + matrix_bot_matrix_reminder_bot_sqlite_database_path_in_container in matrix_bot_matrix_reminder_bot_config_rendered"
|
|
- "'@molecule-allowed:molecule.local' in matrix_bot_matrix_reminder_bot_config_rendered"
|
|
- "'.*:blocked.molecule.local' in matrix_bot_matrix_reminder_bot_config_rendered"
|
|
fail_msg: "The rendered configuration does not carry the scenario's settings"
|
|
success_msg: "The rendered configuration carries the scenario's settings"
|
|
vars:
|
|
matrix_bot_matrix_reminder_bot_config_rendered: "{{ matrix_bot_matrix_reminder_bot_config_file.content | b64decode }}"
|
|
|
|
# `device_name` is hardcoded in the role's template, so this value can only be here if
|
|
# `..._configuration_extension_yaml` was merged over it rather than ignored.
|
|
- name: Assert the configuration extension was merged over the template
|
|
ansible.builtin.assert:
|
|
that:
|
|
- "'device_name: Molecule Reminder Bot' in matrix_bot_matrix_reminder_bot_config_rendered"
|
|
- "'device_name: Reminder Bot' not in matrix_bot_matrix_reminder_bot_config_rendered"
|
|
fail_msg: >-
|
|
The configuration extension did not override the device name the
|
|
role's template hardcodes
|
|
success_msg: "The configuration extension was merged over the role's template"
|
|
vars:
|
|
matrix_bot_matrix_reminder_bot_config_rendered: "{{ matrix_bot_matrix_reminder_bot_config_file.content | b64decode }}"
|
|
|
|
# With no HTTP surface, where the database landed is the evidence that the storage
|
|
# configuration reached the running process and not merely the file on disk.
|
|
- name: Stat the database at the path the scenario configured
|
|
ansible.builtin.stat:
|
|
path: "{{ matrix_bot_matrix_reminder_bot_sqlite_database_path_local }}"
|
|
register: matrix_bot_matrix_reminder_bot_database
|
|
|
|
- name: Assert the database landed under the role's data path, owned by the role's user
|
|
ansible.builtin.assert:
|
|
that:
|
|
- matrix_bot_matrix_reminder_bot_database.stat.exists
|
|
- matrix_bot_matrix_reminder_bot_database.stat.uid == matrix_user_uid
|
|
- matrix_bot_matrix_reminder_bot_database.stat.gid == matrix_user_gid
|
|
fail_msg: >-
|
|
{{ matrix_bot_matrix_reminder_bot_sqlite_database_path_local }} is missing or is not
|
|
owned by {{ matrix_user_uid }}:{{ matrix_user_gid }}
|
|
success_msg: >-
|
|
The database is at the configured path, owned by {{ matrix_user_uid }}:{{ matrix_user_gid }}
|
|
|
|
# Negative control for the assertion above: the role's own default database name must NOT
|
|
# appear, or a file at the configured path would prove nothing.
|
|
- name: Stat the database name the role would have used by default
|
|
ansible.builtin.stat:
|
|
path: "{{ matrix_bot_matrix_reminder_bot_data_path }}/bot.db"
|
|
register: matrix_bot_matrix_reminder_bot_default_database
|
|
|
|
- name: Assert the role's default database name was not used
|
|
ansible.builtin.assert:
|
|
that:
|
|
- not matrix_bot_matrix_reminder_bot_default_database.stat.exists
|
|
fail_msg: >-
|
|
{{ matrix_bot_matrix_reminder_bot_data_path }}/bot.db exists as well, so the
|
|
database at the configured path does not prove the role's storage
|
|
configuration reached the bot
|
|
success_msg: "Only the configured database path was used"
|
|
|
|
# matrix-nio writes its encryption store here once login succeeds, so a populated directory
|
|
# means the bot could use the store path the role created inside a read-only container.
|
|
- name: List the encryption store the role created
|
|
ansible.builtin.find:
|
|
paths: "{{ matrix_bot_matrix_reminder_bot_data_store_path }}"
|
|
file_type: file
|
|
register: matrix_bot_matrix_reminder_bot_store_files
|
|
|
|
- name: Assert the bot wrote its encryption store where the role put it
|
|
ansible.builtin.assert:
|
|
that:
|
|
- matrix_bot_matrix_reminder_bot_store_files.matched | int > 0
|
|
fail_msg: >-
|
|
{{ matrix_bot_matrix_reminder_bot_data_store_path }} is empty, so the bot never
|
|
got far enough to open its encryption store
|
|
success_msg: "The bot wrote its encryption store under the role's data path"
|
|
|
|
- name: Read the running container's user and environment
|
|
ansible.builtin.command:
|
|
argv:
|
|
- docker
|
|
- container
|
|
- inspect
|
|
- matrix-bot-matrix-reminder-bot
|
|
- --format
|
|
- "{{ '{{' }} .Config.User {{ '}}' }} {{ '{{' }} json .Config.Env {{ '}}' }} {{ '{{' }} .Config.Image {{ '}}' }}"
|
|
register: matrix_bot_matrix_reminder_bot_container
|
|
changed_when: false
|
|
|
|
# The timezone reaches the container twice, through the config file checked above and
|
|
# through TZ on the unit. The uid/gid come from the playbook context, not from the image.
|
|
- name: Assert the container runs as the role's user with the configured timezone
|
|
ansible.builtin.assert:
|
|
that:
|
|
- "'\"TZ=' + matrix_bot_matrix_reminder_bot_reminders_timezone + '\"' in matrix_bot_matrix_reminder_bot_container.stdout"
|
|
- matrix_bot_matrix_reminder_bot_molecule_container_user in matrix_bot_matrix_reminder_bot_container.stdout
|
|
fail_msg: >-
|
|
The container does not run as {{ matrix_user_uid }}:{{ matrix_user_gid }} with
|
|
TZ={{ matrix_bot_matrix_reminder_bot_reminders_timezone }}
|
|
({{ matrix_bot_matrix_reminder_bot_container.stdout }})
|
|
success_msg: >-
|
|
The container runs as {{ matrix_user_uid }}:{{ matrix_user_gid }} with
|
|
TZ={{ matrix_bot_matrix_reminder_bot_reminders_timezone }}
|
|
|
|
- name: Assert the running container is the version defaults/main.yml pins
|
|
ansible.builtin.assert:
|
|
that:
|
|
- matrix_bot_matrix_reminder_bot_role_defaults.matrix_bot_matrix_reminder_bot_version | string in matrix_bot_matrix_reminder_bot_container.stdout
|
|
fail_msg: >-
|
|
The running container is {{ matrix_bot_matrix_reminder_bot_container.stdout }}, which
|
|
does not carry the pinned version
|
|
{{ matrix_bot_matrix_reminder_bot_role_defaults.matrix_bot_matrix_reminder_bot_version }}
|
|
success_msg: "The running container is the version defaults/main.yml pins"
|