Test the Molecule scenarios against Postgres rather than sqlite

`group_vars/matrix_servers` selects postgres whenever postgres is enabled, which
is the default, so postgres is what essentially every deployment runs. The
scenarios were testing sqlite - a path almost nobody is on.

How little that path is used is not a guess: the mautrix-meta bridges could not
start at all under sqlite, and nobody reported it. Testing the engine users are
actually on is worth more than keeping coverage of the one they are not, so no
scenario is left behind on sqlite.

Four of the eight scenarios have a database and are converted; the other four
have none and are untouched.

molecule-shared/tasks/postgres.yml stands Postgres up on the scenario's network,
with the data directory on a tmpfs since it is thrown away with the container.
The image is pinned at the major the postgres role deploys to new installations
and left to Renovate: when a new major lands, the PR bumping that pin runs every
scenario against it, which is the earliest warning we get that a component does
not cope.

Each scenario gives its database and user names that differ from the role's
defaults, so the component reaching the database proves the role built its
connection string out of them. The assertions moved from "a file appeared at the
path we configured" to "these tables exist", which is strictly stronger: tables
can only appear once the component has resolved the hostname, authenticated with
the credentials the role rendered, and run its migrations to completion.

Costs about 10 seconds per affected scenario (115s to 125s locally for
mautrix-whatsapp), on jobs that run in parallel.

Gotcha worth recording: since Postgres 18 the image puts PGDATA in a versioned
subdirectory and refuses to start if it finds a mount at the old
/var/lib/postgresql/data, so the tmpfs is mounted at /var/lib/postgresql.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SEH3vxYSQ5SV4N5z61eyGT
This commit is contained in:
Slavi Pantaleev
2026-08-27 18:02:53 +03:00
co-authored by Claude Opus 5
parent c447e1528b
commit 85f80a3c7e
15 changed files with 296 additions and 100 deletions
+73
View File
@@ -0,0 +1,73 @@
# SPDX-FileCopyrightText: 2026 Slavi Pantaleev
#
# SPDX-License-Identifier: AGPL-3.0-or-later
---
# Stands up Postgres on a container network, for scenarios whose role has a database.
#
# Include from a scenario's prepare.yml:
#
# - name: Ensure Postgres is running
# ansible.builtin.include_tasks:
# file: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/../../../molecule-shared/tasks/postgres.yml"
# vars:
# molecule_shared_postgres_network: "{{ <role>_container_network }}"
# molecule_shared_postgres_database: matrix_<role>
# molecule_shared_postgres_username: matrix_<role>
# molecule_shared_postgres_password: <something>
#
# The role should then be pointed at it with its own `_database_engine: postgres` and
# `_database_hostname: matrix-postgres-molecule` (or whatever hostname is passed here).
#
# This is what the playbook does in a real run: `group_vars/matrix_servers` selects postgres
# whenever postgres is enabled, which is the default. sqlite is the path almost nobody uses.
- name: Ensure a previous Postgres is gone
ansible.builtin.command:
argv:
- docker
- rm
- --force
- "{{ molecule_shared_postgres_name | default('matrix-postgres-molecule') }}"
register: molecule_shared_postgres_removal
changed_when: molecule_shared_postgres_removal.rc == 0
failed_when: false
- name: Ensure Postgres is running
ansible.builtin.command:
argv:
- docker
- run
- --detach
- --name={{ molecule_shared_postgres_name | default('matrix-postgres-molecule') }}
- --network={{ molecule_shared_postgres_network }}
- --network-alias={{ molecule_shared_postgres_hostname | default('matrix-postgres-molecule') }}
- --env=POSTGRES_DB={{ molecule_shared_postgres_database }}
- --env=POSTGRES_USER={{ molecule_shared_postgres_username }}
- --env=POSTGRES_PASSWORD={{ molecule_shared_postgres_password }}
# A tmpfs, because the database is thrown away with the container and not writing it
# to the overlay filesystem is faster. Mounted at /var/lib/postgresql rather than at
# .../data: since 18 the image puts PGDATA in a versioned subdirectory and refuses to
# start if it finds a mount at the old path.
- --tmpfs=/var/lib/postgresql
- "{{ molecule_shared_image_postgres }}"
register: molecule_shared_postgres_start
changed_when: molecule_shared_postgres_start.rc == 0
# `pg_isready` needs the username: it defaults to the OS user of whoever runs it, which inside
# this container is `postgres` and may not be the role the scenario created.
- name: Wait for Postgres to accept connections
ansible.builtin.command:
argv:
- docker
- exec
- "{{ molecule_shared_postgres_name | default('matrix-postgres-molecule') }}"
- pg_isready
- --username={{ molecule_shared_postgres_username }}
- --dbname={{ molecule_shared_postgres_database }}
- --quiet
register: molecule_shared_postgres_ready
changed_when: false
until: molecule_shared_postgres_ready.rc == 0
retries: 30
delay: 2