mirror of
https://github.com/spantaleev/matrix-docker-ansible-deploy.git
synced 2026-09-18 22:00:11 +00:00
Add Molecule coverage for Tuwunel
This commit is contained in:
@@ -156,7 +156,8 @@
|
|||||||
"roles/custom/matrix-livekit-jwt-service/defaults/main.yml",
|
"roles/custom/matrix-livekit-jwt-service/defaults/main.yml",
|
||||||
"roles/custom/matrix-static-files/defaults/main.yml",
|
"roles/custom/matrix-static-files/defaults/main.yml",
|
||||||
"roles/custom/matrix-synapse/defaults/main.yml",
|
"roles/custom/matrix-synapse/defaults/main.yml",
|
||||||
"roles/custom/matrix-synapse-auto-compressor/defaults/main.yml"
|
"roles/custom/matrix-synapse-auto-compressor/defaults/main.yml",
|
||||||
|
"roles/custom/matrix-tuwunel/defaults/main.yml"
|
||||||
],
|
],
|
||||||
"matchUpdateTypes": [
|
"matchUpdateTypes": [
|
||||||
"patch"
|
"patch"
|
||||||
|
|||||||
@@ -160,7 +160,7 @@ a fresh install.
|
|||||||
|
|
||||||
## Databases
|
## Databases
|
||||||
|
|
||||||
Scenarios for roles that have a database run against **Postgres**, not sqlite.
|
Scenarios for roles that support an external SQL database run against **Postgres**, not sqlite.
|
||||||
|
|
||||||
That is what `group_vars/matrix_servers` selects whenever postgres is enabled, which is the
|
That is what `group_vars/matrix_servers` selects whenever postgres is enabled, which is the
|
||||||
default, so it is what essentially every deployment runs. sqlite is a path almost nobody is on:
|
default, so it is what essentially every deployment runs. sqlite is a path almost nobody is on:
|
||||||
@@ -180,6 +180,8 @@ cope with it.
|
|||||||
Prefer asserting on the schema the component created over a file on disk: tables can only appear
|
Prefer asserting on the schema the component created over a file on disk: tables can only appear
|
||||||
once it has resolved the hostname, authenticated, and run its migrations.
|
once it has resolved the hostname, authenticated, and run its migrations.
|
||||||
|
|
||||||
|
Tuwunel uses embedded RocksDB instead. Its scenario exercises token-protected registration and authenticated Matrix APIs against that database, and checks that the configured listener, server name, display-name suffix, upload limit, and client discovery URL reach the running homeserver.
|
||||||
|
|
||||||
## Reclaiming the disk space
|
## Reclaiming the disk space
|
||||||
|
|
||||||
`just molecule-clean` removes what the runs leave under `var/`.
|
`just molecule-clean` removes what the runs leave under `var/`.
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
# SPDX-FileCopyrightText: 2026 Slavi Pantaleev
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
|
---
|
||||||
|
- name: Include roles for Tuwunel Molecule tests
|
||||||
|
hosts: all
|
||||||
|
become: true
|
||||||
|
vars_files:
|
||||||
|
- "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/../../../molecule-shared/playbook-context.yml"
|
||||||
|
gather_facts: true
|
||||||
|
tasks:
|
||||||
|
- name: Include base roles for Tuwunel 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
|
||||||
|
loop_control:
|
||||||
|
loop_var: role_name
|
||||||
|
|
||||||
|
# Include setup directly: main.yml also carries self-check and migration actions.
|
||||||
|
- name: Include Tuwunel setup task files
|
||||||
|
ansible.builtin.include_role:
|
||||||
|
name: "custom/{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') | basename }}"
|
||||||
|
public: true
|
||||||
|
tasks_from: "{{ role_task_file }}"
|
||||||
|
loop:
|
||||||
|
- validate_config.yml
|
||||||
|
- install.yml
|
||||||
|
loop_control:
|
||||||
|
loop_var: role_task_file
|
||||||
|
|
||||||
|
- name: Ensure Tuwunel 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 the Tuwunel systemd service is started
|
||||||
|
ansible.builtin.systemd_service:
|
||||||
|
name: matrix-tuwunel.service
|
||||||
|
state: started
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
# 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: tuwunel-${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_tuwunel_hostname: matrix.example.com
|
||||||
|
matrix_tuwunel_container_network: tuwunel-molecule
|
||||||
|
matrix_tuwunel_config_server_name: example.com
|
||||||
|
matrix_tuwunel_config_port_number: 16167
|
||||||
|
matrix_tuwunel_systemd_service_post_start_delay_seconds: 0
|
||||||
|
|
||||||
|
# Exercise real local registration against the embedded RocksDB database.
|
||||||
|
# These credentials never authenticate to an external service.
|
||||||
|
matrix_tuwunel_config_allow_registration: true
|
||||||
|
matrix_tuwunel_config_registration_token: molecule-registration-7e290fc5
|
||||||
|
matrix_tuwunel_config_new_user_displayname_suffix: '[Molecule]'
|
||||||
|
matrix_tuwunel_config_grant_admin_to_first_user: false
|
||||||
|
matrix_tuwunel_config_allow_federation: false
|
||||||
|
matrix_tuwunel_config_trusted_servers: []
|
||||||
|
matrix_tuwunel_config_max_request_size: 17000000
|
||||||
|
matrix_tuwunel_config_well_known_client: https://matrix.example.com
|
||||||
|
|
||||||
|
# Keep resource use modest on the CI runner. RocksDB is Tuwunel's native
|
||||||
|
# database; no external database or homeserver fixture is needed.
|
||||||
|
matrix_tuwunel_config_rocksdb_parallelism_threads: 2
|
||||||
|
matrix_tuwunel_config_db_cache_capacity_mb: 32
|
||||||
|
matrix_tuwunel_config_db_write_buffer_capacity_mb: 16
|
||||||
|
|
||||||
|
# Labels are inspected on the running container; probes reach it directly
|
||||||
|
# over its own network without publishing a host port or running Traefik.
|
||||||
|
matrix_tuwunel_container_labels_traefik_entrypoints: web
|
||||||
|
|
||||||
|
# Pin paths needed by verify.yml, but load the image version from the role.
|
||||||
|
matrix_tuwunel_base_path: /matrix/tuwunel-molecule
|
||||||
|
matrix_tuwunel_config_path: /matrix/tuwunel-molecule/config
|
||||||
|
matrix_tuwunel_data_path: /matrix/tuwunel-molecule/data
|
||||||
|
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
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
# SPDX-FileCopyrightText: 2026 Slavi Pantaleev
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
|
---
|
||||||
|
- name: Prepare Tuwunel Molecule tests
|
||||||
|
hosts: all
|
||||||
|
become: true
|
||||||
|
vars_files:
|
||||||
|
- "{{ 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 a full deployment. The role's file tasks
|
||||||
|
# resolve owner and group by name, so the role-scoped test provides it directly.
|
||||||
|
- 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"
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
# SPDX-FileCopyrightText: 2026 Slavi Pantaleev
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
|
"""Exercise local Matrix registration and report observations to verify.yml."""
|
||||||
|
|
||||||
|
import json
|
||||||
|
import secrets
|
||||||
|
import sys
|
||||||
|
from urllib.error import HTTPError
|
||||||
|
from urllib.parse import quote
|
||||||
|
from urllib.request import Request, urlopen
|
||||||
|
|
||||||
|
|
||||||
|
base_url, registration_token = sys.argv[1:]
|
||||||
|
|
||||||
|
|
||||||
|
def request(path, body=None, access_token=None):
|
||||||
|
headers = {"Content-Type": "application/json"}
|
||||||
|
if access_token:
|
||||||
|
headers["Authorization"] = f"Bearer {access_token}"
|
||||||
|
data = json.dumps(body).encode() if body is not None else None
|
||||||
|
req = Request(base_url + path, data=data, headers=headers)
|
||||||
|
try:
|
||||||
|
response = urlopen(req, timeout=15)
|
||||||
|
except HTTPError as error:
|
||||||
|
response = error
|
||||||
|
with response:
|
||||||
|
return {"status": response.status, "body": json.load(response)}
|
||||||
|
|
||||||
|
|
||||||
|
# A fresh localpart lets `molecule verify` run repeatedly against the same database.
|
||||||
|
username = "alice_" + secrets.token_hex(6)
|
||||||
|
credentials = {"username": username, "password": secrets.token_urlsafe(24)}
|
||||||
|
result = {"username": username}
|
||||||
|
result["challenge"] = request("/_matrix/client/v3/register", credentials)
|
||||||
|
session = result["challenge"]["body"].get("session", "")
|
||||||
|
auth = {"type": "m.login.registration_token", "session": session}
|
||||||
|
result["bad_token"] = request(
|
||||||
|
"/_matrix/client/v3/register",
|
||||||
|
{**credentials, "auth": {**auth, "token": registration_token + "-invalid"}},
|
||||||
|
)
|
||||||
|
registration = request(
|
||||||
|
"/_matrix/client/v3/register",
|
||||||
|
{**credentials, "auth": {**auth, "token": registration_token}},
|
||||||
|
)
|
||||||
|
access_token = registration["body"].pop("access_token", None)
|
||||||
|
result["registration"] = registration
|
||||||
|
if access_token:
|
||||||
|
result["whoami"] = request("/_matrix/client/v3/account/whoami", access_token=access_token)
|
||||||
|
user_id = quote(registration["body"]["user_id"], safe="")
|
||||||
|
result["profile"] = request(f"/_matrix/client/v3/profile/{user_id}/displayname")
|
||||||
|
result["media"] = request("/_matrix/client/v1/media/config", access_token=access_token)
|
||||||
|
result["well_known"] = request("/.well-known/matrix/client")
|
||||||
|
print(json.dumps(result))
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
../../../../../molecule-shared/requirements.yml
|
||||||
@@ -0,0 +1,219 @@
|
|||||||
|
# SPDX-FileCopyrightText: 2026 Slavi Pantaleev
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
|
---
|
||||||
|
- name: Verify Tuwunel
|
||||||
|
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_tuwunel_molecule_config: "{{ (matrix_tuwunel_molecule_config_result.stdout | from_json).global }}"
|
||||||
|
matrix_tuwunel_molecule_probe: "{{ matrix_tuwunel_molecule_probe_result.stdout | from_json }}"
|
||||||
|
matrix_tuwunel_molecule_container: "{{ (matrix_tuwunel_molecule_inspect.stdout | from_json) | first }}"
|
||||||
|
|
||||||
|
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_tuwunel_role_defaults
|
||||||
|
|
||||||
|
# No host ports: a helper reaches the service over its own container network.
|
||||||
|
- name: Wait for the Matrix versions endpoint on the configured listener
|
||||||
|
ansible.builtin.command:
|
||||||
|
argv:
|
||||||
|
- docker
|
||||||
|
- run
|
||||||
|
- --rm
|
||||||
|
- --network={{ matrix_tuwunel_container_network }}
|
||||||
|
- "{{ molecule_shared_image_curl }}"
|
||||||
|
- --fail
|
||||||
|
- --silent
|
||||||
|
- --show-error
|
||||||
|
- --max-time
|
||||||
|
- '10'
|
||||||
|
- http://matrix-tuwunel:{{ matrix_tuwunel_config_port_number }}/_matrix/client/versions
|
||||||
|
register: matrix_tuwunel_molecule_versions
|
||||||
|
changed_when: false
|
||||||
|
until: matrix_tuwunel_molecule_versions.rc == 0
|
||||||
|
retries: 24
|
||||||
|
delay: 5
|
||||||
|
|
||||||
|
- name: Assert the non-default listener serves the Matrix API
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- "'v1.1' in (matrix_tuwunel_molecule_versions.stdout | from_json).versions"
|
||||||
|
|
||||||
|
- name: Request the default listener which the scenario moved away from
|
||||||
|
ansible.builtin.command:
|
||||||
|
argv:
|
||||||
|
- docker
|
||||||
|
- run
|
||||||
|
- --rm
|
||||||
|
- --network={{ matrix_tuwunel_container_network }}
|
||||||
|
- "{{ molecule_shared_image_curl }}"
|
||||||
|
- --silent
|
||||||
|
- --output
|
||||||
|
- /dev/null
|
||||||
|
- --connect-timeout
|
||||||
|
- '2'
|
||||||
|
- --max-time
|
||||||
|
- '3'
|
||||||
|
- http://matrix-tuwunel:6167/_matrix/client/versions
|
||||||
|
register: matrix_tuwunel_molecule_default_listener
|
||||||
|
changed_when: false
|
||||||
|
failed_when: false
|
||||||
|
|
||||||
|
- name: Assert the default listener refuses connections
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- matrix_tuwunel_molecule_default_listener.rc == 7
|
||||||
|
|
||||||
|
# This uses the real homeserver and its embedded RocksDB database. The helper
|
||||||
|
# only makes requests and records responses; the assertions remain below.
|
||||||
|
- name: Exercise token-protected registration and authenticated Matrix APIs
|
||||||
|
ansible.builtin.command:
|
||||||
|
argv:
|
||||||
|
- docker
|
||||||
|
- run
|
||||||
|
- --rm
|
||||||
|
- --interactive
|
||||||
|
- --network={{ matrix_tuwunel_container_network }}
|
||||||
|
- "{{ molecule_shared_image_python }}"
|
||||||
|
- python
|
||||||
|
- '-'
|
||||||
|
- http://matrix-tuwunel:{{ matrix_tuwunel_config_port_number }}
|
||||||
|
- "{{ matrix_tuwunel_config_registration_token }}"
|
||||||
|
stdin: "{{ lookup('file', 'probe.py') }}"
|
||||||
|
register: matrix_tuwunel_molecule_probe_result
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Assert registration requires the configured token
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- matrix_tuwunel_molecule_probe.challenge.status == 401
|
||||||
|
- "matrix_tuwunel_molecule_probe.challenge.body.flows == [{'stages': ['m.login.registration_token']}]"
|
||||||
|
- matrix_tuwunel_molecule_probe.bad_token.status == 401
|
||||||
|
- matrix_tuwunel_molecule_probe.bad_token.body.errcode == 'M_FORBIDDEN'
|
||||||
|
|
||||||
|
- name: Assert the configured token registers a user on the configured server
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- matrix_tuwunel_molecule_probe.registration.status == 200
|
||||||
|
- matrix_tuwunel_molecule_probe.registration.body.user_id == '@' + matrix_tuwunel_molecule_probe.username + ':' + matrix_tuwunel_config_server_name
|
||||||
|
- matrix_tuwunel_molecule_probe.whoami.status == 200
|
||||||
|
- matrix_tuwunel_molecule_probe.whoami.body.user_id == matrix_tuwunel_molecule_probe.registration.body.user_id
|
||||||
|
|
||||||
|
- name: Assert the configured display-name suffix reaches new users
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- matrix_tuwunel_molecule_probe.profile.status == 200
|
||||||
|
- matrix_tuwunel_molecule_probe.profile.body.displayname == matrix_tuwunel_molecule_probe.username + ' ' + matrix_tuwunel_config_new_user_displayname_suffix
|
||||||
|
|
||||||
|
- name: Assert the authenticated media API advertises the configured upload limit
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- matrix_tuwunel_molecule_probe.media.status == 200
|
||||||
|
- matrix_tuwunel_molecule_probe.media.body['m.upload.size'] == matrix_tuwunel_config_max_request_size
|
||||||
|
|
||||||
|
- name: Assert client discovery serves the configured URL
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- matrix_tuwunel_molecule_probe.well_known.status == 200
|
||||||
|
# Tuwunel normalizes the configured origin to a URL with a trailing slash.
|
||||||
|
- matrix_tuwunel_molecule_probe.well_known.body['m.homeserver'].base_url == matrix_tuwunel_config_well_known_client.rstrip('/') + '/'
|
||||||
|
|
||||||
|
- name: Parse the rendered TOML configuration
|
||||||
|
ansible.builtin.command:
|
||||||
|
argv:
|
||||||
|
- python3
|
||||||
|
- '-c'
|
||||||
|
- "import json, sys, tomllib; print(json.dumps(tomllib.load(open(sys.argv[1], 'rb'))))"
|
||||||
|
- "{{ matrix_tuwunel_config_path }}/tuwunel.toml"
|
||||||
|
register: matrix_tuwunel_molecule_config_result
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Assert the parsed TOML carries the scenario's settings
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- matrix_tuwunel_molecule_config.server_name == matrix_tuwunel_config_server_name
|
||||||
|
- matrix_tuwunel_molecule_config.port == matrix_tuwunel_config_port_number
|
||||||
|
- matrix_tuwunel_molecule_config.allow_registration
|
||||||
|
- matrix_tuwunel_molecule_config.registration_token == matrix_tuwunel_config_registration_token
|
||||||
|
- matrix_tuwunel_molecule_config.new_user_displayname_suffix == matrix_tuwunel_config_new_user_displayname_suffix
|
||||||
|
- matrix_tuwunel_molecule_config.max_request_size == matrix_tuwunel_config_max_request_size
|
||||||
|
- matrix_tuwunel_molecule_config.well_known.client == matrix_tuwunel_config_well_known_client
|
||||||
|
- not matrix_tuwunel_molecule_config.allow_federation
|
||||||
|
- matrix_tuwunel_molecule_config.trusted_servers == []
|
||||||
|
- not matrix_tuwunel_molecule_config.grant_admin_to_first_user
|
||||||
|
|
||||||
|
- name: Inspect the running Tuwunel container
|
||||||
|
ansible.builtin.command:
|
||||||
|
argv: [docker, container, inspect, matrix-tuwunel]
|
||||||
|
register: matrix_tuwunel_molecule_inspect
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Assert the running container uses the exact image pinned by the role
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- matrix_tuwunel_molecule_container.Config.Image == 'ghcr.io/matrix-construct/tuwunel:' + matrix_tuwunel_role_defaults.matrix_tuwunel_version
|
||||||
|
|
||||||
|
- name: Assert the running container uses the playbook identity and isolation
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- matrix_tuwunel_molecule_container.Config.User == (matrix_user_uid | string) + ':' + (matrix_user_gid | string)
|
||||||
|
- matrix_tuwunel_molecule_container.HostConfig.ReadonlyRootfs
|
||||||
|
- "'ALL' in matrix_tuwunel_molecule_container.HostConfig.CapDrop"
|
||||||
|
|
||||||
|
- name: Assert the role's configuration is mounted read-only and selected at runtime
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- "'TUWUNEL_CONFIG=/etc/tuwunel/tuwunel.toml' in matrix_tuwunel_molecule_container.Config.Env"
|
||||||
|
- matrix_tuwunel_molecule_mount.Source == matrix_tuwunel_config_path
|
||||||
|
- not matrix_tuwunel_molecule_mount.RW
|
||||||
|
vars:
|
||||||
|
matrix_tuwunel_molecule_mount: >-
|
||||||
|
{{ matrix_tuwunel_molecule_container.Mounts
|
||||||
|
| selectattr('Destination', 'equalto', '/etc/tuwunel') | first }}
|
||||||
|
|
||||||
|
- name: Assert the embedded database has the configured writable data mount
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- matrix_tuwunel_molecule_mount.Source == matrix_tuwunel_data_path
|
||||||
|
- matrix_tuwunel_molecule_mount.RW
|
||||||
|
vars:
|
||||||
|
matrix_tuwunel_molecule_mount: >-
|
||||||
|
{{ matrix_tuwunel_molecule_container.Mounts
|
||||||
|
| selectattr('Destination', 'equalto', '/var/lib/tuwunel') | first }}
|
||||||
|
|
||||||
|
- name: Assert the container uses only the scenario network and publishes no ports
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- matrix_tuwunel_molecule_container.NetworkSettings.Networks.keys() | list == [matrix_tuwunel_container_network]
|
||||||
|
- matrix_tuwunel_molecule_container.HostConfig.PortBindings | default({}, true) == {}
|
||||||
|
|
||||||
|
- name: Assert Traefik targets the configured listener and public hostname
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- matrix_tuwunel_molecule_labels['traefik.http.services.matrix-tuwunel.loadbalancer.server.port'] == matrix_tuwunel_config_port_number | string
|
||||||
|
- matrix_tuwunel_molecule_labels['traefik.http.routers.matrix-tuwunel-public-client-api.rule'] == 'Host(`' + matrix_tuwunel_hostname + '`) && PathPrefix(`/_matrix`)'
|
||||||
|
- matrix_tuwunel_molecule_labels['traefik.http.routers.matrix-tuwunel-public-tuwunel-api.rule'] == 'Host(`' + matrix_tuwunel_hostname + '`) && PathPrefix(`/_tuwunel`)'
|
||||||
|
- matrix_tuwunel_molecule_labels['traefik.http.routers.matrix-tuwunel-public-client-api.entrypoints'] == matrix_tuwunel_container_labels_traefik_entrypoints
|
||||||
|
vars:
|
||||||
|
matrix_tuwunel_molecule_labels: "{{ matrix_tuwunel_molecule_container.Config.Labels }}"
|
||||||
|
|
||||||
|
# Check after the API probes: Restart=always alone can hide a crash loop.
|
||||||
|
- name: Read the Tuwunel service state after the probes
|
||||||
|
ansible.builtin.systemd_service:
|
||||||
|
name: matrix-tuwunel.service
|
||||||
|
register: matrix_tuwunel_molecule_service
|
||||||
|
|
||||||
|
- name: Assert the service is active without automatic restarts
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- matrix_tuwunel_molecule_service.status.ActiveState == 'active'
|
||||||
|
- matrix_tuwunel_molecule_service.status.NRestarts is defined
|
||||||
|
- matrix_tuwunel_molecule_service.status.NRestarts | int == 0
|
||||||
Reference in New Issue
Block a user