Przeglądaj źródła

Test matrix-bot-honoroit with Molecule

pull/5555/merge
Slavi Pantaleev 1 dzień temu
rodzic
commit
d9997550f9
6 zmienionych plików z 690 dodań i 0 usunięć
  1. +2
    -0
      .github/renovate.json
  2. +36
    -0
      roles/custom/matrix-bot-honoroit/molecule/default/converge.yml
  3. +122
    -0
      roles/custom/matrix-bot-honoroit/molecule/default/molecule.yml
  4. +88
    -0
      roles/custom/matrix-bot-honoroit/molecule/default/prepare.yml
  5. +1
    -0
      roles/custom/matrix-bot-honoroit/molecule/default/requirements.yml
  6. +441
    -0
      roles/custom/matrix-bot-honoroit/molecule/default/verify.yml

+ 2
- 0
.github/renovate.json Wyświetl plik

@@ -125,6 +125,7 @@
"roles/custom/matrix-authentication-service/defaults/main.yml",
"roles/custom/matrix-bot-baibot/defaults/main.yml",
"roles/custom/matrix-bot-draupnir/defaults/main.yml",
"roles/custom/matrix-bot-honoroit/defaults/main.yml",
"roles/custom/matrix-bot-maubot/defaults/main.yml",
"roles/custom/matrix-bot-matrix-reminder-bot/defaults/main.yml",
"roles/custom/matrix-bot-meowlnir/defaults/main.yml",
@@ -154,6 +155,7 @@
"description": "Automerge minor bumps of explicitly approved components with a weight-bearing Molecule scenario (via branch push - no PR). This list is intentionally narrower than the patch rule: the scenario must exercise enough real behavior to be a useful compatibility gate across a minor release. A failure surfaces as a PR instead. Keep every entry backed by a scenario and the patch rule - bin/check-molecule-automerge-list.py (minor rule) enforces that.",
"matchFileNames": [
"roles/custom/matrix-authentication-service/defaults/main.yml",
"roles/custom/matrix-bot-honoroit/defaults/main.yml",
"roles/custom/matrix-bot-meowlnir/defaults/main.yml",
"roles/custom/matrix-element-admin/defaults/main.yml",
"roles/custom/matrix-static-files/defaults/main.yml",


+ 36
- 0
roles/custom/matrix-bot-honoroit/molecule/default/converge.yml Wyświetl plik

@@ -0,0 +1,36 @@
# SPDX-FileCopyrightText: 2026 Slavi Pantaleev
#
# SPDX-License-Identifier: AGPL-3.0-or-later

---
- name: Include roles for matrix-bot-honoroit Molecule tests
hosts: all
become: true
vars_files:
- "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/../../../molecule-shared/playbook-context.yml"
gather_facts: true
tasks:
- name: Include roles for matrix-bot-honoroit 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

- name: Ensure matrix-bot-honoroit 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-bot-honoroit systemd service is started
ansible.builtin.systemd_service:
name: matrix-bot-honoroit.service
state: started

+ 122
- 0
roles/custom/matrix-bot-honoroit/molecule/default/molecule.yml Wyświetl plik

@@ -0,0 +1,122 @@
# 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-bot-honoroit-${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:
# setup.yml normally carries this play-wide switch.
run_setup: true

matrix_bot_honoroit_hostname: honoroit.molecule.local
matrix_bot_honoroit_path_prefix: /molecule-honoroit
matrix_bot_honoroit_metrics_path: /molecule-metrics
matrix_bot_honoroit_container_port: 18080
matrix_bot_honoroit_container_network: matrix-bot-honoroit-molecule
matrix_bot_honoroit_container_additional_networks:
- matrix-bot-honoroit-molecule-additional

# verify.yml is a separate play, where the role's defaults are out of scope.
matrix_bot_honoroit_base_path: /matrix/honoroit
matrix_bot_honoroit_config_path: /matrix/honoroit/config
matrix_bot_honoroit_data_path: /matrix/honoroit/data

# Traefik itself is not deployed, but the full metrics routing contract is rendered
# with non-default values and compared with the live container labels.
matrix_bot_honoroit_container_labels_traefik_enabled: true
matrix_bot_honoroit_container_labels_traefik_docker_network: "{{ matrix_bot_honoroit_container_network }}"
matrix_bot_honoroit_container_labels_traefik_priority: 710
matrix_bot_honoroit_container_labels_traefik_entrypoints: molecule-web
matrix_bot_honoroit_container_labels_traefik_tls: false
matrix_bot_honoroit_container_labels_traefik_additional_response_headers_custom:
X-Molecule-Honoroit: scenario
X-Robots-Tag: noindex
matrix_bot_honoroit_container_labels_additional_labels: |
molecule.honoroit.scenario=live-postgres-and-metrics

# Real deployments use Postgres whenever the playbook's Postgres is enabled. All
# identifiers differ from the role defaults, so a migrated schema proves the role
# constructed and passed the configured DSN.
matrix_bot_honoroit_database_engine: postgres
matrix_bot_honoroit_database_hostname: matrix-postgres-molecule
matrix_bot_honoroit_database_username: molecule_honoroit_user
matrix_bot_honoroit_database_password: molecule_honoroit_password_8c45d1
matrix_bot_honoroit_database_name: molecule_honoroit_database
matrix_bot_honoroit_database_sslmode: disable

# These differ from both role and upstream defaults. The shared homeserver stub is
# sufficient for password login, crypto initialization and an idle sync loop.
matrix_bot_honoroit_login: molecule-honoroit
matrix_bot_honoroit_password: molecule_matrix_password_74aa5e
matrix_bot_honoroit_homeserver: http://matrix.molecule.local:8008
matrix_bot_honoroit_roomid: "!molecule-operators:molecule.local"
matrix_bot_honoroit_prefix: "!molecule-help"
matrix_bot_honoroit_loglevel: DEBUG
matrix_bot_honoroit_cachesize: 317
matrix_bot_honoroit_no_encryption_warning: true
matrix_bot_honoroit_ignorenothread: true
matrix_bot_honoroit_ignoredrooms:
- "!molecule-ignored-one:molecule.local"
- "!molecule-ignored-two:molecule.local"
matrix_bot_honoroit_allowedusers:
- "@support-*:molecule.local"
- "@operator:molecule.local"
matrix_bot_honoroit_text_prefix_open: "[MOLECULE OPEN]"
matrix_bot_honoroit_text_prefix_done: "[MOLECULE DONE]"
matrix_bot_honoroit_text_greetings: Molecule helpdesk greeting
matrix_bot_honoroit_text_done: Molecule helpdesk completion

# Live metrics authentication forms the primary compatibility gate: correct
# credentials must work and both absent and wrong credentials must not.
matrix_bot_honoroit_auth_metrics_login: molecule-metrics-user
matrix_bot_honoroit_auth_metrics_password: molecule_metrics_password_c9ed32
# Keep the generic environment extension observable independently of the role's
# first-class port wiring.
matrix_bot_honoroit_environment_variables_extension: |
MOLECULE_HONOROIT_MARKER=role-rendered-environment

# Exercise arbitrary runtime arguments and a second private network without making
# anything reachable from the Molecule host.
matrix_bot_honoroit_container_extra_arguments:
- --pids-limit=73
- --tmpfs=/tmp:rw,noexec,nosuid,size=16m
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_HOME:-~/.ansible}/roles
scenario:
test_sequence:
- dependency
- cleanup
- destroy
- syntax
- create
- prepare
- converge
- idempotence
- verify
- cleanup
- destroy
verifier:
name: ansible

