ソースを参照

Test matrix-bot-maubot with Molecule

master
Slavi Pantaleev 5時間前
コミット
494589c5af
6個のファイルの変更626行の追加0行の削除
  1. +1
    -0
      .github/renovate.json
  2. +38
    -0
      roles/custom/matrix-bot-maubot/molecule/default/converge.yml
  3. +100
    -0
      roles/custom/matrix-bot-maubot/molecule/default/molecule.yml
  4. +80
    -0
      roles/custom/matrix-bot-maubot/molecule/default/prepare.yml
  5. +1
    -0
      roles/custom/matrix-bot-maubot/molecule/default/requirements.yml
  6. +406
    -0
      roles/custom/matrix-bot-maubot/molecule/default/verify.yml

+ 1
- 0
.github/renovate.json ファイルの表示

@@ -85,6 +85,7 @@
"matchFileNames": [
"roles/custom/matrix-alertmanager-receiver/defaults/main.yml",
"roles/custom/matrix-bot-baibot/defaults/main.yml",
"roles/custom/matrix-bot-maubot/defaults/main.yml",
"roles/custom/matrix-bot-matrix-reminder-bot/defaults/main.yml",
"roles/custom/matrix-bridge-heisenbridge/defaults/main.yml",
"roles/custom/matrix-bridge-hookshot/defaults/main.yml",


+ 38
- 0
roles/custom/matrix-bot-maubot/molecule/default/converge.yml ファイルの表示

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

---
- name: Include roles for matrix-bot-maubot 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-maubot 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

# Component roles install their unit; the complete playbook's systemd service manager starts
# it. Keep that boundary explicit rather than pulling the manager into this role test.
- name: Ensure matrix-bot-maubot 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-maubot systemd service is started
ansible.builtin.systemd_service:
name: matrix-bot-maubot.service
state: started

+ 100
- 0
roles/custom/matrix-bot-maubot/molecule/default/molecule.yml ファイルの表示

@@ -0,0 +1,100 @@
# 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-maubot-${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:
matrix_bot_maubot_container_network: matrix-bot-maubot-molecule

# verify.yml runs as a separate play, where role defaults are out of scope. Pin
# paths it reads to the values the role derives during converge.
matrix_bot_maubot_base_path: /matrix/maubot
matrix_bot_maubot_config_path: /matrix/maubot/config
matrix_bot_maubot_data_path: /matrix/maubot/data

# Postgres is the normal playbook path. The non-default database and user mean
# successful migrations prove that the role assembled and Maubot consumed this URI.
matrix_bot_maubot_database_engine: postgres
matrix_bot_maubot_database_hostname: matrix-postgres-molecule
matrix_bot_maubot_database_name: molecule_maubot
matrix_bot_maubot_database_username: molecule_maubot
matrix_bot_maubot_database_password: molecule_pg_password_27c541
matrix_bot_maubot_database_sslmode: disable

# Both the port and public path differ from the role and Maubot defaults. The scenario
# proves the internal endpoints and the public-prefix/StripPrefix mapping separately.
matrix_bot_maubot_server_port: 29427
matrix_bot_maubot_scheme: https
matrix_bot_maubot_hostname: maubot.molecule.local
matrix_bot_maubot_path_prefix: /molecule-maubot
matrix_bot_maubot_unshared_secret: molecule_unshared_secret_b9317a

# The homeserver is only recorded as a convenient management-interface preset.
# Maubot has no clients or instances yet, so it does not contact this deliberately
# nonexistent service during startup and no external Matrix credentials are needed.
matrix_bot_maubot_homeserver_name: molecule-homeserver
matrix_bot_maubot_homeserver_url: https://matrix-no-such-service.invalid
matrix_bot_maubot_homeserver_secret: molecule_registration_secret_98e127
matrix_bot_maubot_initial_password: molecule_matrix_password_unused_62aa11

# This account belongs only to Maubot's management API. Logging in below proves the
# running process loaded it, rather than merely proving that a YAML file exists.
matrix_bot_maubot_admins:
molecule-admin: molecule_admin_password_61bd93
matrix_bot_maubot_logging_level: DEBUG

# No Traefik is deployed. Non-default values still let the scenario verify the role's
# public-routing contract in both the label file and Docker's accepted labels.
matrix_bot_maubot_container_labels_traefik_enabled: true
matrix_bot_maubot_container_labels_traefik_docker_network: matrix-bot-maubot-molecule
matrix_bot_maubot_container_labels_management_enabled: true
matrix_bot_maubot_container_labels_management_hostname: maubot.molecule.local
matrix_bot_maubot_container_labels_management_traefik_priority: 719
matrix_bot_maubot_container_labels_management_traefik_entrypoints: web
matrix_bot_maubot_container_labels_management_traefik_tls: false
matrix_bot_maubot_container_labels_additional_labels: |
molecule.maubot.coverage=enabled

# Keep the production-like no-host-port path: probes join the container network.
matrix_bot_maubot_container_management_interface_http_bind_port: ''
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

+ 80
- 0
roles/custom/matrix-bot-maubot/molecule/default/prepare.yml ファイルの表示

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

---
- name: Prepare matrix-bot-maubot 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

# matrix-base creates this identity in the complete playbook. The component role's file
# tasks resolve owner/group by name, so a role-scoped scenario supplies that side effect.
- 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"

# The role also creates this during converge, but Postgres must join it first.
- name: Ensure the container network the role attaches to exists
ansible.builtin.command:
argv:
- docker
- network
- create
- "{{ matrix_bot_maubot_container_network }}"
register: matrix_bot_maubot_molecule_network
changed_when: matrix_bot_maubot_molecule_network.rc == 0
failed_when:
- matrix_bot_maubot_molecule_network.rc != 0
- "'already exists' not in matrix_bot_maubot_molecule_network.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_maubot_container_network }}"
molecule_shared_postgres_database: "{{ matrix_bot_maubot_database_name }}"
molecule_shared_postgres_username: "{{ matrix_bot_maubot_database_username }}"
molecule_shared_postgres_password: "{{ matrix_bot_maubot_database_password }}"

+ 1
- 0
roles/custom/matrix-bot-maubot/molecule/default/requirements.yml ファイルの表示

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

+ 406
- 0
roles/custom/matrix-bot-maubot/molecule/default/verify.yml ファイルの表示

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

---
# Maubot starts without a Matrix client configured. Its real management API and Postgres
# migrations are the weight-bearing evidence, so this needs no homeserver or credentials.
- name: Verify matrix-bot-maubot
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_maubot_config: "{{ matrix_bot_maubot_config_file.content | b64decode | from_yaml }}"
matrix_bot_maubot_ui_body: "{{ matrix_bot_maubot_ui_http.stdout_lines[:-1] | join('\n') }}"
matrix_bot_maubot_paths_body: "{{ matrix_bot_maubot_paths_http.stdout_lines[:-1] | join('\n') }}"
matrix_bot_maubot_paths: "{{ matrix_bot_maubot_paths_body | from_json }}"
matrix_bot_maubot_login_body: "{{ matrix_bot_maubot_login_http.stdout_lines[:-1] | join('\n') }}"
matrix_bot_maubot_login_response: "{{ matrix_bot_maubot_login_body | from_json }}"
matrix_bot_maubot_expected_database_uri: >-
postgres://{{ matrix_bot_maubot_database_username }}:{{ matrix_bot_maubot_database_password }}@{{ matrix_bot_maubot_database_hostname }}:5432/{{ matrix_bot_maubot_database_name }}?sslmode={{ matrix_bot_maubot_database_sslmode }}
matrix_bot_maubot_labels_lines: "{{ (matrix_bot_maubot_labels_file.content | b64decode).splitlines() }}"
matrix_bot_maubot_runtime: "{{ (matrix_bot_maubot_container_inspect.stdout | from_json) | first }}"
matrix_bot_maubot_config_mounts: "{{ matrix_bot_maubot_runtime.Mounts | selectattr('Destination', 'equalto', '/config') | list }}"
matrix_bot_maubot_data_mounts: "{{ matrix_bot_maubot_runtime.Mounts | selectattr('Destination', 'equalto', '/data') | list }}"
gather_facts: false
tasks:
# Read the version from the role itself, so a role pin bump changes what is expected.
- 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_maubot_role_defaults

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

# Restart=always can hide a crash loop behind ActiveState=active.
- name: Assert the service is active and has not restarted
ansible.builtin.assert:
that:
- matrix_bot_maubot_service.status.ActiveState == 'active'
- matrix_bot_maubot_service.status.NRestarts is defined
- matrix_bot_maubot_service.status.NRestarts | int == 0
fail_msg: >-
matrix-bot-maubot.service is
{{ matrix_bot_maubot_service.status.ActiveState | default('unknown') }} after
{{ matrix_bot_maubot_service.status.NRestarts | default('?') }} restart(s)
success_msg: "matrix-bot-maubot.service is active and has not restarted"

- name: Read the configuration file the role rendered
ansible.builtin.slurp:
src: "{{ matrix_bot_maubot_config_path }}/config.yaml"
register: matrix_bot_maubot_config_file

- name: Assert the parsed server configuration carries the scenario's values
ansible.builtin.assert:
that:
- matrix_bot_maubot_config.server.hostname == '0.0.0.0'
- matrix_bot_maubot_config.server.port == matrix_bot_maubot_server_port
- matrix_bot_maubot_config.server.public_url == 'https://maubot.molecule.local/molecule-maubot'
- matrix_bot_maubot_config.server.ui_base_path == '/'
- matrix_bot_maubot_config.server.plugin_base_path == '/_matrix/maubot/plugin/'
- "'base_path' not in matrix_bot_maubot_config.server"
- "'appservice_base_path' not in matrix_bot_maubot_config.server"
- matrix_bot_maubot_config.server.unshared_secret == matrix_bot_maubot_unshared_secret
fail_msg: "The parsed server block does not carry the scenario's non-default values"
success_msg: "The parsed server block carries the scenario's non-default values"

- name: Assert the parsed database configuration carries the scenario's Postgres URI
ansible.builtin.assert:
that:
- matrix_bot_maubot_config.database == matrix_bot_maubot_expected_database_uri
- matrix_bot_maubot_config.database.startswith('postgres://')
- matrix_bot_maubot_database_username in matrix_bot_maubot_config.database
- matrix_bot_maubot_database_password in matrix_bot_maubot_config.database
- matrix_bot_maubot_database_hostname in matrix_bot_maubot_config.database
- matrix_bot_maubot_database_name in matrix_bot_maubot_config.database
fail_msg: "The parsed configuration does not point at the scenario's Postgres database"
success_msg: "The parsed configuration points at the scenario's Postgres database"

- name: Assert the parsed homeserver preset carries the scenario's values
ansible.builtin.assert:
that:
- matrix_bot_maubot_homeserver_name in matrix_bot_maubot_config.homeservers
- matrix_bot_maubot_config.homeservers[matrix_bot_maubot_homeserver_name].url == matrix_bot_maubot_homeserver_url
- matrix_bot_maubot_config.homeservers[matrix_bot_maubot_homeserver_name].secret == matrix_bot_maubot_homeserver_secret
fail_msg: "The parsed homeserver preset does not carry the scenario's values"
success_msg: "The parsed homeserver preset carries the scenario's values"

- name: Assert the parsed management configuration carries scenario values
ansible.builtin.assert:
that:
- matrix_bot_maubot_config.admins['molecule-admin'] == 'molecule_admin_password_61bd93'
- matrix_bot_maubot_config.admins.root == ''
- matrix_bot_maubot_config.api_features.values() | select('equalto', true) | list | length == matrix_bot_maubot_config.api_features | length
fail_msg: "The parsed management configuration lost the admin or API features"
success_msg: "The parsed management configuration carries the admin and API features"

- name: Assert the parsed logging configuration carries the scenario's level
ansible.builtin.assert:
that:
- matrix_bot_maubot_config.logging.loggers.maubot.level == matrix_bot_maubot_logging_level
- matrix_bot_maubot_config.logging.loggers.mau.level == matrix_bot_maubot_logging_level
- matrix_bot_maubot_config.logging.loggers.aiohttp.level == matrix_bot_maubot_logging_level
- matrix_bot_maubot_config.logging.root.level == matrix_bot_maubot_logging_level
fail_msg: "The parsed logging configuration does not carry the scenario's level"
success_msg: "The parsed logging configuration carries the scenario's level"

# Traefik strips the configured public prefix before proxying, so the role deliberately
# configures the process UI at /. Probe that exact internal contract over the network.
- name: Wait for Maubot's management UI on the configured internal port and path
ansible.builtin.command:
argv:
- docker
- run
- --rm
- --network={{ matrix_bot_maubot_container_network }}
- "{{ molecule_shared_image_curl }}"
- --silent
- --show-error
- --write-out
- "\nHTTP_STATUS=%{http_code}"
- "http://matrix-bot-maubot:{{ matrix_bot_maubot_server_port }}/"
register: matrix_bot_maubot_ui_http
changed_when: false
until: "'HTTP_STATUS=200' in matrix_bot_maubot_ui_http.stdout"
retries: 24
delay: 5
failed_when: false

- name: Assert Maubot serves its real management UI at the configured internal address
ansible.builtin.assert:
that:
- matrix_bot_maubot_ui_http.rc == 0
- matrix_bot_maubot_ui_http.stdout_lines[-1] == 'HTTP_STATUS=200'
- matrix_bot_maubot_ui_body | trim | length > 100
- "'<!doctype html' in matrix_bot_maubot_ui_body | lower"
- "'maubot' in matrix_bot_maubot_ui_body | lower"
fail_msg: "Maubot did not serve its management UI on the configured port at /"
success_msg: "Maubot serves its real management UI on the configured port at /"

# Maubot derives this browser-facing API location from server.public_url. It is live
# process evidence that the public prefix reached Maubot and that the frontend will send
# requests through the Traefik prefix which the role's labels strip below.
- name: Fetch Maubot's live frontend path metadata
ansible.builtin.command:
argv:
- docker
- run
- --rm
- --network={{ matrix_bot_maubot_container_network }}
- "{{ molecule_shared_image_curl }}"
- --silent
- --show-error
- --write-out
- "\nHTTP_STATUS=%{http_code}"
- "http://matrix-bot-maubot:{{ matrix_bot_maubot_server_port }}/paths.json"
register: matrix_bot_maubot_paths_http
changed_when: false
failed_when: false

- name: Assert Maubot advertises the API under the configured public prefix
ansible.builtin.assert:
that:
- matrix_bot_maubot_paths_http.rc == 0
- matrix_bot_maubot_paths_http.stdout_lines[-1] == 'HTTP_STATUS=200'
- matrix_bot_maubot_paths is mapping
- matrix_bot_maubot_paths.api_path == '/molecule-maubot/_matrix/maubot/v1'
fail_msg: >-
Maubot did not derive the prefixed browser API path from server.public_url
({{ matrix_bot_maubot_paths_http.stdout | default('no output') }})
success_msg: "Maubot advertises its browser API under the configured public prefix"

# This authenticates against the live management API with the non-default admin from
# the rendered YAML. It proves both the API base path and account reached the process.
- name: Log in to Maubot's management API
ansible.builtin.command:
argv:
- docker
- run
- --rm
- --network={{ matrix_bot_maubot_container_network }}
- "{{ molecule_shared_image_curl }}"
- --silent
- --show-error
- --header
- 'Content-Type: application/json'
- --data
- '{"username":"molecule-admin","password":"molecule_admin_password_61bd93"}'
- --write-out
- "\nHTTP_STATUS=%{http_code}"
- "http://matrix-bot-maubot:{{ matrix_bot_maubot_server_port }}/_matrix/maubot/v1/auth/login"
register: matrix_bot_maubot_login_http
changed_when: false
failed_when: false

- name: Assert the management API authenticated the configured admin
ansible.builtin.assert:
that:
- matrix_bot_maubot_login_http.rc == 0
- matrix_bot_maubot_login_http.stdout_lines[-1] == 'HTTP_STATUS=200'
- matrix_bot_maubot_login_response is mapping
- matrix_bot_maubot_login_response.token is string
- matrix_bot_maubot_login_response.token | length > 20
fail_msg: >-
Maubot's live management API rejected the admin the role configured
({{ matrix_bot_maubot_login_http.stdout | default('no output') }})
success_msg: "Maubot's live management API authenticated the configured admin"

# The role's default port must not answer. Otherwise reaching the custom port would not
# prove that the configured value made it into the running process.
- name: Probe the role's default Maubot port
ansible.builtin.command:
argv:
- docker
- run
- --rm
- --network={{ matrix_bot_maubot_container_network }}
- "{{ molecule_shared_image_curl }}"
- --silent
- --show-error
- --max-time
- '3'
- --output
- /dev/null
- --write-out
- "HTTP_STATUS=%{http_code}"
- http://matrix-bot-maubot:29316/
register: matrix_bot_maubot_default_port_http
changed_when: false
failed_when: false

- name: Assert Maubot is not listening on the role's default port
ansible.builtin.assert:
that:
- matrix_bot_maubot_default_port_http.rc != 0
- "'HTTP_STATUS=200' not in matrix_bot_maubot_default_port_http.stdout"
fail_msg: "Maubot still answers on 29316, so the configured port is not proven"
success_msg: "Maubot does not listen on the role's default port"

# Tables can only appear after hostname resolution, authentication and migrations.
- name: List the tables Maubot created in Postgres
ansible.builtin.command:
argv:
- docker
- exec
- matrix-postgres-molecule
- psql
- --username={{ matrix_bot_maubot_database_username }}
- --dbname={{ matrix_bot_maubot_database_name }}
- --tuples-only
- --no-align
- --command=SELECT tablename FROM pg_tables WHERE schemaname = 'public'
register: matrix_bot_maubot_tables
changed_when: false

- name: Assert Maubot migrated its schema into the configured Postgres database
ansible.builtin.assert:
that:
- matrix_bot_maubot_tables.rc == 0
- "'version' in matrix_bot_maubot_table_names"
- "'client' in matrix_bot_maubot_table_names"
- "'instance' in matrix_bot_maubot_table_names"
- "'database_owner' in matrix_bot_maubot_table_names"
- matrix_bot_maubot_table_names | length > 5
fail_msg: >-
Maubot did not create its schema in {{ matrix_bot_maubot_database_name }}
(found {{ matrix_bot_maubot_table_names | length }} table(s))
success_msg: "Maubot migrated its schema into the configured Postgres database"
vars:
matrix_bot_maubot_table_names: "{{ matrix_bot_maubot_tables.stdout_lines | select | list }}"

- name: Read the labels the role rendered
ansible.builtin.slurp:
src: "{{ matrix_bot_maubot_base_path }}/labels"
register: matrix_bot_maubot_labels_file

- name: Assert the labels carry the configured management exposure
ansible.builtin.assert:
that:
- "'traefik.enable=true' in matrix_bot_maubot_labels_lines"
- "'traefik.docker.network=matrix-bot-maubot-molecule' in matrix_bot_maubot_labels_lines"
- "'traefik.http.services.matrix-bot-maubot.loadbalancer.server.port=29427' in matrix_bot_maubot_labels_lines"
- "'traefik.http.routers.matrix-bot-maubot-management.rule=Host(`maubot.molecule.local`) && PathPrefix(`/molecule-maubot`)' in matrix_bot_maubot_labels_lines"
- "'traefik.http.routers.matrix-bot-maubot-management.priority=719' in matrix_bot_maubot_labels_lines"
- "'traefik.http.middlewares.matrix-bot-maubot-management-strip-prefix.stripprefix.prefixes=/molecule-maubot' in matrix_bot_maubot_labels_lines"
- "'traefik.http.routers.matrix-bot-maubot-management.middlewares=matrix-bot-maubot-management-strip-prefix' in matrix_bot_maubot_labels_lines"
- "'traefik.http.routers.matrix-bot-maubot-management.entrypoints=web' in matrix_bot_maubot_labels_lines"
- "'traefik.http.routers.matrix-bot-maubot-management.tls=false' in matrix_bot_maubot_labels_lines"
- "'molecule.maubot.coverage=enabled' in matrix_bot_maubot_labels_lines"
fail_msg: "The role's labels do not carry the scenario's public management wiring"
success_msg: "The role's labels carry the scenario's public management wiring"

- name: Inspect the running Maubot container
ansible.builtin.command:
argv:
- docker
- container
- inspect
- matrix-bot-maubot
register: matrix_bot_maubot_container_inspect
changed_when: false

- name: Assert the running container uses the exact image pinned by the role
ansible.builtin.assert:
that:
- matrix_bot_maubot_runtime.Config.Image == 'dock.mau.dev/maubot/maubot:' + matrix_bot_maubot_role_defaults.matrix_bot_maubot_version
fail_msg: >-
The running image {{ matrix_bot_maubot_runtime.Config.Image }} is not the exact
dock.mau.dev/maubot/maubot:{{ matrix_bot_maubot_role_defaults.matrix_bot_maubot_version }} pin
success_msg: "The running container uses the exact image pinned by the role"

- name: Assert the running container uses the playbook's matrix identity
ansible.builtin.assert:
that:
- matrix_bot_maubot_runtime.Config.User.split(':')[0] == matrix_user_uid | string
- matrix_bot_maubot_runtime.Config.User.split(':')[1] == matrix_user_gid | string
fail_msg: >-
Maubot runs as {{ matrix_bot_maubot_runtime.Config.User }} instead of
{{ matrix_user_uid }}:{{ matrix_user_gid }}
success_msg: "The running container uses the playbook's matrix UID and GID"

- name: Assert the container root filesystem is read-only
ansible.builtin.assert:
that:
- matrix_bot_maubot_runtime.HostConfig.ReadonlyRootfs is sameas true
fail_msg: "The Maubot container root filesystem is writable"
success_msg: "The Maubot container root filesystem is read-only"

- name: Assert all Linux capabilities are dropped
ansible.builtin.assert:
that:
- matrix_bot_maubot_runtime.HostConfig.CapDrop == ['ALL']
fail_msg: "The Maubot container does not drop all Linux capabilities"
success_msg: "The Maubot container drops all Linux capabilities"

- name: Assert the container has the role's writable temporary filesystem
ansible.builtin.assert:
that:
- matrix_bot_maubot_runtime.HostConfig.Tmpfs['/tmp'] == 'rw,noexec,nosuid,size=1024m'
fail_msg: "The Maubot container is missing its constrained /tmp tmpfs"
success_msg: "The Maubot container has its constrained writable /tmp tmpfs"

- name: Assert the configuration bind mount is read-only
ansible.builtin.assert:
that:
- matrix_bot_maubot_config_mounts | length == 1
- matrix_bot_maubot_config_mounts[0].RW is sameas false
fail_msg: "The Maubot configuration bind mount is missing or writable"
success_msg: "The Maubot configuration bind mount is present and read-only"

- name: Assert the data bind mount is writable
ansible.builtin.assert:
that:
- matrix_bot_maubot_data_mounts | length == 1
- matrix_bot_maubot_data_mounts[0].RW is sameas true
fail_msg: "The Maubot data bind mount is missing or read-only"
success_msg: "The Maubot data bind mount is present and writable"

- name: Assert the container is attached only to its dedicated network
ansible.builtin.assert:
that:
- matrix_bot_maubot_runtime.NetworkSettings.Networks is mapping
- matrix_bot_maubot_runtime.NetworkSettings.Networks | length == 1
- matrix_bot_maubot_container_network in matrix_bot_maubot_runtime.NetworkSettings.Networks
fail_msg: >-
Maubot has unexpected networks:
{{ matrix_bot_maubot_runtime.NetworkSettings.Networks.keys() | list }}
success_msg: "The Maubot container is attached only to its dedicated network"

- name: Ask Docker for Maubot's published ports
ansible.builtin.command:
argv:
- docker
- container
- port
- matrix-bot-maubot
register: matrix_bot_maubot_published_ports
changed_when: false
failed_when: false

- name: Assert the role did not publish a host port
ansible.builtin.assert:
that:
- matrix_bot_maubot_runtime.HostConfig.PortBindings | default({}, true) | length == 0
- matrix_bot_maubot_published_ports.rc == 0
- matrix_bot_maubot_published_ports.stdout | trim | length == 0
fail_msg: >-
Maubot unexpectedly publishes a host port:
{{ matrix_bot_maubot_published_ports.stdout | default('unknown') }}
success_msg: "The role leaves Maubot's management port unpublished"

- name: Assert Docker accepted the role's custom label
ansible.builtin.assert:
that:
- matrix_bot_maubot_runtime.Config.Labels is mapping
- matrix_bot_maubot_runtime.Config.Labels['molecule.maubot.coverage'] == 'enabled'
fail_msg: "Docker did not attach the custom label from the role's label file"
success_msg: "Docker accepted the custom label from the role's label file"

読み込み中…
キャンセル
保存