mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-06 19:01:54 +00:00
- test_prefix_grouped_files: files/sql/store_database.sql and store_layout.sql become files/sql/store/database.sql and layout.sql. - test_file_spot: test_store_guard.py builds the vars path with ROLE_FILE_VARS_MAIN instead of a hard-coded "vars" / "main.yml" chain. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
124 lines
5.5 KiB
YAML
124 lines
5.5 KiB
YAML
---
|
|
# Stalwart 1.x keeps a bytea column v in table u; every 0.x store keeps its bigint quota counters under the same name.
|
|
# mask_values scrubs the role's names out of psql errors, so existence comes from the shared catalog or the dedicated volume, and a stopped dedicated database fails its own assert instead of a masked psql error.
|
|
# The host marker outranks the store: a marked host that goes back to a 0.16 store (rollback, old backup) passes unless the marker is removed first.
|
|
|
|
- name: "🔍 Find the 1.x store marker of this host"
|
|
ansible.builtin.stat:
|
|
path: "{{ STALWART_STORE_MARKER_FILE }}"
|
|
register: stalwart_store_marker_file
|
|
|
|
- name: "📖 Read the 1.x store marker of this host"
|
|
ansible.builtin.slurp:
|
|
src: "{{ STALWART_STORE_MARKER_FILE }}"
|
|
register: stalwart_store_marker
|
|
when: stalwart_store_marker_file.stat.exists
|
|
|
|
- name: "🔧 Record whether this host last deployed Stalwart 1.x on its store"
|
|
ansible.builtin.set_fact:
|
|
stalwart_store_marked: >-
|
|
{{ stalwart_store_marker is not skipped
|
|
and (stalwart_store_marker.content | b64decode | trim) == (STALWART_STORE_LAYOUT_VERSION | string) }}
|
|
|
|
- name: "🔍 Find the store database on the shared PostgreSQL"
|
|
database_query:
|
|
config: >-
|
|
{{ lookup('database', application_id) | combine({
|
|
'username': 'postgres',
|
|
'name': 'postgres',
|
|
'password': lookup('config', 'svc-db-postgres', 'secrets.credentials.POSTGRES_PASSWORD'),
|
|
}) }}
|
|
query_file: "{{ role_path }}/files/sql/store/database.sql"
|
|
named_args:
|
|
name: "{{ lookup('database', application_id, 'name') }}"
|
|
expect_rows: true
|
|
mask_values: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"
|
|
register: stalwart_store_database
|
|
until: stalwart_store_database is succeeded
|
|
retries: 6
|
|
delay: 5
|
|
changed_when: false
|
|
no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"
|
|
when:
|
|
- not (stalwart_store_marked | bool)
|
|
- STALWART_DATABASE_SHARED | bool
|
|
|
|
- name: "🔍 Find the volume of the dedicated store database"
|
|
ansible.builtin.command: >-
|
|
container volume ls -q --filter name=^{{ lookup('database', application_id, 'volume') }}$
|
|
register: stalwart_store_volume
|
|
changed_when: false
|
|
when:
|
|
- not (stalwart_store_marked | bool)
|
|
- not (STALWART_DATABASE_SHARED | bool)
|
|
|
|
- name: "🔧 Record whether a store exists that this host never deployed Stalwart 1.x on"
|
|
ansible.builtin.set_fact:
|
|
stalwart_store_exists: >-
|
|
{{ not (stalwart_store_marked | bool) and (
|
|
(stalwart_store_database.rows[0][0] | int > 0)
|
|
if STALWART_DATABASE_SHARED | bool
|
|
else (stalwart_store_volume.stdout | trim | length > 0)) }}
|
|
|
|
- name: "🔍 Find the running container of the dedicated store database"
|
|
ansible.builtin.command:
|
|
argv:
|
|
- container
|
|
- ps
|
|
- --quiet
|
|
- --filter
|
|
- status=running
|
|
- --filter
|
|
- >-
|
|
{{ ('label=com.docker.swarm.service.name=' ~ lookup('database', application_id, 'service_name'))
|
|
if DEPLOYMENT_MODE == 'swarm'
|
|
else ('name=^' ~ lookup('database', application_id, 'container') ~ '$') }}
|
|
register: stalwart_store_engine
|
|
changed_when: false
|
|
when:
|
|
- stalwart_store_exists | bool
|
|
- not (STALWART_DATABASE_SHARED | bool)
|
|
|
|
- name: "✅ Ensure the dedicated store database runs, so the deploy can read its layout"
|
|
ansible.builtin.assert:
|
|
that:
|
|
- stalwart_store_engine.stdout | trim | length > 0
|
|
fail_msg: >-
|
|
The dedicated store database of Stalwart, service '{{ lookup('database', application_id, 'host') }}'
|
|
of stack '{{ application_id | get_entity_name }}', is not running on {{ inventory_hostname }},
|
|
so the deploy cannot read the layout of the store in volume '{{ lookup('database', application_id, 'volume') }}'.
|
|
This host has no record of a Stalwart 1.x deploy ({{ STALWART_STORE_MARKER_FILE }}), so that store may predate 1.0,
|
|
and the deploy stops before Stalwart {{ lookup('config', application_id, 'services.stalwart.version') }} opens it.
|
|
Start that database from the stack this host last deployed, or finish the stalwart-migrate steps of
|
|
"Upgrading from 0.16" in roles/web-app-stalwart/README.md, then deploy again.
|
|
quiet: true
|
|
when: stalwart_store_engine is not skipped
|
|
|
|
- name: "🔍 Read the layout of the store Stalwart opens"
|
|
database_query:
|
|
config: "{{ lookup('database', application_id) }}"
|
|
query_file: "{{ role_path }}/files/sql/store/layout.sql"
|
|
expect_rows: true
|
|
mask_values: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"
|
|
register: stalwart_store_layout
|
|
until: stalwart_store_layout is succeeded
|
|
retries: 6
|
|
delay: 5
|
|
changed_when: false
|
|
no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"
|
|
when: stalwart_store_exists | bool
|
|
|
|
- name: "✅ Ensure Stalwart opens no store from before 1.0"
|
|
ansible.builtin.assert:
|
|
that:
|
|
- stalwart_store_layout.rows[0][0] in ['bytea', 'absent']
|
|
fail_msg: >-
|
|
The store in database '{{ lookup('database', application_id, 'name') }}' predates Stalwart 1.0
|
|
(type of column u.v: {{ stalwart_store_layout.rows[0][0] }}, a 1.x store holds bytea).
|
|
Stalwart {{ lookup('config', application_id, 'services.stalwart.version') }} refuses to open it and has no in-place upgrade,
|
|
so the deploy stops before it starts Stalwart. Migrate the store into a new
|
|
database with stalwart-migrate and swap it in as '{{ lookup('database', application_id, 'name') }}',
|
|
following "Upgrading from 0.16" in roles/web-app-stalwart/README.md, then deploy again.
|
|
quiet: true
|
|
when: stalwart_store_layout is not skipped
|