+ 88
- 0
roles/custom/matrix-bot-honoroit/molecule/default/prepare.yml Wyświetl plik

@@ -0,0 +1,88 @@
# SPDX-FileCopyrightText: 2026 Slavi Pantaleev
#
# SPDX-License-Identifier: AGPL-3.0-or-later

---
- name: Prepare matrix-bot-honoroit Molecule tests
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: true
tasks:
- name: Ensure apt cache is updated
ansible.builtin.apt:
update_cache: true
cache_valid_time: 600
when: ansible_facts['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

- 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 Honoroit's container networks exist
ansible.builtin.command:
argv:
- docker
- network
- create
- "{{ item }}"
loop: "{{ [matrix_bot_honoroit_container_network] + matrix_bot_honoroit_container_additional_networks }}"
register: matrix_bot_honoroit_molecule_networks
changed_when: matrix_bot_honoroit_molecule_networks.rc == 0
failed_when:
- matrix_bot_honoroit_molecule_networks.rc != 0
- "'already exists' not in matrix_bot_honoroit_molecule_networks.stderr"

- name: Ensure Postgres is running
ansible.builtin.include_tasks:
file: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/../../../molecule-shared/tasks/postgres.yml"
vars:
molecule_shared_postgres_network: "{{ matrix_bot_honoroit_container_network }}"
molecule_shared_postgres_database: "{{ matrix_bot_honoroit_database_name }}"
molecule_shared_postgres_username: "{{ matrix_bot_honoroit_database_username }}"
molecule_shared_postgres_password: "{{ matrix_bot_honoroit_database_password }}"

- name: Ensure the homeserver stub is running
ansible.builtin.include_tasks:
file: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/../../../molecule-shared/tasks/homeserver-stub.yml"
vars:
molecule_shared_stub_network: "{{ matrix_bot_honoroit_container_network }}"
molecule_shared_stub_server_name: molecule.local
molecule_shared_stub_user_id: "@{{ matrix_bot_honoroit_login }}:molecule.local"
molecule_shared_stub_joined_rooms:
- "{{ matrix_bot_honoroit_roomid }}"

+ 1
- 0
roles/custom/matrix-bot-honoroit/molecule/default/requirements.yml Wyświetl plik

@@ -0,0 +1 @@
../../../../../molecule-shared/requirements.yml

+ 441
- 0
roles/custom/matrix-bot-honoroit/molecule/default/verify.yml Wyświetl plik

@@ -0,0 +1,441 @@
# SPDX-FileCopyrightText: 2026 Slavi Pantaleev
#
# SPDX-License-Identifier: AGPL-3.0-or-later

---
- name: Verify matrix-bot-honoroit
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"
vars:
matrix_bot_honoroit_health_body: "{{ matrix_bot_honoroit_health.stdout_lines[:-1] | join('\n') }}"
matrix_bot_honoroit_metrics_body: "{{ matrix_bot_honoroit_metrics.stdout_lines[:-1] | join('\n') }}"
matrix_bot_honoroit_metrics_lines: "{{ matrix_bot_honoroit_metrics_body.splitlines() | select | list }}"
matrix_bot_honoroit_rendered_env_lines: >-
{{ (matrix_bot_honoroit_env_file.content | b64decode).splitlines()
| select('match', '^[A-Z0-9_]+=') | list }}
matrix_bot_honoroit_rendered_env: >-
{{ dict(matrix_bot_honoroit_rendered_env_lines | map('split', '=', 1)) }}
matrix_bot_honoroit_rendered_labels_lines: >-
{{ (matrix_bot_honoroit_labels_file.content | b64decode).splitlines()
| select('match', '^[A-Za-z0-9_.-]+=') | list }}
matrix_bot_honoroit_rendered_labels: >-
{{ dict(matrix_bot_honoroit_rendered_labels_lines | map('split', '=', 1)) }}
matrix_bot_honoroit_runtime: "{{ (matrix_bot_honoroit_container_inspect.stdout | from_json) | first }}"
matrix_bot_honoroit_runtime_env: "{{ dict(matrix_bot_honoroit_runtime.Config.Env | map('split', '=', 1)) }}"
matrix_bot_honoroit_data_mounts: >-
{{ matrix_bot_honoroit_runtime.Mounts | selectattr('Destination', 'equalto', '/data') | list }}
gather_facts: false
tasks:
- 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_honoroit_role_defaults

- name: Wait for the matrix-bot-honoroit service to become active
ansible.builtin.systemd_service:
name: matrix-bot-honoroit.service
register: matrix_bot_honoroit_service
until: matrix_bot_honoroit_service.status.ActiveState == 'active'
retries: 30
delay: 5
failed_when: false

- name: Assert the service is active and has not restarted
ansible.builtin.assert:
that:
- matrix_bot_honoroit_service.status.ActiveState == 'active'
- matrix_bot_honoroit_service.status.NRestarts is defined
- matrix_bot_honoroit_service.status.NRestarts | int == 0
fail_msg: >-
matrix-bot-honoroit.service is
{{ matrix_bot_honoroit_service.status.ActiveState | default('unknown') }} after
{{ matrix_bot_honoroit_service.status.NRestarts | default('?') }} restart(s)
success_msg: "matrix-bot-honoroit.service is active and has not restarted"

- name: Wait for Honoroit's health API
ansible.builtin.command:
argv:
- docker
- run
- --rm
- --network={{ matrix_bot_honoroit_container_network }}
- "{{ molecule_shared_image_curl }}"
- --silent
- --show-error
- --write-out
- "\nHTTP_STATUS=%{http_code}"
- "http://matrix-bot-honoroit:{{ matrix_bot_honoroit_container_port }}/_health"
register: matrix_bot_honoroit_health
changed_when: false
until: "'HTTP_STATUS=200' in matrix_bot_honoroit_health.stdout"
retries: 24
delay: 5
failed_when: false

- name: Assert the configured health API is live and structurally valid
ansible.builtin.assert:
that:
- matrix_bot_honoroit_health.rc == 0
- matrix_bot_honoroit_health.stdout_lines[-1] == 'HTTP_STATUS=200'
- "matrix_bot_honoroit_health_body | from_json == {'status': 'ok'}"
fail_msg: >-
Honoroit's health API did not answer with its expected JSON
({{ matrix_bot_honoroit_health.stdout | default('no output') }})
success_msg: "Honoroit's live health API answers with its structured status"

- name: Ask the metrics endpoint without credentials
ansible.builtin.command:
argv:
- docker
- run
- --rm
- --network={{ matrix_bot_honoroit_container_network }}
- "{{ molecule_shared_image_curl }}"
- --silent
- --output
- /dev/null
- --write-out
- "HTTP_STATUS=%{http_code}"
- "http://matrix-bot-honoroit:{{ matrix_bot_honoroit_container_port }}/metrics"
register: matrix_bot_honoroit_unauthenticated_metrics
changed_when: false
failed_when: false

- name: Assert the metrics endpoint requires authentication
ansible.builtin.assert:
that:
- matrix_bot_honoroit_unauthenticated_metrics.rc == 0
- matrix_bot_honoroit_unauthenticated_metrics.stdout == 'HTTP_STATUS=401'
fail_msg: "Honoroit's metrics endpoint did not reject an unauthenticated request"
success_msg: "Honoroit's metrics endpoint rejects unauthenticated requests"

- name: Ask the metrics endpoint with incorrect credentials
ansible.builtin.command:
argv:
- docker
- run
- --rm
- --network={{ matrix_bot_honoroit_container_network }}
- "{{ molecule_shared_image_curl }}"
- --silent
- --user
- molecule-metrics-user:incorrect-molecule-password
- --output
- /dev/null
- --write-out
- "HTTP_STATUS=%{http_code}"
- "http://matrix-bot-honoroit:{{ matrix_bot_honoroit_container_port }}/metrics"
register: matrix_bot_honoroit_wrong_metrics_credentials
changed_when: false
failed_when: false

- name: Assert the metrics endpoint rejects incorrect credentials
ansible.builtin.assert:
that:
- matrix_bot_honoroit_wrong_metrics_credentials.rc == 0
- matrix_bot_honoroit_wrong_metrics_credentials.stdout == 'HTTP_STATUS=401'
fail_msg: "Honoroit's metrics endpoint accepted incorrect credentials"
success_msg: "Honoroit's metrics endpoint rejects incorrect credentials"

- name: Ask the metrics endpoint with the configured credentials
ansible.builtin.command:
argv:
- docker
- run
- --rm
- --network={{ matrix_bot_honoroit_container_network }}
- "{{ molecule_shared_image_curl }}"
- --silent
- --show-error
- --user
- molecule-metrics-user:molecule_metrics_password_c9ed32
- --write-out
- "\nHTTP_STATUS=%{http_code}"
- "http://matrix-bot-honoroit:{{ matrix_bot_honoroit_container_port }}/metrics"
register: matrix_bot_honoroit_metrics
changed_when: false
failed_when: false

- name: Assert configured credentials unlock genuine Prometheus metrics
ansible.builtin.assert:
that:
- matrix_bot_honoroit_metrics.rc == 0
- matrix_bot_honoroit_metrics.stdout_lines[-1] == 'HTTP_STATUS=200'
- matrix_bot_honoroit_metrics_lines | select('match', '^honoroit_messages_operator [0-9]+$') | list | length == 1
- matrix_bot_honoroit_metrics_lines | select('match', '^honoroit_messages_total [0-9]+$') | list | length == 1
- matrix_bot_honoroit_metrics_lines | select('match', '^honoroit_request_done [0-9]+$') | list | length == 1
- matrix_bot_honoroit_metrics_lines | select('match', '^honoroit_request_new [0-9]+$') | list | length == 1
fail_msg: >-
Configured credentials did not unlock Honoroit's Prometheus metrics
({{ matrix_bot_honoroit_metrics.stdout | default('no output') }})
success_msg: "Configured credentials unlock Honoroit's Prometheus metrics"

- name: Read the environment file the role rendered
ansible.builtin.slurp:
src: "{{ matrix_bot_honoroit_config_path }}/env"
register: matrix_bot_honoroit_env_file

- name: Assert the parsed environment contains the Matrix and Postgres wiring
ansible.builtin.assert:
that:
- matrix_bot_honoroit_rendered_env.HONOROIT_LOGIN == matrix_bot_honoroit_login
- matrix_bot_honoroit_rendered_env.HONOROIT_PASSWORD == matrix_bot_honoroit_password
- matrix_bot_honoroit_rendered_env.HONOROIT_HOMESERVER == matrix_bot_honoroit_homeserver
- matrix_bot_honoroit_rendered_env.HONOROIT_ROOMID == matrix_bot_honoroit_roomid
- matrix_bot_honoroit_rendered_env.HONOROIT_DB_DIALECT == 'postgres'
- matrix_bot_honoroit_rendered_env.HONOROIT_DB_DSN == 'postgres://molecule_honoroit_user:molecule_honoroit_password_8c45d1@matrix-postgres-molecule:5432/molecule_honoroit_database?sslmode=disable'
fail_msg: "The parsed environment lost the scenario's Matrix or Postgres wiring"
success_msg: "The parsed environment carries the scenario's Matrix and Postgres wiring"

- name: Assert the parsed environment contains non-default bot behavior
ansible.builtin.assert:
that:
- matrix_bot_honoroit_rendered_env.HONOROIT_PREFIX == '!molecule-help'
- matrix_bot_honoroit_rendered_env.HONOROIT_NOENCRYPTIONWARNING | lower == 'true'
- matrix_bot_honoroit_rendered_env.HONOROIT_LOGLEVEL == 'DEBUG'
- matrix_bot_honoroit_rendered_env.HONOROIT_CACHESIZE == '317'
- matrix_bot_honoroit_rendered_env.HONOROIT_IGNORENOTHREAD | lower == 'true'
- matrix_bot_honoroit_rendered_env.HONOROIT_IGNOREDROOMS == '!molecule-ignored-one:molecule.local !molecule-ignored-two:molecule.local'
- matrix_bot_honoroit_rendered_env.HONOROIT_ALLOWEDUSERS == '@support-*:molecule.local @operator:molecule.local'
- matrix_bot_honoroit_rendered_env.HONOROIT_TEXT_PREFIX_OPEN == '[MOLECULE OPEN]'
- matrix_bot_honoroit_rendered_env.HONOROIT_TEXT_PREFIX_DONE == '[MOLECULE DONE]'
- matrix_bot_honoroit_rendered_env.HONOROIT_TEXT_GREETINGS == 'Molecule helpdesk greeting'
- matrix_bot_honoroit_rendered_env.HONOROIT_TEXT_DONE == 'Molecule helpdesk completion'
fail_msg: "The parsed environment lost non-default Honoroit behavior"
success_msg: "The parsed environment carries non-default Honoroit behavior"

- name: Assert the parsed environment contains metrics auth and extension values
ansible.builtin.assert:
that:
- matrix_bot_honoroit_rendered_env.HONOROIT_AUTH_METRICS_LOGIN == 'molecule-metrics-user'
- matrix_bot_honoroit_rendered_env.HONOROIT_AUTH_METRICS_PASSWORD == 'molecule_metrics_password_c9ed32'
- matrix_bot_honoroit_rendered_env.HONOROIT_PORT == ':' ~ (matrix_bot_honoroit_container_port | string)
- matrix_bot_honoroit_rendered_env.MOLECULE_HONOROIT_MARKER == 'role-rendered-environment'
fail_msg: "The parsed environment lost the metrics auth or extension values"
success_msg: "The parsed environment carries metrics auth and extension values"

- name: List the tables Honoroit created in Postgres
ansible.builtin.command:
argv:
- docker
- exec
- matrix-postgres-molecule
- psql
- --username={{ matrix_bot_honoroit_database_username }}
- --dbname={{ matrix_bot_honoroit_database_name }}
- --tuples-only
- --no-align
- --command=SELECT tablename FROM pg_tables WHERE schemaname = 'public' ORDER BY tablename
register: matrix_bot_honoroit_tables
changed_when: false

- name: Assert Honoroit migrated a substantial schema into the configured Postgres database
ansible.builtin.assert:
that:
- matrix_bot_honoroit_tables.rc == 0
- "'crypto_account' in matrix_bot_honoroit_table_names"
- "'crypto_device' in matrix_bot_honoroit_table_names"
- "'crypto_megolm_inbound_session' in matrix_bot_honoroit_table_names"
- "'crypto_olm_session' in matrix_bot_honoroit_table_names"
- "'crypto_secrets' in matrix_bot_honoroit_table_names"
- "'crypto_version' in matrix_bot_honoroit_table_names"
- "'mx_registrations' in matrix_bot_honoroit_table_names"
- "'mx_room_state' in matrix_bot_honoroit_table_names"
- "'mx_user_profile' in matrix_bot_honoroit_table_names"
- "'mx_version' in matrix_bot_honoroit_table_names"
- matrix_bot_honoroit_table_names | length == 17
fail_msg: >-
Honoroit did not create its crypto schema in {{ matrix_bot_honoroit_database_name }}
(found {{ matrix_bot_honoroit_table_names | length }} table(s):
{{ matrix_bot_honoroit_table_names | join(', ') }})
success_msg: "Honoroit migrated its crypto schema into the configured Postgres database"
vars:
matrix_bot_honoroit_table_names: "{{ matrix_bot_honoroit_tables.stdout_lines | select | list }}"

- name: Read Honoroit's database migration markers
ansible.builtin.command:
argv:
- docker
- exec
- matrix-postgres-molecule
- psql
- --username={{ matrix_bot_honoroit_database_username }}
- --dbname={{ matrix_bot_honoroit_database_name }}
- --tuples-only
- --no-align
- "--command=SELECT 'crypto:' || version || ':' || compat FROM crypto_version UNION ALL SELECT 'state:' || version || ':' || compat FROM mx_version ORDER BY 1"
register: matrix_bot_honoroit_migration_versions
changed_when: false

- name: Assert Honoroit's Postgres migrations reached the pinned schema versions
ansible.builtin.assert:
that:
- matrix_bot_honoroit_migration_versions.rc == 0
- matrix_bot_honoroit_migration_versions.stdout_lines == ['crypto:19:15', 'state:10:3']
fail_msg: >-
Honoroit's database migration markers are missing or unexpected
({{ matrix_bot_honoroit_migration_versions.stdout | default('no output') | trim }})
success_msg: "Honoroit's Postgres migrations reached the pinned schema versions"

- name: Read Honoroit's full systemd journal
ansible.builtin.command:
argv:
- journalctl
- --unit=matrix-bot-honoroit.service
- --no-pager
register: matrix_bot_honoroit_journal
changed_when: false

- name: Assert the journal records successful initialization and configured debug logging
ansible.builtin.assert:
that:
- "'Honoroit' in matrix_bot_honoroit_journal_plain"
- "'starting bot...' in matrix_bot_honoroit_journal_plain"
- "'client has been started' in matrix_bot_honoroit_journal_plain"
- "'cannot initialize the bot' not in matrix_bot_honoroit_journal_plain"
- "'matrix bot crashed' not in matrix_bot_honoroit_journal_plain"
fail_msg: "Honoroit's journal does not show a clean debug-level startup"
success_msg: "Honoroit's journal records a clean debug-level startup"
vars:
matrix_bot_honoroit_journal_plain: >-
{{ matrix_bot_honoroit_journal.stdout
| regex_replace('\x1B\[[0-?]*[ -/]*[@-~]', '') }}

- name: Inspect the running Honoroit container
ansible.builtin.command:
argv:
- docker
- container
- inspect
- matrix-bot-honoroit
register: matrix_bot_honoroit_container_inspect
changed_when: false

- name: Assert the running container uses the exact image pinned by the role
ansible.builtin.assert:
that:
- matrix_bot_honoroit_runtime.Config.Image == matrix_bot_honoroit_expected_image
fail_msg: >-
The running image is {{ matrix_bot_honoroit_runtime.Config.Image }}, expected
{{ matrix_bot_honoroit_expected_image }} from defaults/main.yml
success_msg: "The running container uses the exact image pinned by the role"
vars:
matrix_bot_honoroit_expected_image: >-
{{ matrix_bot_honoroit_role_defaults.matrix_bot_honoroit_container_image_registry_prefix_upstream_default }}etkecc/honoroit:{{ matrix_bot_honoroit_role_defaults.matrix_bot_honoroit_version }}

- name: Assert the running container preserves the image and systemd lifecycle contract
ansible.builtin.assert:
that:
- matrix_bot_honoroit_runtime.Config.Entrypoint == ['/bin/honoroit']
- matrix_bot_honoroit_runtime.Config.Cmd is none
- matrix_bot_honoroit_runtime.Config.WorkingDir == '/'
- matrix_bot_honoroit_runtime.HostConfig.RestartPolicy.Name == 'no'
- matrix_bot_honoroit_runtime.HostConfig.AutoRemove is sameas true
- matrix_bot_honoroit_runtime.HostConfig.LogConfig.Type == 'none'
fail_msg: "The running container has an unexpected command or Docker lifecycle contract"
success_msg: "The running container preserves the image command and systemd lifecycle contract"

- name: Assert the running container uses the configured identity and hardening
ansible.builtin.assert:
that:
- matrix_bot_honoroit_runtime.Config.User == '1234:1234'
- matrix_bot_honoroit_runtime.HostConfig.ReadonlyRootfs is sameas true
- matrix_bot_honoroit_runtime.HostConfig.CapDrop == ['ALL']
- matrix_bot_honoroit_runtime.HostConfig.PidsLimit == 73
- matrix_bot_honoroit_runtime.HostConfig.Tmpfs['/tmp'] == 'rw,noexec,nosuid,size=16m'
fail_msg: "The running container lost its configured identity or hardening"
success_msg: "The running container uses the configured identity and hardening"

- name: Assert the running container has only private network attachments
ansible.builtin.assert:
that:
- matrix_bot_honoroit_runtime.HostConfig.NetworkMode == matrix_bot_honoroit_container_network
- matrix_bot_honoroit_runtime.NetworkSettings.Networks.keys() | sort == ([matrix_bot_honoroit_container_network] + matrix_bot_honoroit_container_additional_networks) | sort
- matrix_bot_honoroit_runtime.HostConfig.PortBindings | length == 0
fail_msg: "The running container lost a private network or published a host port"
success_msg: "The running container has both private networks and no published host ports"

- name: Assert the writable data mount is scoped to Honoroit's data directory
ansible.builtin.assert:
that:
- matrix_bot_honoroit_runtime.Mounts | length == 1
- matrix_bot_honoroit_data_mounts | length == 1
- matrix_bot_honoroit_data_mounts[0].Source == matrix_bot_honoroit_data_path
- matrix_bot_honoroit_data_mounts[0].RW is sameas true
fail_msg: "The running container's /data mount does not match the role-managed data path"
success_msg: "The running container has the role-managed writable /data mount"

- name: Assert the running process received the role-rendered environment
ansible.builtin.assert:
that:
- matrix_bot_honoroit_runtime_env.HONOROIT_DB_DSN == matrix_bot_honoroit_rendered_env.HONOROIT_DB_DSN
- matrix_bot_honoroit_runtime_env.HONOROIT_HOMESERVER == matrix_bot_honoroit_rendered_env.HONOROIT_HOMESERVER
- matrix_bot_honoroit_runtime_env.HONOROIT_AUTH_METRICS_LOGIN == matrix_bot_honoroit_rendered_env.HONOROIT_AUTH_METRICS_LOGIN
- matrix_bot_honoroit_runtime_env.HONOROIT_PORT == ':' ~ (matrix_bot_honoroit_container_port | string)
- matrix_bot_honoroit_runtime_env.MOLECULE_HONOROIT_MARKER == 'role-rendered-environment'
fail_msg: "The live container environment differs from the file the role rendered"
success_msg: "The live container received the role-rendered environment"

- name: Read the labels file the role rendered
ansible.builtin.slurp:
src: "{{ matrix_bot_honoroit_config_path }}/labels"
register: matrix_bot_honoroit_labels_file

- name: Assert the parsed labels contain the configured metrics router
ansible.builtin.assert:
that:
- matrix_bot_honoroit_rendered_labels['traefik.enable'] == 'true'
- matrix_bot_honoroit_rendered_labels['traefik.docker.network'] == matrix_bot_honoroit_container_network
- matrix_bot_honoroit_rendered_labels['traefik.http.services.matrix-bot-honoroit-metrics.loadbalancer.server.port'] == (matrix_bot_honoroit_container_port | string)
- matrix_bot_honoroit_rendered_labels['traefik.http.routers.matrix-bot-honoroit-metrics.rule'] == 'Host(`honoroit.molecule.local`) && Path(`/molecule-honoroit/molecule-metrics`)'
- matrix_bot_honoroit_rendered_labels['traefik.http.routers.matrix-bot-honoroit-metrics.priority'] == '710'
- matrix_bot_honoroit_rendered_labels['traefik.http.routers.matrix-bot-honoroit-metrics.service'] == 'matrix-bot-honoroit-metrics'
- matrix_bot_honoroit_rendered_labels['traefik.http.routers.matrix-bot-honoroit-metrics.entrypoints'] == 'molecule-web'
- matrix_bot_honoroit_rendered_labels['traefik.http.routers.matrix-bot-honoroit-metrics.tls'] == 'false'
- matrix_bot_honoroit_rendered_labels['traefik.http.routers.matrix-bot-honoroit-metrics.tls.certResolver'] is not defined
fail_msg: "The parsed labels lost the scenario's metrics router contract"
success_msg: "The parsed labels contain the configured metrics router"

- name: Assert the parsed labels contain the configured middleware chain
ansible.builtin.assert:
that:
- matrix_bot_honoroit_rendered_labels['traefik.http.middlewares.matrix-bot-honoroit-slashless-redirect.redirectregex.regex'] == '(/molecule-honoroit)$'
- matrix_bot_honoroit_rendered_labels['traefik.http.middlewares.matrix-bot-honoroit-slashless-redirect.redirectregex.replacement'] == '${1}/'
- matrix_bot_honoroit_rendered_labels['traefik.http.middlewares.matrix-bot-honoroit-strip-prefix.stripprefix.prefixes'] == '/molecule-honoroit'
- matrix_bot_honoroit_rendered_labels['traefik.http.middlewares.matrix-bot-honoroit-add-headers.headers.customresponseheaders.X-Molecule-Honoroit'] == 'scenario'
- matrix_bot_honoroit_rendered_labels['traefik.http.middlewares.matrix-bot-honoroit-add-headers.headers.customresponseheaders.X-Robots-Tag'] == 'noindex'
- matrix_bot_honoroit_rendered_labels['traefik.http.routers.matrix-bot-honoroit-metrics.middlewares'] == 'matrix-bot-honoroit-slashless-redirect,matrix-bot-honoroit-strip-prefix,matrix-bot-honoroit-add-headers'
fail_msg: "The parsed labels lost the scenario's path and response-header middlewares"
success_msg: "The parsed labels contain the configured middleware chain"

- name: Assert the live container received the rendered router and middleware labels
ansible.builtin.assert:
that:
- matrix_bot_honoroit_runtime.Config.Labels['traefik.http.services.matrix-bot-honoroit-metrics.loadbalancer.server.port'] == (matrix_bot_honoroit_container_port | string)
- matrix_bot_honoroit_runtime.Config.Labels['traefik.http.routers.matrix-bot-honoroit-metrics.rule'] == matrix_bot_honoroit_rendered_labels['traefik.http.routers.matrix-bot-honoroit-metrics.rule']
- matrix_bot_honoroit_runtime.Config.Labels['traefik.http.routers.matrix-bot-honoroit-metrics.priority'] == matrix_bot_honoroit_rendered_labels['traefik.http.routers.matrix-bot-honoroit-metrics.priority']
- matrix_bot_honoroit_runtime.Config.Labels['traefik.http.routers.matrix-bot-honoroit-metrics.middlewares'] == matrix_bot_honoroit_rendered_labels['traefik.http.routers.matrix-bot-honoroit-metrics.middlewares']
- matrix_bot_honoroit_runtime.Config.Labels['traefik.http.middlewares.matrix-bot-honoroit-add-headers.headers.customresponseheaders.X-Molecule-Honoroit'] == 'scenario'
- matrix_bot_honoroit_runtime.Config.Labels['molecule.honoroit.scenario'] == 'live-postgres-and-metrics'
fail_msg: "The live container labels differ from the rendered routing contract"
success_msg: "The live container received the rendered router and middleware labels"

- name: Inspect role-rendered support file ownership and permissions
ansible.builtin.stat:
path: "{{ item }}"
loop:
- "{{ matrix_bot_honoroit_config_path }}/env"
- "{{ matrix_bot_honoroit_config_path }}/labels"
register: matrix_bot_honoroit_support_files

- name: Assert support files are owned by Matrix and not world-readable
ansible.builtin.assert:
that:
- matrix_bot_honoroit_support_files.results | map(attribute='stat.uid') | unique | list == [1234]
- matrix_bot_honoroit_support_files.results | map(attribute='stat.gid') | unique | list == [1234]
- matrix_bot_honoroit_support_files.results | map(attribute='stat.mode') | unique | list == ['0640']
fail_msg: "Honoroit's support files have unexpected ownership or permissions"
success_msg: "Honoroit's support files are Matrix-owned and mode 0640"

Ładowanie…
Anuluj
Zapisz