mirror of
https://github.com/spantaleev/matrix-docker-ansible-deploy.git
synced 2026-08-29 12:03:14 +00:00
Reword the Molecule scenario comments
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
This commit is contained in:
co-authored by
Claude Opus 5
parent
e2d3be504e
commit
c447e1528b
@@ -3,11 +3,9 @@
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
---
|
||||
# The devture base roles carry the variables this role reads
|
||||
# (`devture_systemd_docker_base_*`, `devture_playbook_help_*`), the same way
|
||||
# they do when the playbook runs. `matrix-base` is deliberately NOT included:
|
||||
# it does far more than this role needs, and the two variables it would supply
|
||||
# are set directly in molecule.yml instead.
|
||||
# The devture base roles carry the variables this role reads, the same way they do when
|
||||
# the playbook runs. `matrix-base` is deliberately NOT included: it does far more than this
|
||||
# role needs, and the two variables it would supply are set directly in molecule.yml.
|
||||
- name: Include roles for matrix-alertmanager-receiver Molecule tests
|
||||
hosts: all
|
||||
become: true
|
||||
@@ -26,8 +24,8 @@
|
||||
loop_control:
|
||||
loop_var: role_name
|
||||
|
||||
# The role installs the unit but does not start it - in the playbook that is
|
||||
# `systemd_service_manager`'s job - so the scenario starts it here.
|
||||
# The role installs the unit but does not start it; in the playbook that is
|
||||
# `systemd_service_manager`'s job.
|
||||
- name: Ensure matrix-alertmanager-receiver is started
|
||||
hosts: all
|
||||
become: true
|
||||
|
||||
@@ -31,27 +31,24 @@ provisioner:
|
||||
matrix_alertmanager_receiver_path_prefix: /
|
||||
matrix_alertmanager_receiver_container_network: matrix-alertmanager-receiver-molecule
|
||||
|
||||
# verify.yml runs as its own play, where the role's defaults are out
|
||||
# of scope, so the paths it reads are pinned here as literals. They
|
||||
# match what the role derives from matrix_base_data_path above.
|
||||
# verify.yml runs as its own play, where the role's defaults are out of scope,
|
||||
# so the paths it reads are pinned here to match what the role derives.
|
||||
matrix_alertmanager_receiver_base_path: /matrix/alertmanager-receiver
|
||||
matrix_alertmanager_receiver_config_path: /matrix/alertmanager-receiver/config
|
||||
|
||||
# Traefik is not deployed in this scenario, so the labels the role would
|
||||
# render for it are switched off and their absence is asserted instead.
|
||||
# Traefik is not deployed here, so the labels the role would render for it are
|
||||
# switched off and their absence is asserted instead.
|
||||
matrix_alertmanager_receiver_container_labels_traefik_enabled: false
|
||||
|
||||
# Deliberately different from the role's own defaults (port 12345,
|
||||
# metrics disabled, alerts under /alerts), so that `verify.yml` can tell
|
||||
# what the role rendered apart from what the application would have done
|
||||
# on its own.
|
||||
# Different from the role's own defaults, so verify.yml can tell what the role
|
||||
# rendered apart from what the application would have done on its own.
|
||||
matrix_alertmanager_receiver_config_http_port: 12399
|
||||
matrix_alertmanager_receiver_config_http_metrics_enabled: true
|
||||
matrix_alertmanager_receiver_config_http_metrics_path: /molecule-metrics
|
||||
matrix_alertmanager_receiver_config_http_alerts_path_prefix: /molecule-alerts
|
||||
|
||||
# The homeserver IS reached at startup - the service fetches its joined
|
||||
# rooms and exits 1 if that fails - so prepare.yml stands up a stub for it
|
||||
# The homeserver IS reached at startup: the service fetches its joined rooms and
|
||||
# exits 1 if that fails, so prepare.yml stands up a stub for it.
|
||||
matrix_alertmanager_receiver_config_matrix_homeserver_url: http://matrix.molecule.local:8008
|
||||
matrix_alertmanager_receiver_config_matrix_user_id: "@alertmanager:molecule.local"
|
||||
matrix_alertmanager_receiver_config_matrix_access_token: molecule_access_token_4f2a91
|
||||
|
||||
@@ -31,9 +31,8 @@
|
||||
docker_daemon_options:
|
||||
storage-driver: fuse-overlayfs
|
||||
|
||||
# The role's file tasks set owner/group by name, and Ansible resolves those
|
||||
# through the passwd database - so they have to exist before it runs. In a
|
||||
# real deployment `matrix-base` creates them.
|
||||
# The role's file tasks set owner/group by name, which Ansible resolves through the
|
||||
# passwd database, so they have to exist first. `matrix-base` creates them for real.
|
||||
- name: Ensure the matrix group exists
|
||||
ansible.builtin.group:
|
||||
name: "{{ matrix_group_name }}"
|
||||
@@ -70,10 +69,9 @@
|
||||
- matrix_alertmanager_receiver_molecule_network.rc != 0
|
||||
- "'already exists' not in matrix_alertmanager_receiver_molecule_network.stderr"
|
||||
|
||||
# matrix-alertmanager-receiver contacts the homeserver while starting up -
|
||||
# it fetches /_matrix/client/v3/joined_rooms to resolve its room mapping and
|
||||
# exits 1 if that fails - so a homeserver has to exist for it to come up at
|
||||
# all. The shared stub is enough; see molecule-shared/homeserver-stub.py.
|
||||
# The service fetches /_matrix/client/v3/joined_rooms to resolve its room mapping and
|
||||
# exits 1 if that fails, so a homeserver has to exist for it to come up at all.
|
||||
# The shared stub is enough. See molecule-shared/homeserver-stub.py.
|
||||
- name: Ensure the homeserver stub is running
|
||||
ansible.builtin.include_tasks:
|
||||
file: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/../../../molecule-shared/tasks/homeserver-stub.yml"
|
||||
|
||||
@@ -12,10 +12,9 @@
|
||||
gather_facts: false
|
||||
|
||||
tasks:
|
||||
# The version is read out of the role's own defaults rather than pinned in
|
||||
# molecule.yml, so that the assertion further down compares the running
|
||||
# image against what defaults/main.yml actually ships. Pinning it here
|
||||
# would make that assertion compare the scenario with itself.
|
||||
# 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"
|
||||
@@ -30,10 +29,9 @@
|
||||
delay: 5
|
||||
failed_when: false
|
||||
|
||||
# `Restart=always` means a crash-looping container still reports `active`,
|
||||
# so the restart counter is checked alongside it. Asserted as `is defined`
|
||||
# too, because `| int` turns a missing property into 0 and would pass
|
||||
# vacuously on a systemd that does not expose it.
|
||||
# `Restart=always` means a crash-looping container still reports `active`, so the restart
|
||||
# counter is checked too. Asserted `is defined` because `| int` turns a missing property
|
||||
# into 0 and would pass vacuously.
|
||||
- name: Assert the service is active and has not been restarting
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
@@ -47,9 +45,9 @@
|
||||
automatic restart(s)
|
||||
success_msg: "matrix-alertmanager-receiver.service is active and has not restarted"
|
||||
|
||||
# Probed from inside the container network rather than from the host: the
|
||||
# role publishes no host port, exactly as it does in a real deployment,
|
||||
# where Traefik reaches it over the network instead.
|
||||
# Probed from inside the container network rather than the host, because the role
|
||||
# publishes no host port - exactly as in a real deployment, where Traefik reaches it
|
||||
# over the network.
|
||||
- name: Wait for matrix-alertmanager-receiver to answer on the port the role configured
|
||||
ansible.builtin.command:
|
||||
argv:
|
||||
@@ -70,9 +68,8 @@
|
||||
delay: 5
|
||||
failed_when: false
|
||||
|
||||
# The port and the metrics path are both non-default in this scenario, so a
|
||||
# 200 here is only reachable if the configuration the role rendered is what
|
||||
# the process is actually running on.
|
||||
# Port and metrics path are both non-default here, so a 200 is only reachable if what
|
||||
# the role rendered is what the process is running on.
|
||||
- name: Assert the configured port and metrics path reached the process
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
@@ -93,9 +90,8 @@
|
||||
The metrics endpoint answered, but did not return Prometheus metrics
|
||||
success_msg: "The metrics endpoint returns Prometheus metrics"
|
||||
|
||||
# A negative control for the assertion above: the role's own default metrics
|
||||
# path must NOT answer, or a 200 on the configured path would prove nothing
|
||||
# about the configuration having been applied.
|
||||
# Negative control for the assertion above: the role's own default metrics path must NOT
|
||||
# answer, or a 200 on the configured path would prove nothing.
|
||||
- name: Ask for the role's default metrics path, which this scenario moved away from
|
||||
ansible.builtin.command:
|
||||
argv:
|
||||
|
||||
@@ -3,11 +3,9 @@
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
---
|
||||
# The devture base roles carry the variables this role reads
|
||||
# (`devture_systemd_docker_base_*`, `devture_playbook_help_*`), the same way
|
||||
# they do when the playbook runs. `matrix-base` is deliberately NOT included:
|
||||
# it does far more than this role needs, and what it would supply comes from
|
||||
# molecule-shared/playbook-context.yml instead.
|
||||
# The devture base roles carry the variables this role reads, the same way they do when
|
||||
# the playbook runs. `matrix-base` is deliberately NOT included: it does far more than this
|
||||
# role needs, and what it would supply comes from molecule-shared/playbook-context.yml.
|
||||
- name: Include roles for matrix-bot-baibot Molecule tests
|
||||
hosts: all
|
||||
become: true
|
||||
@@ -26,8 +24,8 @@
|
||||
loop_control:
|
||||
loop_var: role_name
|
||||
|
||||
# The role installs the unit but does not start it - in the playbook that is
|
||||
# `systemd_service_manager`'s job - so the scenario starts it here.
|
||||
# The role installs the unit but does not start it; in the playbook that is
|
||||
# `systemd_service_manager`'s job.
|
||||
- name: Ensure matrix-bot-baibot is started
|
||||
hosts: all
|
||||
become: true
|
||||
|
||||
@@ -29,22 +29,18 @@ provisioner:
|
||||
all:
|
||||
matrix_bot_baibot_container_network: matrix-bot-baibot-molecule
|
||||
|
||||
# verify.yml runs as its own play, where the role's defaults are out
|
||||
# of scope, so the paths it reads are pinned here as literals. They
|
||||
# match what the role derives from matrix_base_data_path.
|
||||
# verify.yml runs as its own play, where the role's defaults are out of scope,
|
||||
# so the paths it reads are pinned here to match what the role derives.
|
||||
matrix_bot_baibot_base_path: /matrix/baibot
|
||||
matrix_bot_baibot_config_path: /matrix/baibot/config
|
||||
matrix_bot_baibot_data_path: /matrix/baibot/data
|
||||
|
||||
# baibot is a plain Matrix client, not an appservice: it logs in with a
|
||||
# password and then syncs, so a homeserver has to answer for it to get
|
||||
# anywhere. prepare.yml stands up the shared stub for that.
|
||||
# baibot is a plain Matrix client, not an appservice: it logs in with a password
|
||||
# and then syncs, so a homeserver has to answer for it to get anywhere.
|
||||
matrix_bot_baibot_config_homeserver_url: http://matrix.molecule.local:8008
|
||||
|
||||
# Deliberately different from the role's defaults (localpart `baibot`,
|
||||
# name `baibot`, prefix `!bai`, self-introduction on) AND from baibot's
|
||||
# own built-in defaults, so that a passing assertion cannot be explained
|
||||
# by "it would have happened anyway".
|
||||
# Deliberately different from the role's defaults AND from baibot's own, so a passing
|
||||
# assertion cannot be explained by "it would have happened anyway".
|
||||
matrix_bot_baibot_config_user_mxid_localpart: molecule-baibot
|
||||
matrix_bot_baibot_config_user_name: Molecule baibot
|
||||
matrix_bot_baibot_config_user_password: molecule_baibot_password_5b7c14
|
||||
@@ -53,18 +49,15 @@ provisioner:
|
||||
matrix_bot_baibot_config_access_admin_patterns:
|
||||
- "@molecule-admin:molecule.local"
|
||||
|
||||
# `debug` rather than the role's `info`, so the journal carries what the
|
||||
# bot loaded. verify.yml reads it.
|
||||
# `debug` rather than the role's `info`, so the journal carries what the bot loaded.
|
||||
matrix_bot_baibot_config_logging_level_baibot: debug
|
||||
|
||||
# baibot talks to AI providers, and a scenario must not need a provider
|
||||
# account. It does not have to: providers are contacted only when a
|
||||
# message asks an agent to do something, never at startup. So a static
|
||||
# agent is defined with a placeholder key and a base URL that resolves
|
||||
# nowhere. Nothing is ever called, and the agent still has to survive
|
||||
# baibot's startup parsing of `agents.static_definitions` - which is
|
||||
# what proves the role's provider templating produced something the bot
|
||||
# accepts.
|
||||
# A scenario must not need an AI provider account, and does not have to: providers are
|
||||
# contacted only when a message asks an agent to do something, never at startup.
|
||||
# So the agent below carries a placeholder key and a base URL that resolves nowhere.
|
||||
# Nothing is ever called, yet the definition still has to survive baibot's startup
|
||||
# parsing - which is what proves the role's provider templating produced something
|
||||
# the bot accepts.
|
||||
matrix_bot_baibot_config_agents_static_definitions_anthropic_enabled: true
|
||||
matrix_bot_baibot_config_agents_static_definitions_anthropic_id: molecule-anthropic
|
||||
matrix_bot_baibot_config_agents_static_definitions_anthropic_config_base_url: http://molecule-no-such-provider.invalid/v1
|
||||
|
||||
@@ -31,9 +31,8 @@
|
||||
docker_daemon_options:
|
||||
storage-driver: fuse-overlayfs
|
||||
|
||||
# The role's file tasks set owner/group by name, and Ansible resolves those
|
||||
# through the passwd database - so they have to exist before it runs. In a
|
||||
# real deployment `matrix-base` creates them.
|
||||
# The role's file tasks set owner/group by name, which Ansible resolves through the
|
||||
# passwd database, so they have to exist first. `matrix-base` creates them for real.
|
||||
- name: Ensure the matrix group exists
|
||||
ansible.builtin.group:
|
||||
name: "{{ matrix_group_name }}"
|
||||
@@ -70,13 +69,11 @@
|
||||
- matrix_bot_baibot_molecule_network.rc != 0
|
||||
- "'already exists' not in matrix_bot_baibot_molecule_network.stderr"
|
||||
|
||||
# baibot logs in and then syncs forever; with no homeserver answering it
|
||||
# never gets past login and the unit crash-loops. The shared stub is enough
|
||||
# - see molecule-shared/homeserver-stub.py for what it is not.
|
||||
# baibot logs in and then syncs forever; with no homeserver answering it never gets past
|
||||
# login. The shared stub is enough - see molecule-shared/homeserver-stub.py for what it is not.
|
||||
#
|
||||
# It is told to claim the bot's own MXID, because the bot resolves who it is
|
||||
# from what the homeserver hands back at login, and everything it does
|
||||
# afterwards (its profile, its own-message filtering) hangs off that.
|
||||
# Told to claim the bot's own MXID, because the bot resolves who it is from what the
|
||||
# homeserver hands back at login, and its profile and own-message filtering hang off that.
|
||||
- name: Ensure the homeserver stub is running
|
||||
ansible.builtin.include_tasks:
|
||||
file: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/../../../molecule-shared/tasks/homeserver-stub.yml"
|
||||
|
||||
@@ -12,10 +12,9 @@
|
||||
gather_facts: false
|
||||
|
||||
tasks:
|
||||
# The version is read out of the role's own defaults rather than pinned in
|
||||
# molecule.yml, so that the assertion further down compares the running
|
||||
# image against what defaults/main.yml actually ships. Pinning it here
|
||||
# would make that assertion compare the scenario with itself.
|
||||
# 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"
|
||||
@@ -30,10 +29,9 @@
|
||||
delay: 5
|
||||
failed_when: false
|
||||
|
||||
# `Restart=always` means a crash-looping container still reports `active`,
|
||||
# so the restart counter is checked alongside it. Asserted as `is defined`
|
||||
# too, because `| int` turns a missing property into 0 and would pass
|
||||
# vacuously on a systemd that does not expose it.
|
||||
# `Restart=always` means a crash-looping container still reports `active`, so the restart
|
||||
# counter is checked too. Asserted `is defined` because `| int` turns a missing property
|
||||
# into 0 and would pass vacuously.
|
||||
- name: Assert the service is active and has not been restarting
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
@@ -47,17 +45,15 @@
|
||||
automatic restart(s)
|
||||
success_msg: "matrix-bot-baibot.service is active and has not restarted"
|
||||
|
||||
# baibot publishes no port of its own - it is a Matrix client, not a server -
|
||||
# so what it says about itself has to come from its output. The unit runs
|
||||
# `docker start --attach`, so `--log-driver=none` on the container does not
|
||||
# stop the journal from carrying it.
|
||||
# baibot is a Matrix client, not a server, so what it says about itself has to come from
|
||||
# its output. The unit runs `docker start --attach`, so `--log-driver=none` does not stop
|
||||
# the journal from carrying it.
|
||||
#
|
||||
# `Syncing..` is the line that matters, and it is what carries this scenario
|
||||
# rather than the unit check above. baibot does not exit when its startup
|
||||
# goes wrong: a profile step it cannot complete is retried forever with a
|
||||
# growing delay, so the unit stays `active` with `NRestarts` at 0 while the
|
||||
# bot never reaches its message loop. Point the avatar at a file that is not
|
||||
# there and the assertion above still passes; this one does not.
|
||||
# `Syncing..` is what carries this scenario, not the unit check above. baibot does not exit
|
||||
# when startup goes wrong: a profile step it cannot complete is retried forever with a
|
||||
# growing delay, so the unit stays `active` with `NRestarts` at 0 while the bot never
|
||||
# reaches its message loop. Point the avatar at a missing file and the assertion above
|
||||
# still passes; this one does not.
|
||||
- name: Wait for baibot to reach its sync loop
|
||||
ansible.builtin.shell:
|
||||
cmd: >-
|
||||
@@ -81,10 +77,8 @@
|
||||
retrying profile setup
|
||||
success_msg: "baibot got past startup and is syncing"
|
||||
|
||||
# `user.name` is the bot's display name. The scenario's value is neither the
|
||||
# role's default (`baibot`) nor what the stub reports the account already has
|
||||
# (`stub`), so the bot naming this as what it wants can only have come from
|
||||
# the configuration the role rendered.
|
||||
# The scenario's display name is neither the role's default nor what the stub reports the
|
||||
# account already has, so the bot wanting it can only have come from what the role rendered.
|
||||
- name: Assert the display name the role configured reached the process
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
@@ -96,22 +90,19 @@
|
||||
display name it wants, so `user.name` did not reach the process
|
||||
success_msg: "baibot acts on the display name the role configured"
|
||||
|
||||
# The `logging` setting is one string carrying per-target levels
|
||||
# (`warn,mxlink=info,baibot=debug`), so proving it arrived means proving that
|
||||
# different targets ended up at different levels - a single global level
|
||||
# would satisfy neither half of this.
|
||||
# `logging` is one string carrying per-target levels, so proving it arrived means proving
|
||||
# different targets ended up at different levels. A single global level satisfies neither half.
|
||||
#
|
||||
# First clause: baibot's own records appear at DEBUG, which the role's
|
||||
# default of `info` would not produce.
|
||||
# First clause: baibot's own records appear at DEBUG, which the role's default of `info`
|
||||
# would not produce.
|
||||
#
|
||||
# Second clause is the control, and it is not vacuous: at DEBUG the crates
|
||||
# underneath (matrix-sdk and its spans, hyper, eyeball) are extremely
|
||||
# talkative - raising the catch-all level turns these two records into
|
||||
# roughly a hundred. Their silence is the `warn` catch-all being enforced.
|
||||
# Second clause is the control, and it is not vacuous. At DEBUG the crates underneath are
|
||||
# extremely talkative, so raising the catch-all turns these two records into roughly a
|
||||
# hundred. Their silence is the `warn` catch-all being enforced.
|
||||
#
|
||||
# A control on mxlink was tried first and is the trap here: mxlink happens to
|
||||
# emit no DEBUG records at all on a first run, so asserting their absence
|
||||
# passed just as happily with mxlink set to `debug`.
|
||||
# The trap here: a control on mxlink was tried first, and mxlink emits no DEBUG records at
|
||||
# all on a first run - so asserting their absence passed just as happily with mxlink set
|
||||
# to `debug`.
|
||||
- name: Assert the per-target logging levels reached the process
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
@@ -150,10 +141,8 @@
|
||||
vars:
|
||||
matrix_bot_baibot_config: "{{ matrix_bot_baibot_config_file.content | b64decode | from_yaml }}"
|
||||
|
||||
# The role supports two mutually-exclusive authentication modes and refuses
|
||||
# a configuration that sets both. This scenario uses the password mode, so
|
||||
# the access-token keys must be rendered as nulls rather than omitted or
|
||||
# left with a value.
|
||||
# The role refuses a configuration that sets both authentication modes. This scenario uses
|
||||
# password mode, so the access-token keys must render as nulls, not be omitted or set.
|
||||
- name: Assert only the password authentication mode is rendered
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
@@ -165,14 +154,12 @@
|
||||
vars:
|
||||
matrix_bot_baibot_config: "{{ matrix_bot_baibot_config_file.content | b64decode | from_yaml }}"
|
||||
|
||||
# The agent presets are the most involved templating in this role: a
|
||||
# per-provider template is rendered to YAML, parsed, merged with an
|
||||
# extension, and dropped into the list as a nested structure. This asserts
|
||||
# the whole round trip, key by key.
|
||||
# The agent presets are the most involved templating in this role: a per-provider template
|
||||
# rendered to YAML, parsed, merged with an extension, nested into the list. Asserted as a
|
||||
# whole round trip, key by key.
|
||||
#
|
||||
# No provider is ever contacted. baibot calls one only when a message asks an
|
||||
# agent to do something, and the base URL here resolves nowhere on purpose -
|
||||
# a scenario must not need an account with an AI provider.
|
||||
# No provider is ever contacted. baibot calls one only when a message asks an agent to do
|
||||
# something, and the base URL here resolves nowhere on purpose.
|
||||
- name: Assert the statically-defined agent survived the provider templating
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
@@ -215,10 +202,8 @@
|
||||
{{ matrix_bot_baibot_role_defaults.matrix_bot_baibot_version }}
|
||||
success_msg: "The running container is the version defaults/main.yml pins"
|
||||
|
||||
# The uid/gid come from outside the role (matrix-base supplies them in a real
|
||||
# run, molecule-shared/playbook-context.yml here) and are deliberately not
|
||||
# 1000, which the base image already uses - so this cannot pass by
|
||||
# coincidence with whatever the image would have run as.
|
||||
# The uid/gid come from outside the role and are deliberately not 1000, which the base
|
||||
# image already uses, so this cannot pass by coinciding with the image's own user.
|
||||
- name: Assert the container runs as the identity the playbook supplies
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
@@ -228,10 +213,8 @@
|
||||
({{ matrix_bot_baibot_container.stdout }})
|
||||
success_msg: "The container runs as the uid/gid the playbook supplies"
|
||||
|
||||
# baibot keeps its session and crypto store here. The file existing proves
|
||||
# the bind mount is writable by the user the container runs as - a
|
||||
# read-only-root container whose data directory it could not write would
|
||||
# never have got as far as logging in.
|
||||
# baibot keeps its session and crypto store here. The file existing proves the bind mount
|
||||
# is writable by the user the container runs as.
|
||||
- name: Stat the session file baibot persists
|
||||
ansible.builtin.stat:
|
||||
path: "{{ matrix_bot_baibot_data_path }}/session.json"
|
||||
|
||||
@@ -3,11 +3,9 @@
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
---
|
||||
# The devture base roles carry the variables this role reads
|
||||
# (`devture_systemd_docker_base_*`, `devture_playbook_help_*`), the same way
|
||||
# they do when the playbook runs. `matrix-base` is deliberately NOT included:
|
||||
# it does far more than this role needs, and the variables it would supply come
|
||||
# from molecule-shared/playbook-context.yml instead.
|
||||
# The devture base roles carry the variables this role reads, the same way they do when
|
||||
# the playbook runs. `matrix-base` is deliberately NOT included: it does far more than this
|
||||
# role needs, and what it would supply comes from molecule-shared/playbook-context.yml.
|
||||
- name: Include roles for matrix-reminder-bot Molecule tests
|
||||
hosts: all
|
||||
become: true
|
||||
@@ -26,8 +24,8 @@
|
||||
loop_control:
|
||||
loop_var: role_name
|
||||
|
||||
# The role installs the unit but does not start it - in the playbook that is
|
||||
# `systemd_service_manager`'s job - so the scenario starts it here.
|
||||
# The role installs the unit but does not start it; in the playbook that is
|
||||
# `systemd_service_manager`'s job.
|
||||
- name: Ensure matrix-reminder-bot is started
|
||||
hosts: all
|
||||
become: true
|
||||
|
||||
@@ -29,28 +29,25 @@ provisioner:
|
||||
all:
|
||||
matrix_bot_matrix_reminder_bot_container_network: matrix-reminder-bot-molecule
|
||||
|
||||
# Unlike the bridges, this bot is not an appservice: it logs into the
|
||||
# homeserver as an ordinary user with a password. The stub prepare.yml
|
||||
# stands up answers /_matrix/client/v3/login with an access token, which
|
||||
# is all the bot needs to get past its login and into its sync loop.
|
||||
# Unlike the bridges, this bot is not an appservice: it logs in as an ordinary user
|
||||
# with a password. The stub answers /_matrix/client/v3/login with an access token,
|
||||
# which is all the bot needs to reach its sync loop.
|
||||
matrix_bot_matrix_reminder_bot_matrix_homeserver_url: http://matrix.molecule.local:8008
|
||||
|
||||
# Deliberately different from the role's default localpart
|
||||
# (`bot.matrix-reminder-bot`), so verify.yml can tell what the role
|
||||
# Different from the role's default localpart, so verify.yml can tell what the role
|
||||
# rendered apart from what it would have rendered anyway.
|
||||
matrix_bot_matrix_reminder_bot_matrix_user_id_localpart: molecule.reminder-bot
|
||||
matrix_bot_matrix_reminder_bot_matrix_user_password: molecule_bot_password_4f2a91
|
||||
|
||||
# The role has no default here and refuses to run without one. Also
|
||||
# different from the bot's own fallback (`Etc/UTC`), and it reaches the
|
||||
# container twice - through the config file and through TZ on the unit.
|
||||
# The role has no default here and refuses to run without one. Also different from the
|
||||
# bot's own fallback, and it reaches the container twice: the config file and TZ.
|
||||
matrix_bot_matrix_reminder_bot_reminders_timezone: Europe/Sofia
|
||||
|
||||
# The role and the bot both default to `!`.
|
||||
matrix_bot_matrix_reminder_bot_command_prefix: "%%"
|
||||
|
||||
# Both lists default to off with no entries, so turning them on with
|
||||
# entries of our own exercises the `_auto + _custom` composition.
|
||||
# Both lists default to off with no entries, so turning them on exercises the
|
||||
# `_auto + _custom` composition.
|
||||
matrix_bot_matrix_reminder_bot_allowlist_enabled: true
|
||||
matrix_bot_matrix_reminder_bot_allowlist_regexes_custom:
|
||||
- "@molecule-allowed:molecule.local"
|
||||
@@ -58,22 +55,19 @@ provisioner:
|
||||
matrix_bot_matrix_reminder_bot_blocklist_regexes_custom:
|
||||
- ".*:blocked.molecule.local"
|
||||
|
||||
# The device name is hardcoded in the role's config template, so
|
||||
# overriding it is only possible through the extension mechanism. Doing
|
||||
# it here means the merge of template + extension is tested too.
|
||||
# The device name is hardcoded in the role's template, so overriding it is only
|
||||
# possible through the extension mechanism - which tests that merge too.
|
||||
matrix_bot_matrix_reminder_bot_configuration_extension_yaml: |
|
||||
matrix:
|
||||
device_name: Molecule Reminder Bot
|
||||
|
||||
# The SQLite database path is moved off the role's default (`bot.db`) so
|
||||
# that verify.yml can assert the bot opened the path the role gave it,
|
||||
# with the default name as a negative control.
|
||||
# Moved off the role's default name so verify.yml can assert the bot opened the path
|
||||
# the role gave it, with the default name as a negative control.
|
||||
matrix_bot_matrix_reminder_bot_sqlite_database_path_local: /matrix/matrix-reminder-bot/data/molecule-reminders.db
|
||||
matrix_bot_matrix_reminder_bot_sqlite_database_path_in_container: /data/molecule-reminders.db
|
||||
|
||||
# verify.yml runs as its own play, where the role's defaults are out of
|
||||
# scope, so the paths it reads are pinned here as literals. They match
|
||||
# what the role derives from `matrix_base_data_path`.
|
||||
# verify.yml runs as its own play, where the role's defaults are out of scope,
|
||||
# so the paths it reads are pinned here to match what the role derives.
|
||||
matrix_bot_matrix_reminder_bot_base_path: /matrix/matrix-reminder-bot
|
||||
matrix_bot_matrix_reminder_bot_config_path: /matrix/matrix-reminder-bot/config
|
||||
matrix_bot_matrix_reminder_bot_data_path: /matrix/matrix-reminder-bot/data
|
||||
|
||||
@@ -31,9 +31,8 @@
|
||||
docker_daemon_options:
|
||||
storage-driver: fuse-overlayfs
|
||||
|
||||
# The role's file tasks set owner/group by name, and Ansible resolves those
|
||||
# through the passwd database - so they have to exist before it runs. In a
|
||||
# real deployment `matrix-base` creates them.
|
||||
# The role's file tasks set owner/group by name, which Ansible resolves through the
|
||||
# passwd database, so they have to exist first. `matrix-base` creates them for real.
|
||||
- name: Ensure the matrix group exists
|
||||
ansible.builtin.group:
|
||||
name: "{{ matrix_group_name }}"
|
||||
@@ -57,8 +56,8 @@
|
||||
group: "{{ matrix_group_name }}"
|
||||
mode: "0750"
|
||||
|
||||
# The role creates this network itself during converge, but the homeserver
|
||||
# stub has to be on it before the bot starts, so it is created here first.
|
||||
# The role creates this network itself during converge, but the stub has to be on it
|
||||
# before the bot starts.
|
||||
- name: Ensure the container network the role attaches to exists
|
||||
ansible.builtin.command:
|
||||
argv:
|
||||
@@ -72,11 +71,9 @@
|
||||
- matrix_bot_matrix_reminder_bot_molecule_network.rc != 0
|
||||
- "'already exists' not in matrix_bot_matrix_reminder_bot_molecule_network.stderr"
|
||||
|
||||
# This bot is not an appservice - it logs in with the username and password
|
||||
# the role rendered into its configuration, and retries every 15 seconds
|
||||
# until that succeeds. The shared stub answers the login with an access
|
||||
# token, which is enough to get it into its sync loop. Nothing is asserted
|
||||
# about the stub itself; see molecule-shared/homeserver-stub.py.
|
||||
# Not an appservice: it logs in with the username and password the role rendered, retrying
|
||||
# every 15 seconds until that succeeds. The stub answers with an access token, which is
|
||||
# enough to reach the sync loop. Nothing is asserted about the stub itself.
|
||||
- name: Ensure the homeserver stub is running
|
||||
ansible.builtin.include_tasks:
|
||||
file: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/../../../molecule-shared/tasks/homeserver-stub.yml"
|
||||
|
||||
@@ -3,13 +3,12 @@
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
---
|
||||
# What this proves: matrix-reminder-bot starts on the configuration the role
|
||||
# rendered, logs into a homeserver as the user the role gave it, opens the
|
||||
# database at the path the role gave it, and is the version the role pins.
|
||||
# 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 of its own 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 and never will. See docs/molecule-testing.md.
|
||||
# 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
|
||||
@@ -23,10 +22,9 @@
|
||||
matrix_bot_matrix_reminder_bot_molecule_container_user: "{{ matrix_user_uid }}:{{ matrix_user_gid }}"
|
||||
|
||||
tasks:
|
||||
# The version is read out of the role's own defaults rather than pinned in
|
||||
# molecule.yml, so that the assertion further down compares the running
|
||||
# image against what defaults/main.yml actually ships. Pinning it here
|
||||
# would make that assertion compare the scenario with itself.
|
||||
# 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"
|
||||
@@ -41,12 +39,10 @@
|
||||
delay: 5
|
||||
failed_when: false
|
||||
|
||||
# `Restart=always` means a bot crash-looping on a configuration it cannot
|
||||
# read 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 as `is defined` too, because `| int` turns a missing property
|
||||
# into 0 and would pass vacuously on a systemd that does not expose it.
|
||||
# `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:
|
||||
@@ -60,14 +56,12 @@
|
||||
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`. That is the only thing this bot
|
||||
# reports about itself - it serves nothing over HTTP.
|
||||
# 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: the startup lines are the oldest ones in the
|
||||
# journal, so a `--lines=N` tail would lose them behind anything the bot
|
||||
# logs later, and reading the journal whole would pull an unbounded amount
|
||||
# of text into a variable. The filter keeps the failure line too, so the
|
||||
# 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:
|
||||
@@ -83,10 +77,9 @@
|
||||
delay: 5
|
||||
failed_when: false
|
||||
|
||||
# "Logged in as ..." is only reached after the bot's login call came back as
|
||||
# something other than a LoginError, so this is the whole chain at once: the
|
||||
# homeserver URL, the user ID and the password the role rendered were good
|
||||
# enough for a real login round-trip against the stub.
|
||||
# "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:
|
||||
@@ -99,9 +92,8 @@
|
||||
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 (SQLite here, Postgres otherwise) by
|
||||
# building the connection string the bot parses, and the bot names the type
|
||||
# it settled on once the database is open.
|
||||
# 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:
|
||||
@@ -114,9 +106,8 @@
|
||||
src: "{{ matrix_bot_matrix_reminder_bot_config_path }}/config.yaml"
|
||||
register: matrix_bot_matrix_reminder_bot_config_file
|
||||
|
||||
# Every one of these differs from both the role's defaults and the bot's own
|
||||
# fallbacks, so their presence means the role rendered this file rather than
|
||||
# the values coinciding with what would have happened anyway.
|
||||
# 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:
|
||||
@@ -133,9 +124,8 @@
|
||||
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 config template, so this value can
|
||||
# only be there if `..._configuration_extension_yaml` was merged over the
|
||||
# template rather than ignored.
|
||||
# `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:
|
||||
@@ -148,9 +138,8 @@
|
||||
vars:
|
||||
matrix_bot_matrix_reminder_bot_config_rendered: "{{ matrix_bot_matrix_reminder_bot_config_file.content | b64decode }}"
|
||||
|
||||
# The bot has no HTTP surface, so where its database landed is the evidence
|
||||
# that the storage configuration reached the running process rather than
|
||||
# merely the file on disk.
|
||||
# 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 }}"
|
||||
@@ -168,9 +157,8 @@
|
||||
success_msg: >-
|
||||
The database is at the configured path, owned by {{ matrix_user_uid }}:{{ matrix_user_gid }}
|
||||
|
||||
# A negative control for the assertion above: the role's own default
|
||||
# database name must NOT appear, or a file at the configured path would not
|
||||
# prove the configuration reached the process.
|
||||
# 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"
|
||||
@@ -186,9 +174,8 @@
|
||||
configuration reached the bot
|
||||
success_msg: "Only the configured database path was used"
|
||||
|
||||
# matrix-nio writes its encryption store here once a login has succeeded, so
|
||||
# a populated directory means the bot could use the store path the role
|
||||
# created for it inside an otherwise read-only container.
|
||||
# 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 }}"
|
||||
@@ -216,9 +203,8 @@
|
||||
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 - and the uid/gid come from the playbook
|
||||
# context rather than from anything the image would pick on its own.
|
||||
# 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:
|
||||
|
||||
@@ -29,31 +29,29 @@ provisioner:
|
||||
all:
|
||||
matrix_bridge_heisenbridge_container_network: heisenbridge-molecule
|
||||
|
||||
# The stub prepare.yml stands up. Heisenbridge talks to it while
|
||||
# starting; it is not a real homeserver and nothing is asserted about it.
|
||||
# The stub prepare.yml stands up. Not a real homeserver, and nothing is asserted
|
||||
# about it.
|
||||
matrix_bridge_heisenbridge_homeserver_url: http://matrix.molecule.local:8008
|
||||
|
||||
matrix_bridge_heisenbridge_appservice_token: molecule_as_token_4f2a91
|
||||
|
||||
# Heisenbridge refuses to start without an owner - it is the Matrix user
|
||||
# allowed to administer the bridge.
|
||||
# Heisenbridge refuses to start without an owner: the Matrix user allowed to
|
||||
# administer the bridge.
|
||||
matrix_bridge_heisenbridge_owner: "@molecule-admin:molecule.local"
|
||||
|
||||
# Deliberately different from the role's defaults, so verify.yml can tell
|
||||
# what the role rendered apart from what heisenbridge would have chosen.
|
||||
# Different from the role's defaults, so verify.yml can tell what the role rendered
|
||||
# apart from what heisenbridge would have chosen.
|
||||
matrix_bridge_heisenbridge_path_prefix: /molecule-heisenbridge
|
||||
|
||||
# identd binds host port 113, which would collide with anything else on
|
||||
# the machine and is not what this scenario is proving.
|
||||
# identd binds host port 113, which would collide with anything else on the machine.
|
||||
matrix_bridge_heisenbridge_identd_enabled: false
|
||||
|
||||
# Traefik is not deployed here, so the labels the role would render for
|
||||
# it are switched off and their absence is asserted instead.
|
||||
# Traefik is not deployed here, so the labels the role would render for it are
|
||||
# switched off and their absence is asserted instead.
|
||||
matrix_bridge_heisenbridge_container_labels_traefik_enabled: false
|
||||
|
||||
# verify.yml runs as its own play, where role defaults are out of scope.
|
||||
# Unlike the mautrix bridges, heisenbridge keeps everything directly
|
||||
# under its base path rather than in config/ and data/ subdirectories.
|
||||
# verify.yml runs as its own play, where role defaults are out of scope. Unlike the
|
||||
# mautrix bridges, heisenbridge keeps everything directly under its base path.
|
||||
matrix_bridge_heisenbridge_base_path: /matrix/heisenbridge
|
||||
env:
|
||||
# Workaround for https://github.com/ansible/molecule/issues/4391
|
||||
|
||||
@@ -31,9 +31,8 @@
|
||||
docker_daemon_options:
|
||||
storage-driver: fuse-overlayfs
|
||||
|
||||
# The role's file tasks set owner/group by name and Ansible resolves those
|
||||
# through the passwd database, so they have to exist first. matrix-base
|
||||
# creates them in a real deployment.
|
||||
# The role's file tasks set owner/group by name, which Ansible resolves through the
|
||||
# passwd database, so they have to exist first. `matrix-base` creates them for real.
|
||||
- name: Ensure the matrix group exists
|
||||
ansible.builtin.group:
|
||||
name: "{{ matrix_group_name }}"
|
||||
@@ -70,8 +69,8 @@
|
||||
- heisenbridge_molecule_network.rc != 0
|
||||
- "'already exists' not in heisenbridge_molecule_network.stderr"
|
||||
|
||||
# The bridge contacts the homeserver as it starts. It is not being asked to
|
||||
# bridge anything - see molecule-shared/homeserver-stub.py.
|
||||
# The bridge contacts the homeserver as it starts. It is not being asked to bridge
|
||||
# anything. See molecule-shared/homeserver-stub.py.
|
||||
- name: Ensure the homeserver stub is running
|
||||
ansible.builtin.include_tasks:
|
||||
file: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/../../../molecule-shared/tasks/homeserver-stub.yml"
|
||||
|
||||
@@ -3,9 +3,8 @@
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
---
|
||||
# What this proves: heisenbridge starts, reads the registration the role
|
||||
# rendered, and is the version the role pins. It does NOT connect to IRC and
|
||||
# never will. See docs/molecule-testing.md.
|
||||
# Proves heisenbridge starts, reads the registration the role rendered, and is the version
|
||||
# the role pins. It does NOT connect to IRC. See docs/molecule-testing.md.
|
||||
- name: Verify heisenbridge
|
||||
hosts: all
|
||||
become: true
|
||||
@@ -29,8 +28,8 @@
|
||||
delay: 5
|
||||
failed_when: false
|
||||
|
||||
# `Restart=always` means a bridge crash-looping on a configuration it cannot
|
||||
# read still reports `active`, so the restart counter is checked too.
|
||||
# `Restart=always` means a bridge crash-looping on unreadable config still reports
|
||||
# `active`, so the restart counter is checked too.
|
||||
- name: Assert the service is active and has not been restarting
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
@@ -44,16 +43,14 @@
|
||||
automatic restart(s)
|
||||
success_msg: "matrix-heisenbridge.service is active and has not restarted"
|
||||
|
||||
# Heisenbridge keeps everything directly under its base path rather than in
|
||||
# config/ and data/ subdirectories the way the mautrix bridges do.
|
||||
# Heisenbridge keeps everything directly under its base path, unlike the mautrix bridges.
|
||||
- name: Read the appservice registration the role rendered
|
||||
ansible.builtin.slurp:
|
||||
src: "{{ matrix_bridge_heisenbridge_base_path }}/registration.yaml"
|
||||
register: heisenbridge_registration_file
|
||||
|
||||
# The token and the URL both differ from anything heisenbridge would pick on
|
||||
# its own, so their presence means the role rendered this file rather than
|
||||
# the bridge generating one.
|
||||
# Token and URL both differ from anything heisenbridge would pick on its own, so their
|
||||
# presence means the role rendered this file rather than the bridge generating one.
|
||||
- name: Assert the registration carries the scenario's token and namespace
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
@@ -64,8 +61,7 @@
|
||||
vars:
|
||||
heisenbridge_registration_rendered: "{{ heisenbridge_registration_file.content | b64decode }}"
|
||||
|
||||
# The owner is what heisenbridge is told to accept administration from, and
|
||||
# the role passes it on the command line rather than through a config file,
|
||||
# The role passes the owner on the command line rather than through a config file,
|
||||
# so the unit is where it can be checked.
|
||||
- name: Read the systemd unit the role rendered
|
||||
ansible.builtin.slurp:
|
||||
@@ -82,8 +78,8 @@
|
||||
vars:
|
||||
heisenbridge_unit_rendered: "{{ heisenbridge_unit_file.content | b64decode }}"
|
||||
|
||||
# identd binds host port 113 when enabled, and this scenario turns it off.
|
||||
# Asserting its absence keeps the default from silently becoming "on".
|
||||
# identd binds host port 113 when enabled. Asserting its absence keeps the default from
|
||||
# silently becoming "on".
|
||||
- name: Assert identd is not published while it is disabled
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
|
||||
@@ -29,67 +29,57 @@ provisioner:
|
||||
all:
|
||||
matrix_bridge_hookshot_container_network: hookshot-molecule
|
||||
|
||||
# The stub prepare.yml stands up. Hookshot contacts it while starting;
|
||||
# it is not a real homeserver and nothing is asserted about it.
|
||||
# The stub prepare.yml stands up. Not a real homeserver, and nothing is asserted
|
||||
# about it.
|
||||
matrix_bridge_hookshot_homeserver_address: http://matrix.molecule.local:8008
|
||||
|
||||
# Appservice tokens. These are what Hookshot and the homeserver would
|
||||
# authenticate to each other with; here they only have to reach the
|
||||
# rendered configuration and the registration file.
|
||||
# Here these only have to reach the rendered configuration and the registration.
|
||||
matrix_bridge_hookshot_appservice_token: molecule_as_token_4f2a91
|
||||
matrix_bridge_hookshot_homeserver_token: molecule_hs_token_9b3e77
|
||||
|
||||
# Deliberately different from the role's default (`hookshot`), so the
|
||||
# registration's sender_localpart can only have come from the role.
|
||||
# prepare.yml tells the homeserver stub to claim this same user id.
|
||||
# Different from the role's default, so the registration's sender_localpart can only
|
||||
# have come from the role. prepare.yml tells the stub to claim this same user id.
|
||||
matrix_bridge_hookshot_bot_localpart: molecule-hookshot
|
||||
|
||||
# Hookshot's HTTP surface is the point of this scenario. Every port
|
||||
# below differs from BOTH the role's default and Hookshot's own, so an
|
||||
# answer on one of them can only mean the role's configuration reached
|
||||
# the process. verify.yml also asserts that nothing answers on the
|
||||
# defaults these replace (9993 appservice, 9000 webhooks, 9001 metrics).
|
||||
# Hookshot's HTTP surface is the point of this scenario. Every port below differs
|
||||
# from BOTH the role's default and Hookshot's own, so an answer can only mean the
|
||||
# role's configuration reached the process. verify.yml also asserts nothing answers
|
||||
# on the defaults these replace.
|
||||
matrix_bridge_hookshot_appservice_port: 9772
|
||||
matrix_bridge_hookshot_webhook_port: 9741
|
||||
|
||||
# Off in the role's defaults. Enabling it makes the role render a second
|
||||
# entry in Hookshot's `listeners` list, which is the cheapest listener to
|
||||
# assert *content* on: /metrics answers in a format nothing else would.
|
||||
# Off in the role's defaults. Enabling it renders a second entry in the `listeners`
|
||||
# list, and /metrics is the cheapest listener to assert *content* on.
|
||||
matrix_bridge_hookshot_metrics_enabled: true
|
||||
matrix_bridge_hookshot_metrics_port: 9752
|
||||
|
||||
# On in the role's defaults. Turned off here so that the absence of a
|
||||
# listener can be asserted too - the widgets port staying closed is what
|
||||
# tells "the role rendered the listener list" apart from "Hookshot binds
|
||||
# everything anyway".
|
||||
# On in the role's defaults. Off here so the absence of a listener can be asserted:
|
||||
# the widgets port staying closed is what tells "the role rendered the listener list"
|
||||
# apart from "Hookshot binds everything anyway".
|
||||
matrix_bridge_hookshot_widgets_enabled: false
|
||||
|
||||
# No third-party service is configured in this scenario - see the header
|
||||
# of verify.yml. GitLab is the one the role enables by default, so it is
|
||||
# explicitly switched off and its absence from the rendered config and
|
||||
# registration is asserted.
|
||||
# No third-party service is configured here; see the header of verify.yml.
|
||||
# GitLab is the one the role enables by default, so it is explicitly switched off.
|
||||
matrix_bridge_hookshot_gitlab_enabled: false
|
||||
|
||||
# The generic webhooks listener is the only part of Hookshot that needs
|
||||
# no account anywhere, so it is what this scenario exercises live. The
|
||||
# prefix differs from the role's default (`_webhooks_`).
|
||||
# The generic webhooks listener needs no account anywhere, so it is what this
|
||||
# scenario exercises live. The prefix differs from the role's default.
|
||||
matrix_bridge_hookshot_generic_userIdPrefix: _molecule_hook_ # noqa var-naming
|
||||
|
||||
# Neither the role's default (600) nor Hookshot's own (600).
|
||||
matrix_bridge_hookshot_feeds_pollIntervalSeconds: 907 # noqa var-naming
|
||||
|
||||
# The role defaults to `warn`; Hookshot itself defaults to `info`. This
|
||||
# is a third value, so finding it in config.yml cannot be a coincidence.
|
||||
# The role defaults to `warn`, Hookshot itself to `info`. A third value, so finding
|
||||
# it in config.yml cannot be a coincidence.
|
||||
matrix_bridge_hookshot_logging_level: debug
|
||||
|
||||
# Traefik is not deployed here, so the labels the role would render for
|
||||
# it are switched off and their absence is asserted instead.
|
||||
# Traefik is not deployed here, so the labels the role would render for it are
|
||||
# switched off and their absence is asserted instead.
|
||||
matrix_bridge_hookshot_container_labels_traefik_enabled: false
|
||||
|
||||
# verify.yml runs as its own play, where role defaults are out of scope,
|
||||
# so what it reads is pinned here. These two match the role's own
|
||||
# defaults on purpose - they name things (a path, a container) rather
|
||||
# than configure them, and nothing is asserted *about* them.
|
||||
# verify.yml runs as its own play, where role defaults are out of scope, so what it
|
||||
# reads is pinned here. These two match the role's own defaults on purpose: they name
|
||||
# things rather than configure them, and nothing is asserted *about* them.
|
||||
matrix_bridge_hookshot_base_path: /matrix/hookshot
|
||||
matrix_bridge_hookshot_identifier: matrix-hookshot
|
||||
env:
|
||||
|
||||
@@ -31,9 +31,8 @@
|
||||
docker_daemon_options:
|
||||
storage-driver: fuse-overlayfs
|
||||
|
||||
# The role's file tasks set owner/group by name and Ansible resolves those
|
||||
# through the passwd database, so they have to exist first. matrix-base
|
||||
# creates them in a real deployment.
|
||||
# The role's file tasks set owner/group by name, which Ansible resolves through the
|
||||
# passwd database, so they have to exist first. `matrix-base` creates them for real.
|
||||
- name: Ensure the matrix group exists
|
||||
ansible.builtin.group:
|
||||
name: "{{ matrix_group_name }}"
|
||||
|
||||
@@ -3,15 +3,14 @@
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
---
|
||||
# What this proves: Hookshot starts, accepts the config.yml and registration.yml
|
||||
# the role rendered, and opens exactly the HTTP listeners that configuration
|
||||
# described - on the ports the role put there, and not on the ones it did not.
|
||||
# Proves Hookshot starts on the config.yml and registration.yml the role rendered,
|
||||
# and opens exactly the HTTP listeners that configuration described - on the ports
|
||||
# the role put there, and not on the ones it did not.
|
||||
#
|
||||
# What it deliberately does NOT do: configure GitHub, GitLab, Jira or Figma.
|
||||
# Every one of those needs an account and a credential on a third-party service,
|
||||
# which is the line where a scenario stops testing this repository and starts
|
||||
# testing a fake (see docs/molecule-testing.md). The generic webhooks listener
|
||||
# needs no credential from anyone, so it is the one that gets exercised live.
|
||||
# Deliberately does NOT configure GitHub, GitLab, Jira or Figma. Each needs an account
|
||||
# and a credential on a third-party service, which is where a scenario stops testing this
|
||||
# repository and starts testing a fake (see docs/molecule-testing.md). The generic webhooks
|
||||
# listener needs no credential from anyone, so it is the one exercised live.
|
||||
- name: Verify hookshot
|
||||
hosts: all
|
||||
become: true
|
||||
@@ -21,11 +20,10 @@
|
||||
gather_facts: false
|
||||
|
||||
tasks:
|
||||
# Read from the role's own defaults rather than pinned in molecule.yml, so
|
||||
# the version assertion below compares the running image against what the
|
||||
# role ships instead of against the scenario itself. The default ports come
|
||||
# from here for the same reason: the "these ports stay closed" assertion is
|
||||
# only meaningful against the ports the role would otherwise have used.
|
||||
# From the role's own defaults rather than pinned in molecule.yml, so the version
|
||||
# assertion compares the running image against what the role ships, not against the
|
||||
# scenario. The default ports come from here for the same reason: "these ports stay
|
||||
# closed" is only meaningful against the ports the role would otherwise have used.
|
||||
- name: Load the role's defaults under a separate name
|
||||
ansible.builtin.include_vars:
|
||||
file: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/defaults/main.yml"
|
||||
@@ -40,10 +38,9 @@
|
||||
delay: 5
|
||||
failed_when: false
|
||||
|
||||
# `Restart=always` means a bridge crash-looping on a configuration it cannot
|
||||
# read still reports `active`, so the restart counter is checked too. It is
|
||||
# asserted `is defined` because `| int` turns a missing property into 0 and
|
||||
# would pass vacuously.
|
||||
# `Restart=always` means a bridge crash-looping on unreadable config still reports
|
||||
# `active`, so the restart counter is checked too. Asserted `is defined` because
|
||||
# `| int` turns a missing property into 0 and would pass vacuously.
|
||||
- name: Assert the service is active and has not been restarting
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
@@ -70,9 +67,8 @@
|
||||
ansible.builtin.set_fact:
|
||||
hookshot_config: "{{ hookshot_config_file.content | b64decode | from_yaml }}"
|
||||
|
||||
# Each of these differs from what Hookshot would use on its own AND from
|
||||
# what the role defaults to, so finding them here means the role's
|
||||
# configuration is what Hookshot is running on rather than a coincidence.
|
||||
# Each differs from what Hookshot would use on its own AND from the role's defaults,
|
||||
# so finding them here rules out a coincidence.
|
||||
- name: Assert the rendered configuration carries this scenario's values
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
@@ -86,10 +82,9 @@
|
||||
fail_msg: "The rendered configuration does not carry the scenario's values"
|
||||
success_msg: "The rendered configuration carries the scenario's values"
|
||||
|
||||
# Hookshot's `listeners` list is the role's own construction: it decides
|
||||
# which resources get a port at all, from a handful of independent switches.
|
||||
# Getting this wrong is invisible in a "did it start" test, which is why it
|
||||
# is asserted as a whole rather than key by key.
|
||||
# The `listeners` list is the role's own construction, assembled from a handful of
|
||||
# independent switches. Getting it wrong is invisible in a "did it start" test,
|
||||
# hence asserting the whole shape rather than key by key.
|
||||
- name: Assert the role rendered exactly the listeners the scenario asked for
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
@@ -103,9 +98,8 @@
|
||||
vars:
|
||||
hookshot_listener_ports: "{{ hookshot_config.listeners | map(attribute='port') | map('int') | list }}"
|
||||
|
||||
# No third-party service is configured here, so none of their sections may
|
||||
# appear. GitLab is the interesting one: the role turns it ON by default, so
|
||||
# its absence is what proves the scenario's switch reached the template.
|
||||
# GitLab is the interesting one: the role turns it ON by default, so its absence
|
||||
# proves the scenario's switch reached the template.
|
||||
- name: Assert no third-party service section was rendered
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
@@ -131,9 +125,8 @@
|
||||
# The rendered registration
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
# The registration file is the half of the appservice handshake the
|
||||
# homeserver reads. The role generates it, Hookshot only consumes it, so it
|
||||
# is worth checking on its own.
|
||||
# The role generates the registration; Hookshot only consumes it. Worth checking
|
||||
# on its own, as it is the half of the handshake the homeserver reads.
|
||||
- name: Read the appservice registration the role rendered
|
||||
ansible.builtin.slurp:
|
||||
src: "{{ matrix_bridge_hookshot_base_path }}/registration.yml"
|
||||
@@ -143,9 +136,8 @@
|
||||
ansible.builtin.set_fact:
|
||||
hookshot_registration: "{{ hookshot_registration_file.content | b64decode | from_yaml }}"
|
||||
|
||||
# `url` is where the homeserver would push transactions, and the role builds
|
||||
# it out of the container name and the appservice port. It has to agree with
|
||||
# `bridge.port` in config.yml or the two halves would silently disagree.
|
||||
# `url` is built from the container name and the appservice port. It has to agree
|
||||
# with `bridge.port` in config.yml, or the two halves silently disagree.
|
||||
- name: Assert the registration carries the scenario's tokens, bot and callback URL
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
@@ -156,9 +148,9 @@
|
||||
fail_msg: "The appservice registration does not carry the scenario's tokens, bot and callback URL"
|
||||
success_msg: "The appservice registration carries the scenario's tokens, bot and callback URL"
|
||||
|
||||
# The user namespace is derived from the generic webhook prefix, and the
|
||||
# GitLab namespace is conditional on the service being enabled - so this
|
||||
# checks that the two switches reach the registration, not just config.yml.
|
||||
# The user namespace derives from the generic webhook prefix, and the GitLab namespace
|
||||
# is conditional on that service being enabled. Checks both switches reach the
|
||||
# registration, not just config.yml.
|
||||
- name: Assert the registration namespaces follow the enabled services
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
@@ -176,8 +168,8 @@
|
||||
# The listeners, live
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
# A helper container is used because the role publishes no host port, exactly
|
||||
# as in a real deployment; see docs/molecule-testing.md.
|
||||
# A helper container, because the role publishes no host port - exactly as in a real
|
||||
# deployment. See docs/molecule-testing.md.
|
||||
- name: Wait for the webhooks listener to answer on the port the role configured
|
||||
ansible.builtin.command:
|
||||
argv:
|
||||
@@ -205,11 +197,10 @@
|
||||
delay: 5
|
||||
failed_when: false
|
||||
|
||||
# Hookshot answers an unknown webhook id from its generic-webhook handler,
|
||||
# with a JSON body no other component would produce. An Express "Cannot POST"
|
||||
# page here would mean the port is Hookshot's but the generic webhooks
|
||||
# service was never mounted on it; a refused connection would mean the
|
||||
# listener the role described was never opened at all.
|
||||
# An unknown webhook id draws a JSON body from the generic-webhook handler that no
|
||||
# other component would produce. An Express "Cannot POST" page would mean the port is
|
||||
# Hookshot's but the generic webhooks service was never mounted on it; a refused
|
||||
# connection would mean the listener was never opened at all.
|
||||
- name: Assert the generic webhooks service is mounted on that listener
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
@@ -222,9 +213,8 @@
|
||||
({{ hookshot_webhooks_probe.stdout | default('no output') }})
|
||||
success_msg: "The generic webhooks service answers on the port the role configured"
|
||||
|
||||
# Metrics are OFF in the role's defaults, so this listener exists only
|
||||
# because the scenario asked for it - and /metrics answers in a format
|
||||
# nothing else on that port could have produced.
|
||||
# Metrics are OFF in the role's defaults, so this listener exists only because the
|
||||
# scenario asked for it.
|
||||
- name: Probe the metrics listener on the port the role configured
|
||||
ansible.builtin.command:
|
||||
argv:
|
||||
@@ -253,8 +243,8 @@
|
||||
metrics ({{ hookshot_metrics_probe.stdout | default('no output') | truncate(200) }})
|
||||
success_msg: "The metrics listener serves Hookshot's own metrics"
|
||||
|
||||
# The appservice port is not in `listeners` - it comes from `bridge.port` -
|
||||
# so it is a separate socket, opened by a separate part of the config.
|
||||
# The appservice port is not in `listeners`; it comes from `bridge.port`.
|
||||
# A separate socket, opened by a separate part of the config.
|
||||
- name: Probe the appservice port the role configured
|
||||
ansible.builtin.command:
|
||||
argv:
|
||||
@@ -284,10 +274,10 @@
|
||||
({{ hookshot_appservice_probe.stdout | default('no output') }})
|
||||
success_msg: "The appservice API answers on the port the role configured"
|
||||
|
||||
# The other half of the story. Every port above is one the scenario chose;
|
||||
# these are the ones the role and Hookshot would have used if the scenario's
|
||||
# configuration had never reached the process. If any of them answers, then
|
||||
# a passing probe above proves much less than it looks like it does.
|
||||
# The other half of the story. Every port above is one the scenario chose; these are
|
||||
# the ones the role and Hookshot would have used had the scenario's configuration never
|
||||
# reached the process. If any of them answers, the probes above prove much less than
|
||||
# they appear to.
|
||||
- name: Probe the ports the role's defaults would have used
|
||||
ansible.builtin.command:
|
||||
argv:
|
||||
|
||||
@@ -29,51 +29,41 @@ provisioner:
|
||||
all:
|
||||
matrix_bridge_mautrix_discord_container_network: mautrix-discord-molecule
|
||||
|
||||
# The homeserver stub prepare.yml stands up. The bridge contacts it
|
||||
# while starting; it is not a real homeserver and nothing is asserted
|
||||
# about it. There is deliberately no Discord on the other side either -
|
||||
# see docs/molecule-testing.md.
|
||||
# The stub prepare.yml stands up. Not a real homeserver, and nothing is asserted
|
||||
# about it. There is deliberately no Discord on the other side either.
|
||||
matrix_bridge_mautrix_discord_homeserver_address: http://matrix.molecule.local:8008
|
||||
|
||||
# sqlite keeps the scenario to one container. The role only requires a
|
||||
# database hostname when the engine is postgres, and testing which
|
||||
# database engine the bridge can talk to is not what this proves.
|
||||
# sqlite keeps the scenario to one container. Which database engine the bridge can
|
||||
# talk to is not what this proves.
|
||||
matrix_bridge_mautrix_discord_database_engine: sqlite
|
||||
|
||||
# Appservice tokens. These are what the bridge and homeserver would
|
||||
# authenticate to each other with; here they only have to reach the
|
||||
# rendered configuration and the registration file.
|
||||
# Here these only have to reach the rendered configuration and the registration.
|
||||
matrix_bridge_mautrix_discord_appservice_token: molecule_as_token_d15c07
|
||||
matrix_bridge_mautrix_discord_homeserver_token: molecule_hs_token_a4e2b8
|
||||
|
||||
# Deliberately different from the role's defaults, so verify.yml can
|
||||
# tell what the role rendered apart from what the bridge would have
|
||||
# defaulted to on its own.
|
||||
# Different from the role's defaults, so verify.yml can tell what the role rendered
|
||||
# apart from what the bridge would have chosen.
|
||||
matrix_bridge_mautrix_discord_appservice_bot_username: molecule-discordbot
|
||||
matrix_bridge_mautrix_discord_homeserver_domain: molecule.local
|
||||
matrix_bridge_mautrix_discord_bridge_command_prefix: "!molecule-discord"
|
||||
# The role defaults to `warn`; the bridge's own shipped configuration
|
||||
# uses `debug`. `info` is neither.
|
||||
# The role defaults to `warn`, the bridge's own shipped configuration to `debug`.
|
||||
# `info` is neither.
|
||||
matrix_bridge_mautrix_discord_logging_level: info
|
||||
|
||||
# Unlike most bridge roles here, mautrix-discord *requires* a public
|
||||
# address: `validate_config.yml` fails without
|
||||
# `matrix_bridge_mautrix_discord_bridge_public_address`, which is
|
||||
# derived from these three. Discord fetches avatars over it in relay
|
||||
# mode; nothing reaches it in this scenario, but it has to be set for
|
||||
# the role to run at all.
|
||||
# Unlike most bridge roles here, mautrix-discord *requires* a public address:
|
||||
# `validate_config.yml` fails without one, and it is derived from these three.
|
||||
# Discord fetches avatars over it in relay mode. Nothing reaches it in this scenario,
|
||||
# but it has to be set for the role to run at all.
|
||||
#
|
||||
# A non-`/` path prefix and a non-default scheme are chosen so the
|
||||
# avatar-proxy labels verify.yml reads can only look the way they do if
|
||||
# the role composed them from these values.
|
||||
# A non-`/` path prefix and a non-default scheme are chosen so the avatar-proxy labels
|
||||
# verify.yml reads can only look the way they do if the role composed them.
|
||||
matrix_bridge_mautrix_discord_hostname: discord.molecule.local
|
||||
matrix_bridge_mautrix_discord_path_prefix: /discord-bridge
|
||||
matrix_bridge_mautrix_discord_scheme: http
|
||||
matrix_bridge_mautrix_discord_bridge_avatar_proxy_key: molecule_avatar_proxy_key_7c1d
|
||||
|
||||
# verify.yml runs as its own play, where role defaults are out of scope,
|
||||
# so the paths it reads are pinned here as literals matching what the
|
||||
# role derives from matrix_base_data_path.
|
||||
# verify.yml runs as its own play, where the role's defaults are out of scope,
|
||||
# so the paths it reads are pinned here to match what the role derives.
|
||||
matrix_bridge_mautrix_discord_base_path: /matrix/mautrix-discord
|
||||
matrix_bridge_mautrix_discord_config_path: /matrix/mautrix-discord/config
|
||||
matrix_bridge_mautrix_discord_data_path: /matrix/mautrix-discord/data
|
||||
|
||||
@@ -31,9 +31,8 @@
|
||||
docker_daemon_options:
|
||||
storage-driver: fuse-overlayfs
|
||||
|
||||
# The role's file tasks set owner/group by name and Ansible resolves those
|
||||
# through the passwd database, so they have to exist first. matrix-base
|
||||
# creates them in a real deployment.
|
||||
# The role's file tasks set owner/group by name, which Ansible resolves through the
|
||||
# passwd database, so they have to exist first. `matrix-base` creates them for real.
|
||||
- name: Ensure the matrix group exists
|
||||
ansible.builtin.group:
|
||||
name: "{{ matrix_group_name }}"
|
||||
@@ -70,9 +69,8 @@
|
||||
- mautrix_discord_molecule_network.rc != 0
|
||||
- "'already exists' not in mautrix_discord_molecule_network.stderr"
|
||||
|
||||
# The bridge contacts the homeserver as it starts, and refuses to run if
|
||||
# /whoami does not name the bot user it was configured as - see
|
||||
# molecule-shared/homeserver-stub.py.
|
||||
# The bridge contacts the homeserver as it starts and refuses to run if /whoami does not
|
||||
# name the bot user it was configured as. See molecule-shared/homeserver-stub.py.
|
||||
- name: Ensure the homeserver stub is running
|
||||
ansible.builtin.include_tasks:
|
||||
file: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/../../../molecule-shared/tasks/homeserver-stub.yml"
|
||||
|
||||
@@ -3,13 +3,12 @@
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
---
|
||||
# What this proves: the bridge starts, reads the configuration and registration
|
||||
# the role rendered, opens its appservice port, and is the version the role
|
||||
# pins. It also proves the role composed the avatar-proxy labels out of the
|
||||
# hostname, scheme and path prefix it was given. It does NOT bridge anything -
|
||||
# there is no Discord on the other side and there is deliberately never going to
|
||||
# be one, because that would need a Discord account. See
|
||||
# docs/molecule-testing.md.
|
||||
# Proves the bridge starts, reads the configuration and registration the role rendered, opens
|
||||
# its appservice port, is the version the role pins, and composed the avatar-proxy labels out
|
||||
# of the hostname, scheme and path prefix it was given.
|
||||
#
|
||||
# It does NOT bridge anything: there is no Discord on the other side, and deliberately never
|
||||
# will be, because that would need a Discord account. See docs/molecule-testing.md.
|
||||
- name: Verify mautrix-discord
|
||||
hosts: all
|
||||
become: true
|
||||
@@ -19,17 +18,15 @@
|
||||
gather_facts: false
|
||||
|
||||
vars:
|
||||
# The role derives this from scheme + hostname + path prefix, and role
|
||||
# defaults are out of scope in this play, so it is recomposed here from the
|
||||
# same three values the scenario pinned in molecule.yml.
|
||||
# The role derives this from scheme + hostname + path prefix. Role defaults are out of
|
||||
# scope in this play, so it is recomposed from the same three values molecule.yml pinned.
|
||||
mautrix_discord_expected_public_address: >-
|
||||
{{ matrix_bridge_mautrix_discord_scheme }}://{{ matrix_bridge_mautrix_discord_hostname }}{{ matrix_bridge_mautrix_discord_path_prefix }}
|
||||
mautrix_discord_expected_avatar_proxy_path_prefix: "{{ matrix_bridge_mautrix_discord_path_prefix }}/mautrix-discord/avatar"
|
||||
|
||||
tasks:
|
||||
# Read from the role's own defaults rather than pinned in molecule.yml, so
|
||||
# the version assertion below compares the running image against what the
|
||||
# role ships instead of against the scenario itself.
|
||||
# From the role's own defaults rather than pinned in molecule.yml, so the version
|
||||
# assertion compares the running image against what the role ships, not the scenario.
|
||||
- name: Load the role's defaults under a separate name
|
||||
ansible.builtin.include_vars:
|
||||
file: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/defaults/main.yml"
|
||||
@@ -44,10 +41,9 @@
|
||||
delay: 5
|
||||
failed_when: false
|
||||
|
||||
# `Restart=always` means a bridge crash-looping on a configuration it cannot
|
||||
# read still reports `active`, so the restart counter is checked too. It is
|
||||
# asserted `is defined` because `| int` turns a missing property into 0 and
|
||||
# would pass vacuously.
|
||||
# `Restart=always` means a bridge crash-looping on unreadable config still reports
|
||||
# `active`, so the restart counter is checked too. Asserted `is defined` because
|
||||
# `| int` turns a missing property into 0 and would pass vacuously.
|
||||
- name: Assert the service is active and has not been restarting
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
@@ -61,9 +57,8 @@
|
||||
automatic restart(s)
|
||||
success_msg: "matrix-mautrix-discord.service is active and has not restarted"
|
||||
|
||||
# The appservice port is the bridge's own listener, the one a homeserver
|
||||
# would push transactions to. It opening at all means the bridge got through
|
||||
# reading its configuration and setting itself up.
|
||||
# The appservice listener is where a homeserver would push transactions. It opening at all
|
||||
# means the bridge got through reading its configuration and setting itself up.
|
||||
- name: Wait for the bridge to open its appservice port
|
||||
ansible.builtin.command:
|
||||
argv:
|
||||
@@ -99,13 +94,12 @@
|
||||
src: "{{ matrix_bridge_mautrix_discord_config_path }}/config.yaml"
|
||||
register: mautrix_discord_config_file
|
||||
|
||||
# Each of these differs from what the bridge would use on its own, so their
|
||||
# presence means the role's configuration is what the bridge is running on
|
||||
# rather than something that happened to agree with it. The public address
|
||||
# in particular is composed by the role out of three separate variables.
|
||||
# Each differs from what the bridge would use on its own, so their presence rules out a
|
||||
# coincidence. The public address in particular is composed by the role out of three
|
||||
# separate variables.
|
||||
#
|
||||
# Asserted against the parsed document rather than by substring, so a value
|
||||
# landing under the wrong key cannot pass.
|
||||
# Asserted against the parsed document rather than by substring, so a value landing under
|
||||
# the wrong key cannot pass.
|
||||
- name: Assert the rendered configuration carries this scenario's values
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
@@ -124,10 +118,8 @@
|
||||
vars:
|
||||
mautrix_discord_config: "{{ mautrix_discord_config_file.content | b64decode | from_yaml }}"
|
||||
|
||||
# The registration file is the half of the appservice handshake the
|
||||
# homeserver reads, and it is generated by the role rather than by the
|
||||
# bridge, so it is worth checking on its own. `sender_localpart` is the
|
||||
# role's own `_bot_` prefixing, not something the bridge would produce.
|
||||
# The role generates the registration; the bridge only consumes it. `sender_localpart`
|
||||
# carries the role's own `_bot_` prefixing, not something the bridge would produce.
|
||||
- name: Read the appservice registration the role rendered
|
||||
ansible.builtin.slurp:
|
||||
src: "{{ matrix_bridge_mautrix_discord_config_path }}/registration.yaml"
|
||||
@@ -147,9 +139,8 @@
|
||||
mautrix_discord_registration: "{{ mautrix_discord_registration_file.content | b64decode | from_yaml }}"
|
||||
mautrix_discord_bot_user_regex: "^@{{ matrix_bridge_mautrix_discord_appservice_bot_username | regex_escape }}:{{ matrix_bridge_mautrix_discord_homeserver_domain | regex_escape }}$"
|
||||
|
||||
# sqlite was chosen in molecule.yml, so the bridge should have created its
|
||||
# database under the role's data path. This is the cheap proof that the data
|
||||
# path reached the process and is writable by the uid the role runs it as.
|
||||
# Cheap proof that the data path reached the process and is writable by the uid the role
|
||||
# runs it as.
|
||||
- name: Look for the bridge's sqlite database under the role's data path
|
||||
ansible.builtin.stat:
|
||||
path: "{{ matrix_bridge_mautrix_discord_data_path }}/mautrix-discord.db"
|
||||
@@ -188,9 +179,9 @@
|
||||
{{ mautrix_discord_role_defaults.matrix_bridge_mautrix_discord_version }}
|
||||
success_msg: "The running container is the version defaults/main.yml pins"
|
||||
|
||||
# The avatar proxy is this role's own reverse-proxy wiring: the labels only
|
||||
# appear because a public address was configured, and their hostname and
|
||||
# path prefix are composed by the role rather than copied from a variable.
|
||||
# The avatar proxy is this role's own reverse-proxy wiring. The labels only appear because
|
||||
# a public address was configured, and the role composes their hostname and path prefix
|
||||
# rather than copying them from a variable.
|
||||
- name: Read the labels the role rendered
|
||||
ansible.builtin.slurp:
|
||||
src: "{{ matrix_bridge_mautrix_discord_base_path }}/labels"
|
||||
@@ -211,10 +202,9 @@
|
||||
vars:
|
||||
mautrix_discord_labels_rendered: "{{ mautrix_discord_labels.content | b64decode }}"
|
||||
|
||||
# The label file is fed to `docker create --label-file`, so a label the role
|
||||
# renders wrongly is not merely cosmetic - it would stop the container from
|
||||
# being created at all. Reading them back off the running container proves
|
||||
# Docker accepted them.
|
||||
# The label file is fed to `docker create --label-file`, so a wrongly rendered label is
|
||||
# not cosmetic: it stops the container being created at all. Reading them back off the
|
||||
# running container proves Docker accepted them.
|
||||
- name: Read the labels Docker attached to the running container
|
||||
ansible.builtin.command:
|
||||
argv:
|
||||
|
||||
@@ -29,66 +29,51 @@ provisioner:
|
||||
all:
|
||||
matrix_bridge_mautrix_meta_messenger_container_network: mautrix-meta-messenger-molecule
|
||||
|
||||
# The homeserver stub prepare.yml stands up. The bridge contacts it
|
||||
# while starting; it is not a real homeserver and nothing is asserted
|
||||
# The stub prepare.yml stands up. Not a real homeserver, and nothing is asserted
|
||||
# about it.
|
||||
matrix_bridge_mautrix_meta_messenger_homeserver_address: http://matrix.molecule.local:8008
|
||||
matrix_bridge_mautrix_meta_messenger_homeserver_domain: molecule.local
|
||||
|
||||
# sqlite keeps the scenario to one container. The role only requires a
|
||||
# database hostname when the engine is postgres, and testing which
|
||||
# database engine the bridge can talk to is not what this proves.
|
||||
# sqlite keeps the scenario to one container. Which database engine the bridge can
|
||||
# talk to is not what this proves.
|
||||
matrix_bridge_mautrix_meta_messenger_database_engine: sqlite3-fk-wal
|
||||
|
||||
# The URI the role derives for `sqlite3-fk-wal` is deliberately NOT
|
||||
# overridden here. It used to build `sqlite:///` + the in-container path,
|
||||
# which go-sqlite3 takes as a plain filename rather than parsing as a URL,
|
||||
# so the bridge died at startup with `unable to open database file`. This
|
||||
# scenario is what caught it. Leaving the role's own value in place is
|
||||
# what keeps it caught.
|
||||
# The URI the role derives is deliberately NOT overridden here. It used to build
|
||||
# `sqlite:///` + the in-container path, which go-sqlite3 takes as a plain filename
|
||||
# rather than parsing as a URL, so the bridge died at startup. This scenario caught
|
||||
# that, and leaving the role's own value in place is what keeps it caught.
|
||||
|
||||
# Appservice tokens. These are what the bridge and homeserver would
|
||||
# authenticate to each other with; here they only have to reach the
|
||||
# rendered configuration and the registration file.
|
||||
# Here these only have to reach the rendered configuration and the registration.
|
||||
matrix_bridge_mautrix_meta_messenger_appservice_token: molecule_meta_as_token_5c81de
|
||||
matrix_bridge_mautrix_meta_messenger_homeserver_token: molecule_meta_hs_token_a70f24
|
||||
|
||||
# The one variable that makes this role family unusual: a single
|
||||
# upstream codebase serves several Meta networks, and this variable is
|
||||
# what picks which one. It reaches the rendered configuration in several
|
||||
# places at once - the appservice id, the ghost username prefix, the bot
|
||||
# displayname and the bridge's `tor` switch - so a value other than the
|
||||
# role's default `messenger` is what tells verify.yml that the role
|
||||
# propagated the choice rather than everything merely agreeing by
|
||||
# accident. `facebook-tor` is the only one of the three modes that also
|
||||
# flips a boolean in the configuration, which is why it is the one used.
|
||||
# What makes this role family unusual: one upstream codebase serves several Meta
|
||||
# networks, and this variable picks which. It reaches the rendered configuration in
|
||||
# four places at once - appservice id, ghost username prefix, bot displayname and the
|
||||
# bridge's `tor` switch - so a value other than the role's default is what tells
|
||||
# verify.yml the role propagated the choice rather than everything agreeing by accident.
|
||||
#
|
||||
# Nothing logs in during the scenario, so the bridge never opens a
|
||||
# connection to Meta (over Tor or otherwise). See docs/molecule-testing.md
|
||||
# for why a scenario stops short of that.
|
||||
# `facebook-tor` is used because it is the only mode that also flips a boolean.
|
||||
# Nothing logs in during the scenario, so the bridge never connects to Meta.
|
||||
matrix_bridge_mautrix_meta_messenger_meta_mode: facebook-tor
|
||||
|
||||
# Deliberately different from the role's defaults (`messengerbot`,
|
||||
# `!fb`, `(FB)`, `warn`) and from what the bridge would pick on its own,
|
||||
# so verify.yml can tell what the role rendered apart from a
|
||||
# coincidence.
|
||||
# Different from the role's defaults and from what the bridge would pick on its own,
|
||||
# so verify.yml can tell what the role rendered apart from a coincidence.
|
||||
matrix_bridge_mautrix_meta_messenger_appservice_username: molecule-metabot
|
||||
matrix_bridge_mautrix_meta_messenger_bridge_command_prefix: "!molecule-meta"
|
||||
matrix_bridge_mautrix_meta_messenger_bridge_displayname_suffix: "(Molecule)"
|
||||
matrix_bridge_mautrix_meta_messenger_logging_min_level: debug
|
||||
|
||||
# The bridge's HTTP API exposure. Traefik is not deployed here, so
|
||||
# nothing routes to it; what is being tested is that the role turns
|
||||
# these three variables into both the container's Traefik labels and the
|
||||
# Traefik is not deployed here, so nothing routes to it. What is tested is that the
|
||||
# role turns these three variables into both the container's Traefik labels and the
|
||||
# `appservice.public_address` the bridge itself reads.
|
||||
matrix_bridge_mautrix_meta_messenger_exposure_enabled: true
|
||||
matrix_bridge_mautrix_meta_messenger_exposure_hostname: bridges.molecule.local
|
||||
matrix_bridge_mautrix_meta_messenger_exposure_path_prefix: /bridges/meta-messenger
|
||||
matrix_bridge_mautrix_meta_messenger_scheme: https
|
||||
|
||||
# verify.yml runs as its own play, where role defaults are out of scope,
|
||||
# so the paths it reads are pinned here as literals matching what the
|
||||
# role derives from matrix_base_data_path.
|
||||
# verify.yml runs as its own play, where the role's defaults are out of scope,
|
||||
# so the paths it reads are pinned here to match what the role derives.
|
||||
matrix_bridge_mautrix_meta_messenger_base_path: /matrix/mautrix-meta-messenger
|
||||
matrix_bridge_mautrix_meta_messenger_config_path: /matrix/mautrix-meta-messenger/config
|
||||
matrix_bridge_mautrix_meta_messenger_data_path: /matrix/mautrix-meta-messenger/data
|
||||
|
||||
@@ -31,9 +31,8 @@
|
||||
docker_daemon_options:
|
||||
storage-driver: fuse-overlayfs
|
||||
|
||||
# The role's file tasks set owner/group by name and Ansible resolves those
|
||||
# through the passwd database, so they have to exist first. matrix-base
|
||||
# creates them in a real deployment.
|
||||
# The role's file tasks set owner/group by name, which Ansible resolves through the
|
||||
# passwd database, so they have to exist first. `matrix-base` creates them for real.
|
||||
- name: Ensure the matrix group exists
|
||||
ansible.builtin.group:
|
||||
name: "{{ matrix_group_name }}"
|
||||
@@ -72,11 +71,9 @@
|
||||
- mautrix_meta_messenger_molecule_network.rc != 0
|
||||
- "'already exists' not in mautrix_meta_messenger_molecule_network.stderr"
|
||||
|
||||
# The bridge contacts the homeserver as it starts, and calls /whoami before
|
||||
# it will run at all - it exits if the id it gets back is not the bot user
|
||||
# it was configured as. It is not being asked to bridge anything; there is
|
||||
# no Meta account anywhere in this scenario and there is deliberately never
|
||||
# going to be one. See molecule-shared/homeserver-stub.py.
|
||||
# The bridge calls /whoami before it will run at all, exiting if the id it gets back is
|
||||
# not the bot user it was configured as. It is not being asked to bridge anything: there
|
||||
# is no Meta account in this scenario, and deliberately never will be.
|
||||
- name: Ensure the homeserver stub is running
|
||||
ansible.builtin.include_tasks:
|
||||
file: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/../../../molecule-shared/tasks/homeserver-stub.yml"
|
||||
|
||||
@@ -3,29 +3,24 @@
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
---
|
||||
# What this proves: the bridge starts, reads the configuration and registration
|
||||
# the role rendered, opens its appservice port, and is the Messenger-side image
|
||||
# at the version the role pins.
|
||||
# Proves the bridge starts, reads the configuration and registration the role rendered, opens
|
||||
# its appservice port, and is the Messenger-side image at the version the role pins.
|
||||
#
|
||||
# The extra thing worth proving for this role in particular is that
|
||||
# `matrix_bridge_mautrix_meta_messenger_meta_mode` reaches every place it feeds:
|
||||
# one upstream codebase serves several Meta networks, and the mode is what picks
|
||||
# which one. The scenario runs the bridge in `facebook-tor` mode rather than the
|
||||
# role's default `messenger`, so the assertions below can tell the two apart.
|
||||
# The extra thing worth proving here is that `..._meta_mode` reaches every place it feeds: one
|
||||
# upstream codebase serves several Meta networks, and the mode picks which. The scenario runs
|
||||
# `facebook-tor` rather than the role's default, so the assertions can tell the two apart.
|
||||
#
|
||||
# It does NOT bridge anything - there is no Facebook or Messenger account on the
|
||||
# other side and there is deliberately never going to be one. See
|
||||
# docs/molecule-testing.md.
|
||||
# It does NOT bridge anything: no Facebook or Messenger account is on the other side, and
|
||||
# deliberately never will be. See docs/molecule-testing.md.
|
||||
- name: Verify mautrix-meta-messenger
|
||||
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"
|
||||
# Lazily evaluated, so they are only resolved by the tasks that come after the
|
||||
# matching slurp. Deliberately not set_fact: the rendered configuration
|
||||
# contains the bridge's own Go templates (`{{.}}` in `username_template`), and
|
||||
# a stored fact gets templated again on every lookup, which would try to
|
||||
# Lazily evaluated, so they resolve only in the tasks after the matching slurp.
|
||||
# Deliberately not set_fact: the rendered configuration contains the bridge's own Go
|
||||
# templates, and a stored fact gets templated again on every lookup, which would try to
|
||||
# evaluate those as Jinja.
|
||||
vars:
|
||||
mautrix_meta_messenger_config: "{{ mautrix_meta_messenger_config_file.content | b64decode | from_yaml }}"
|
||||
@@ -34,9 +29,8 @@
|
||||
gather_facts: false
|
||||
|
||||
tasks:
|
||||
# Read from the role's own defaults rather than pinned in molecule.yml, so
|
||||
# the version assertion below compares the running image against what the
|
||||
# role ships instead of against the scenario itself.
|
||||
# From the role's own defaults rather than pinned in molecule.yml, so the version
|
||||
# assertion compares the running image against what the role ships, not the scenario.
|
||||
- name: Load the role's defaults under a separate name
|
||||
ansible.builtin.include_vars:
|
||||
file: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/defaults/main.yml"
|
||||
@@ -51,10 +45,9 @@
|
||||
delay: 5
|
||||
failed_when: false
|
||||
|
||||
# `Restart=always` means a bridge crash-looping on a configuration it cannot
|
||||
# read still reports `active`, so the restart counter is checked too. It is
|
||||
# asserted `is defined` because `| int` turns a missing property into 0 and
|
||||
# would pass vacuously.
|
||||
# `Restart=always` means a bridge crash-looping on unreadable config still reports
|
||||
# `active`, so the restart counter is checked too. Asserted `is defined` because
|
||||
# `| int` turns a missing property into 0 and would pass vacuously.
|
||||
- name: Assert the service is active and has not been restarting
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
@@ -68,10 +61,9 @@
|
||||
automatic restart(s)
|
||||
success_msg: "matrix-mautrix-meta-messenger.service is active and has not restarted"
|
||||
|
||||
# The appservice port is the bridge's own listener, the one a homeserver
|
||||
# would push transactions to. It opening at all means the bridge got through
|
||||
# reading its configuration, through its /whoami check against the
|
||||
# homeserver, and through setting itself up.
|
||||
# The appservice listener is where a homeserver would push transactions. It opening at all
|
||||
# means the bridge got through reading its configuration, through its /whoami check, and
|
||||
# through setting itself up.
|
||||
- name: Wait for the bridge to open its appservice port
|
||||
ansible.builtin.command:
|
||||
argv:
|
||||
@@ -107,9 +99,8 @@
|
||||
src: "{{ matrix_bridge_mautrix_meta_messenger_config_path }}/config.yaml"
|
||||
register: mautrix_meta_messenger_config_file
|
||||
|
||||
# Each of these differs from what the bridge would use on its own, so their
|
||||
# presence means the role's configuration is what the bridge is running on
|
||||
# rather than something that happened to agree with it.
|
||||
# Each differs from what the bridge would use on its own, so their presence rules out
|
||||
# a coincidence.
|
||||
- name: Assert the rendered configuration carries this scenario's values
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
@@ -120,21 +111,19 @@
|
||||
- mautrix_meta_messenger_config.appservice.hs_token == matrix_bridge_mautrix_meta_messenger_homeserver_token
|
||||
- mautrix_meta_messenger_config.bridge.command_prefix == matrix_bridge_mautrix_meta_messenger_bridge_command_prefix
|
||||
- mautrix_meta_messenger_config.logging.min_level == matrix_bridge_mautrix_meta_messenger_logging_min_level
|
||||
# matrix_admin is empty in the shared context, so the only per-domain
|
||||
# permission left is the one the role derives from the homeserver domain.
|
||||
# matrix_admin is empty in the shared context, so the only per-domain permission
|
||||
# left is the one the role derives from the homeserver domain.
|
||||
- mautrix_meta_messenger_config.bridge.permissions[matrix_bridge_mautrix_meta_messenger_homeserver_domain] == 'user'
|
||||
# From the shared context rather than from this scenario, but the role
|
||||
# is what has to carry it into the configuration.
|
||||
# From the shared context rather than this scenario, but the role is what has to
|
||||
# carry it into the configuration.
|
||||
- mautrix_meta_messenger_config.encryption.allow == matrix_bridges_encryption_enabled
|
||||
fail_msg: "The rendered configuration does not carry the scenario's values"
|
||||
success_msg: "The rendered configuration carries the scenario's values"
|
||||
|
||||
# The mode is the variable that makes this role family unusual, and it is
|
||||
# not written into the configuration as-is: the role expands it into an
|
||||
# appservice id, a ghost username prefix, a bot displayname and the bridge's
|
||||
# `tor` switch. Each of these holds a different value under the role's
|
||||
# default `messenger` mode, so together they are what proves the choice
|
||||
# propagated rather than being ignored.
|
||||
# The mode is not written into the configuration as-is: the role expands it into an
|
||||
# appservice id, a ghost username prefix, a bot displayname and the bridge's `tor` switch.
|
||||
# Each holds a different value under the role's default mode, so together they prove the
|
||||
# choice propagated rather than being ignored.
|
||||
- name: Assert the configuration reflects the Meta mode the scenario selected
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
@@ -173,9 +162,8 @@
|
||||
{{ mautrix_meta_messenger_config.appservice.public_address | default('unset') }}
|
||||
success_msg: "The rendered configuration carries the public address the exposure settings imply"
|
||||
|
||||
# The registration file is the half of the appservice handshake the
|
||||
# homeserver reads, and it is generated by the role rather than by the
|
||||
# bridge, so it is worth checking on its own.
|
||||
# The role generates the registration; the bridge only consumes it. Worth checking on its
|
||||
# own, as it is the half of the handshake the homeserver reads.
|
||||
- name: Read the appservice registration the role rendered
|
||||
ansible.builtin.slurp:
|
||||
src: "{{ matrix_bridge_mautrix_meta_messenger_config_path }}/registration.yaml"
|
||||
@@ -192,10 +180,9 @@
|
||||
fail_msg: "The appservice registration does not carry the scenario's tokens, id and bot user"
|
||||
success_msg: "The appservice registration carries the scenario's tokens, id and bot user"
|
||||
|
||||
# The namespaces are regexes the role assembles, so rather than comparing
|
||||
# them as strings - which would only re-derive the role's own escaping -
|
||||
# they are checked by what they do: cover the bot, cover this mode's ghosts,
|
||||
# and not cover the ghosts of the mode the role would have defaulted to.
|
||||
# The namespaces are regexes the role assembles. Comparing them as strings would only
|
||||
# re-derive the role's own escaping, so they are checked by what they match: the bot,
|
||||
# this mode's ghosts, and not the ghosts of the mode the role would have defaulted to.
|
||||
- name: Assert the registration namespaces cover the bot and this mode's ghost users
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
@@ -214,8 +201,8 @@
|
||||
mautrix_meta_messenger_messenger_ghost_mxid: "@messenger_1234567890:{{ matrix_bridge_mautrix_meta_messenger_homeserver_domain }}"
|
||||
mautrix_meta_messenger_bot_mxid: "@{{ matrix_bridge_mautrix_meta_messenger_appservice_username }}:{{ matrix_bridge_mautrix_meta_messenger_homeserver_domain }}"
|
||||
|
||||
# The cheap proof that the data path reached the process and is writable by
|
||||
# the uid the role runs the container as.
|
||||
# Cheap proof that the data path reached the process and is writable by the uid the role
|
||||
# runs the container as.
|
||||
- name: Look for the bridge's sqlite database under the role's data path
|
||||
ansible.builtin.stat:
|
||||
path: "{{ matrix_bridge_mautrix_meta_messenger_data_path }}/mautrix-meta.db"
|
||||
@@ -244,10 +231,9 @@
|
||||
register: mautrix_meta_messenger_image
|
||||
changed_when: false
|
||||
|
||||
# Both Meta bridges are published to the same image repository, with
|
||||
# Instagram's tags carrying an `ig-` prefix, so the tag is compared whole
|
||||
# rather than by substring: an `ig-` prefix would mean this role pulled the
|
||||
# other bridge's image.
|
||||
# Both Meta bridges publish to the same image repository, with Instagram's tags carrying
|
||||
# an `ig-` prefix. Compared whole rather than by substring, so pulling the other bridge's
|
||||
# image would fail here.
|
||||
- name: Assert the running container is the Messenger image at the version defaults/main.yml pins
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
@@ -263,11 +249,10 @@
|
||||
src: "{{ matrix_bridge_mautrix_meta_messenger_base_path }}/labels"
|
||||
register: mautrix_meta_messenger_labels
|
||||
|
||||
# Traefik is not running here, so what is checked is what the role wrote,
|
||||
# not what a reverse-proxy would do with it. The port matters: it is
|
||||
# hardcoded in the role's templates rather than derived from a variable, so
|
||||
# nothing else in this scenario would catch it drifting from the port the
|
||||
# bridge actually listens on.
|
||||
# Traefik is not running here, so this checks what the role wrote, not what a reverse
|
||||
# proxy would do with it. The port matters: it is hardcoded in the role's templates rather
|
||||
# than derived from a variable, so nothing else here would catch it drifting from the port
|
||||
# the bridge actually listens on.
|
||||
- name: Assert the labels route the exposure hostname and path prefix to the appservice port
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
|
||||
@@ -29,35 +29,29 @@ provisioner:
|
||||
all:
|
||||
matrix_bridge_mautrix_whatsapp_container_network: mautrix-whatsapp-molecule
|
||||
|
||||
# The homeserver stub prepare.yml stands up. The bridge contacts it
|
||||
# while starting; it is not a real homeserver and nothing is asserted
|
||||
# The stub prepare.yml stands up. Not a real homeserver, and nothing is asserted
|
||||
# about it.
|
||||
matrix_bridge_mautrix_whatsapp_homeserver_address: http://matrix.molecule.local:8008
|
||||
|
||||
# sqlite keeps the scenario to one container. The role only requires a
|
||||
# database hostname when the engine is postgres, and testing which
|
||||
# database engine the bridge can talk to is not what this proves.
|
||||
# sqlite keeps the scenario to one container. Which database engine the bridge can
|
||||
# talk to is not what this proves.
|
||||
matrix_bridge_mautrix_whatsapp_database_engine: sqlite
|
||||
|
||||
# Appservice tokens. These are what the bridge and homeserver would
|
||||
# authenticate to each other with; here they only have to reach the
|
||||
# rendered configuration and the registration file.
|
||||
# Here these only have to reach the rendered configuration and the registration.
|
||||
matrix_bridge_mautrix_whatsapp_appservice_token: molecule_as_token_4f2a91
|
||||
matrix_bridge_mautrix_whatsapp_homeserver_token: molecule_hs_token_9b3e77
|
||||
|
||||
# Deliberately different from the role's defaults, so verify.yml can
|
||||
# tell what the role rendered apart from what the bridge would have
|
||||
# defaulted to on its own.
|
||||
# Different from the role's defaults, so verify.yml can tell what the role rendered
|
||||
# apart from what the bridge would have chosen.
|
||||
matrix_bridge_mautrix_whatsapp_appservice_bot_username: molecule-whatsappbot
|
||||
matrix_bridge_mautrix_whatsapp_homeserver_domain: molecule.local
|
||||
|
||||
# Traefik is not deployed here, so the labels the role would render for
|
||||
# it are switched off and their absence is asserted instead.
|
||||
# Traefik is not deployed here, so the labels the role would render for it are
|
||||
# switched off and their absence is asserted instead.
|
||||
matrix_bridge_mautrix_whatsapp_container_labels_traefik_enabled: false
|
||||
|
||||
# verify.yml runs as its own play, where role defaults are out of scope,
|
||||
# so the paths it reads are pinned here as literals matching what the
|
||||
# role derives from matrix_base_data_path.
|
||||
# verify.yml runs as its own play, where the role's defaults are out of scope,
|
||||
# so the paths it reads are pinned here to match what the role derives.
|
||||
matrix_bridge_mautrix_whatsapp_base_path: /matrix/mautrix-whatsapp
|
||||
matrix_bridge_mautrix_whatsapp_config_path: /matrix/mautrix-whatsapp/config
|
||||
matrix_bridge_mautrix_whatsapp_data_path: /matrix/mautrix-whatsapp/data
|
||||
|
||||
@@ -31,9 +31,8 @@
|
||||
docker_daemon_options:
|
||||
storage-driver: fuse-overlayfs
|
||||
|
||||
# The role's file tasks set owner/group by name and Ansible resolves those
|
||||
# through the passwd database, so they have to exist first. matrix-base
|
||||
# creates them in a real deployment.
|
||||
# The role's file tasks set owner/group by name, which Ansible resolves through the
|
||||
# passwd database, so they have to exist first. `matrix-base` creates them for real.
|
||||
- name: Ensure the matrix group exists
|
||||
ansible.builtin.group:
|
||||
name: "{{ matrix_group_name }}"
|
||||
@@ -70,8 +69,8 @@
|
||||
- mautrix_whatsapp_molecule_network.rc != 0
|
||||
- "'already exists' not in mautrix_whatsapp_molecule_network.stderr"
|
||||
|
||||
# The bridge contacts the homeserver as it starts. It is not being asked to
|
||||
# bridge anything - see molecule-shared/homeserver-stub.py.
|
||||
# The bridge contacts the homeserver as it starts. It is not being asked to bridge
|
||||
# anything. See molecule-shared/homeserver-stub.py.
|
||||
- name: Ensure the homeserver stub is running
|
||||
ansible.builtin.include_tasks:
|
||||
file: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/../../../molecule-shared/tasks/homeserver-stub.yml"
|
||||
|
||||
@@ -3,10 +3,11 @@
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
---
|
||||
# What this proves: the bridge starts, reads the configuration and registration
|
||||
# the role rendered, opens its appservice port, and is the version the role
|
||||
# pins. It does NOT bridge anything - there is no WhatsApp on the other side and
|
||||
# there is deliberately never going to be one. See docs/molecule-testing.md.
|
||||
# Proves the bridge starts, reads the configuration and registration the role rendered, opens
|
||||
# its appservice port, and is the version the role pins.
|
||||
#
|
||||
# It does NOT bridge anything: there is no WhatsApp on the other side, and deliberately never
|
||||
# will be. See docs/molecule-testing.md.
|
||||
- name: Verify mautrix-whatsapp
|
||||
hosts: all
|
||||
become: true
|
||||
@@ -16,9 +17,8 @@
|
||||
gather_facts: false
|
||||
|
||||
tasks:
|
||||
# Read from the role's own defaults rather than pinned in molecule.yml, so
|
||||
# the version assertion below compares the running image against what the
|
||||
# role ships instead of against the scenario itself.
|
||||
# From the role's own defaults rather than pinned in molecule.yml, so the version
|
||||
# assertion compares the running image against what the role ships, not the scenario.
|
||||
- name: Load the role's defaults under a separate name
|
||||
ansible.builtin.include_vars:
|
||||
file: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/defaults/main.yml"
|
||||
@@ -33,10 +33,9 @@
|
||||
delay: 5
|
||||
failed_when: false
|
||||
|
||||
# `Restart=always` means a bridge crash-looping on a configuration it cannot
|
||||
# read still reports `active`, so the restart counter is checked too. It is
|
||||
# asserted `is defined` because `| int` turns a missing property into 0 and
|
||||
# would pass vacuously.
|
||||
# `Restart=always` means a bridge crash-looping on unreadable config still reports
|
||||
# `active`, so the restart counter is checked too. Asserted `is defined` because
|
||||
# `| int` turns a missing property into 0 and would pass vacuously.
|
||||
- name: Assert the service is active and has not been restarting
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
@@ -50,9 +49,8 @@
|
||||
automatic restart(s)
|
||||
success_msg: "matrix-mautrix-whatsapp.service is active and has not restarted"
|
||||
|
||||
# The appservice port is the bridge's own listener, the one a homeserver
|
||||
# would push transactions to. It opening at all means the bridge got through
|
||||
# reading its configuration and setting itself up.
|
||||
# The appservice listener is where a homeserver would push transactions. It opening at all
|
||||
# means the bridge got through reading its configuration and setting itself up.
|
||||
- name: Wait for the bridge to open its appservice port
|
||||
ansible.builtin.command:
|
||||
argv:
|
||||
@@ -88,9 +86,8 @@
|
||||
src: "{{ matrix_bridge_mautrix_whatsapp_config_path }}/config.yaml"
|
||||
register: mautrix_whatsapp_config_file
|
||||
|
||||
# Each of these differs from what the bridge would use on its own, so their
|
||||
# presence means the role's configuration is what the bridge is running on
|
||||
# rather than something that happened to agree with it.
|
||||
# Each differs from what the bridge would use on its own, so their presence rules out
|
||||
# a coincidence.
|
||||
- name: Assert the rendered configuration carries this scenario's values
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
@@ -102,9 +99,8 @@
|
||||
vars:
|
||||
mautrix_whatsapp_config_rendered: "{{ mautrix_whatsapp_config_file.content | b64decode }}"
|
||||
|
||||
# The registration file is the half of the appservice handshake the
|
||||
# homeserver reads, and it is generated by the role rather than by the
|
||||
# bridge, so it is worth checking on its own.
|
||||
# The role generates the registration; the bridge only consumes it. Worth checking on its
|
||||
# own, as it is the half of the handshake the homeserver reads.
|
||||
- name: Read the appservice registration the role rendered
|
||||
ansible.builtin.slurp:
|
||||
src: "{{ matrix_bridge_mautrix_whatsapp_config_path }}/registration.yaml"
|
||||
@@ -121,9 +117,8 @@
|
||||
vars:
|
||||
mautrix_whatsapp_registration_rendered: "{{ mautrix_whatsapp_registration_file.content | b64decode }}"
|
||||
|
||||
# sqlite was chosen in molecule.yml, so the bridge should have created its
|
||||
# database under the role's data path. This is the cheap proof that the data
|
||||
# path reached the process and is writable by the uid the role runs it as.
|
||||
# Cheap proof that the data path reached the process and is writable by the uid the role
|
||||
# runs it as.
|
||||
- name: Look for the bridge's sqlite database under the role's data path
|
||||
ansible.builtin.stat:
|
||||
path: "{{ matrix_bridge_mautrix_whatsapp_data_path }}/mautrix-whatsapp.db"
|
||||
|
||||
Reference in New Issue
Block a user