matrix-docker-ansible-deploy/roles/matrix-postgres/tasks/upgrade_postgres.yml
Slavi Pantaleev dd797ba6a7 Fix Postgres database importing/upgrading conflicts
We were running into conflicts, because having initialized
the roles (users) and databases, trying to import leads to
errors (role XXX already exists, etc.).

We were previously ignoring the Synapse database (`homeserver`)
when upgrading/importing, because that one gets created by default
whenever the container starts.

For our additional databases, it's a similar situation now.
It's not created by default as soon as Postgres starts with an empty
database, but rather we create it as part of running the playbook.

So we either need to skip those role/database creation statements
while upgrading/importing, or to avoid creating the additional database
and rely on the import for that. I've gone for the former, because
it's already similar to what we were doing and it's simpler
(it lets `setup_postgres.yml` be the same in all scenarios).
2020-12-14 22:28:20 +02:00

173 lines
6.7 KiB
YAML

---
- name: Set default postgres_dump_dir, if not provided
set_fact:
postgres_dump_dir: "/tmp"
when: "postgres_dump_dir|default('') == ''"
- name: Set postgres_dump_name, if not provided
set_fact:
postgres_dump_name: "matrix-postgres-dump.sql.gz"
when: "postgres_dump_name|default('') == ''"
- name: Set postgres_auto_upgrade_backup_data_path, if not provided
set_fact:
postgres_auto_upgrade_backup_data_path: "{{ matrix_postgres_data_path }}-auto-upgrade-backup"
when: "postgres_auto_upgrade_backup_data_path|default('') == ''"
- name: Set postgres_start_wait_time, if not provided
set_fact:
postgres_start_wait_time: 15
when: "postgres_start_wait_time|default('') == ''"
- name: Set postgres_force_upgrade, if not provided
set_fact:
postgres_force_upgrade: false
when: "postgres_force_upgrade|default('') == ''"
- name: Fail, if trying to upgrade external Postgres database
fail:
msg: "Your configuration indicates that you're not using Postgres from this role. There is nothing to upgrade."
when: "not matrix_postgres_enabled|bool"
- name: Check Postgres auto-upgrade backup data directory
stat:
path: "{{ postgres_auto_upgrade_backup_data_path }}"
register: result_auto_upgrade_path
- name: Abort, if existing Postgres auto-upgrade data path detected
fail:
msg: "Detected that a left-over {{ postgres_auto_upgrade_backup_data_path }} exists. You should rename it to {{ matrix_postgres_data_path }} if the previous upgrade went wrong, or delete it if it went well."
when: "result_auto_upgrade_path.stat.exists"
- import_tasks: tasks/util/detect_existing_postgres_version.yml
- name: Abort, if no existing Postgres version detected
fail:
msg: "Could not find existing Postgres installation"
when: "not matrix_postgres_detected_existing|bool"
- name: Abort, if already at latest Postgres version
fail:
msg: "You are already running the latest Postgres version supported ({{ matrix_postgres_docker_image_latest }}). Nothing to do"
when: "matrix_postgres_detected_version_corresponding_docker_image == matrix_postgres_docker_image_latest and not postgres_force_upgrade"
- debug:
msg: "Upgrading database from {{ matrix_postgres_detected_version_corresponding_docker_image }} to {{ matrix_postgres_docker_image_latest }}"
- name: Ensure matrix-synapse is stopped
service:
name: matrix-synapse
state: stopped
- name: Ensure matrix-postgres is started
service:
name: matrix-postgres
state: started
daemon_reload: yes
- name: Wait a bit, so that Postgres can start
wait_for:
timeout: "{{ postgres_start_wait_time }}"
delegate_to: 127.0.0.1
become: false
# We dump all databases, roles, etc.
#
# Because we'll be importing into a new container which initializes the default
# role (`matrix_postgres_connection_username`) and database (`matrix_postgres_db_name`) by itself on startup,
# we need to remove these from the dump, or we'll get errors saying these already exist.
- name: Perform Postgres database dump
command: >-
{{ matrix_host_command_docker }} run --rm --name matrix-postgres-dump
--log-driver=none
--user={{ matrix_user_uid }}:{{ matrix_user_gid }}
--network={{ matrix_docker_network }}
--env-file={{ matrix_postgres_base_path }}/env-postgres-psql
--entrypoint=/bin/sh
--mount type=bind,src={{ postgres_dump_dir }},dst=/out
{{ matrix_postgres_detected_version_corresponding_docker_image }}
-c "pg_dumpall -h matrix-postgres
{{ '| gzip -c ' if postgres_dump_name.endswith('.gz') else '' }}
> /out/{{ postgres_dump_name }}"
- name: Ensure matrix-postgres is stopped
service:
name: matrix-postgres
state: stopped
- name: Rename existing Postgres data directory
command: "mv {{ matrix_postgres_data_path }} {{ postgres_auto_upgrade_backup_data_path }}"
- debug:
msg: "NOTE: Your Postgres data directory has been moved from `{{ matrix_postgres_data_path }}` to `{{ postgres_auto_upgrade_backup_data_path }}`. In the event of failure, you can move it back and run the playbook with --tags=setup-postgres to restore operation."
- import_tasks: tasks/setup_postgres.yml
- name: Ensure matrix-postgres autoruns and is restarted
service:
name: matrix-postgres
enabled: yes
state: restarted
daemon_reload: yes
- name: Wait a bit, so that Postgres can start
wait_for:
timeout: "{{ postgres_start_wait_time }}"
delegate_to: 127.0.0.1
become: false
# Starting the database container had automatically created the default
# role (`matrix_postgres_connection_username`) and database (`matrix_postgres_db_name`).
# The dump most likely contains those same entries and would try to re-create them, leading to errors.
# We need to skip over those lines.
- name: Generate Postgres database import command
set_fact:
matrix_postgres_import_command: >-
{{ matrix_host_command_docker }} run --rm --name matrix-postgres-import
--log-driver=none
--user={{ matrix_user_uid }}:{{ matrix_user_gid }}
--cap-drop=ALL
--network={{ matrix_docker_network }}
--env-file={{ matrix_postgres_base_path }}/env-postgres-psql
--entrypoint=/bin/sh
--mount type=bind,src={{ postgres_dump_dir }},dst=/in,ro
{{ matrix_postgres_docker_image_latest }}
-c "cat /in/{{ postgres_dump_name }} |
{{ 'gunzip |' if postgres_dump_name.endswith('.gz') else '' }}
grep -vE '{{ matrix_postgres_import_roles_ignore_regex }}' |
grep -vE '{{ matrix_postgres_import_databases_ignore_regex }}' |
psql -v ON_ERROR_STOP=1 -h matrix-postgres"
# This is a hack.
# See: https://ansibledaily.com/print-to-standard-output-without-escaping/
#
# We want to run `debug: msg=".."`, but that dumps it as JSON and escapes double quotes within it,
# which ruins the command (`matrix_postgres_import_command`)
- name: Note about Postgres importing
set_fact:
dummy: true
with_items:
- >-
Importing Postgres database using the following command: `{{ matrix_postgres_import_command }}`.
If this crashes, you can stop Postgres (`systemctl stop matrix-postgres`),
delete the new database data (`rm -rf {{ matrix_postgres_data_path }}`)
and restore the automatically-made backup (`mv {{ postgres_auto_upgrade_backup_data_path }} {{ matrix_postgres_data_path }}`).
- name: Perform Postgres database import
command: "{{ matrix_postgres_import_command }}"
- name: Delete Postgres database dump file
file:
path: "{{ postgres_dump_dir }}/{{ postgres_dump_name }}"
state: absent
- name: Ensure matrix-synapse is started
service:
name: matrix-synapse
state: started
daemon_reload: yes
- debug:
msg: "NOTE: Your old Postgres data directory is preserved at `{{ postgres_auto_upgrade_backup_data_path }}`. You might want to get rid of it once you've confirmed that all is well."