feat(postgresql): add ansible role for postgresql deployment

This commit is contained in:
2024-11-14 18:38:38 +01:00
parent bff5cce7e9
commit 6f70e8c2bf
17 changed files with 529 additions and 0 deletions

View File

@ -0,0 +1,60 @@
---
- name: Configure postgresql
block:
- name: Ensure postgresql superuser is set
community.postgresql.postgresql_user:
name: "{{ postgresql_admin_role }}"
password: "{{ postgresql_superuser_password }}"
login_host: "{{ postgresql_login_host }}"
register: postgresql_superuser_password_result
until: "postgresql_superuser_password_result is succeeded"
retries: 10
delay: 2
- name: Ensure postgresql configuration is set
community.postgresql.postgresql_set:
name: "{{ option.key }}"
value: "{{ pg_option_value }}"
login_host: "{{ postgresql_login_host }}"
login_port: "{{ postgresql_config_port }}"
login_password: "{{ postgresql_superuser_password }}"
loop: "{{ postgresql_merged_config | dict2items }}"
loop_control:
loop_var: option
vars:
pg_option_value: >-2
{{
(option.value | join(' '))
if (option.value is iterable
and option.value is not string
and option.value is not mapping)
else option.value
}}
register: postgresql_config_results
- name: Ensure postgresql configuration is reloaded
community.postgresql.postgresql_query:
db: "postgres"
query: "SELECT pg_reload_conf();"
login_host: "{{ postgresql_login_host }}"
login_port: "{{ postgresql_config_port }}"
login_password: "{{ postgresql_superuser_password }}"
- name: Ensure restart handler is fired if required
debug:
msg: "{{ result.option.key }} changed! Restart required: {{ result.restart_required }}"
when: result.changed
changed_when: "{{ result.restart_required }}"
notify: postgresql_restart
loop: "{{ postgresql_config_results.results }}"
loop_control:
loop_var: result
label: "{{ result.option.key }}"
when: postgresql_state == 'present'
vars:
postgresql_login_host: >-2
{{
(postgresql_config_unix_socket_directories | first)
if postgresql_config_connect_socket else
(postgresql_container_info.container.NetworkSettings.IPAddress)
}}

View File

@ -0,0 +1,95 @@
---
- name: Ensure postgresql container image '{{ postgresql_container_image }}' is {{ postgresql_state }}
community.docker.docker_image:
name: "{{ postgresql_container_image }}"
state: "{{ postgresql_state }}"
source: "{{ postgresql_container_image_source }}"
force_source: "{{ postgresql_container_image_force_source }}"
register: postgresql_container_image_info
until: postgresql_container_image_info is success
retries: 5
delay: 4
- name: Ensure /etc/passwd for container is {{ postgresql_state }}
ansible.builtin.template:
src: "postgresql-passwd.j2"
dest: "{{ postgresql_container_passwd_file }}"
owner: "{{ postgresql_user_id }}"
group: "{{ postgresql_user_group_id }}"
mode: "0640"
when: postgresql_state == 'present'
- name: Ensure systemd unit to correct path permissions is {{ postgresql_state }}
ansible.builtin.copy:
dest: "/etc/systemd/system/{{ postgresql_systemd_tmpfile_socket_correction_unit_name }}.service"
content: |+2
[Unit]
Description="Ensure permissions on {{ postgresql_container_unix_socket_path }}"
After=systemd-tmpfiles-setup.service
Before=docker.service
[Service]
Type=exec
RemainAfterExit=yes
ExecStart=/bin/bash -c 'mkdir {{ postgresql_container_unix_socket_path }} ||:; chown {{ postgresql_user }}:{{ postgresql_user }} {{ postgresql_container_unix_socket_path }}'
[Install]
WantedBy=multi-user.target
when:
- ansible_facts['service_mgr'] == 'systemd'
- postgresql_state == 'present'
register: postgresql_systemd_tmpfile_correction_unit_info
- name: Ensure systemd is reloaded
ansible.builtin.systemd:
daemon_reload: true
when:
- postgresql_systemd_tmpfile_correction_unit_info.changed
- name: Ensure systemd unit {{ postgresql_systemd_tmpfile_socket_correction_unit_name }} is {{ postgresql_container_state }}
ansible.builtin.systemd:
name: "{{ postgresql_systemd_tmpfile_socket_correction_unit_name }}.service"
state: "{{ postgresql_container_state }}"
when: ansible_facts['service_mgr'] == 'systemd'
- name: Ensure systemd unit {{ postgresql_systemd_tmpfile_socket_correction_unit_name }} is {{ postgresql_container_state }}
ansible.builtin.systemd:
name: "{{ postgresql_systemd_tmpfile_socket_correction_unit_name }}.service"
enabled: "{{ postgresql_state == 'present' }}"
when: ansible_facts['service_mgr'] == 'systemd'
- name: Lookup {{ postgresql_data_path }}/global
ansible.builtin.stat:
path: "{{ postgresql_data_path }}/global"
get_checksum: false
register: postgresql_global_data_info
- name: Initialize database if empty
ansible.builtin.include_tasks:
file: "initialize-docker.yml"
when:
- postgresql_state == 'present'
- not postgresql_global_data_info.stat.exists
- postgresql_global_data_info.stat.isdir is defined
- not postgresql_global_data_info.stat.isdir
- name: Ensure postgresql container '{{ postgresql_container_name }}' is {{ postgresql_container_state }}
community.docker.docker_container:
name: "{{ postgresql_container_name }}"
image: "{{ postgresql_container_image }}"
env: "{{ postgresql_container_env | default(omit, true) }}"
user: "{{ postgresql_container_user | default(omit, true) }}"
ports: "{{ postgresql_container_ports | default(omit, true) }}"
labels: "{{ postgresql_container_labels | default(omit, true) }}"
volumes: "{{ postgresql_container_merged_volumes }}"
recreate: "{{ postgresql_container_recreate | default(omit, true) }}"
networks: "{{ postgresql_container_networks | default(omit, true) }}"
etc_hosts: "{{ postgresql_container_etc_hosts | default(omit, true) }}"
memory: "{{ postgresql_container_memory | default(omit, true) }}"
memory_reservation: "{{ postgresql_container_memory_reservation | default(omit, true) }}"
oom_killer: "{{ postgresql_container_oom_killer | default(omit, true) }}"
oom_score_adj: "{{ postgresql_container_oom_score_adj | default(omit, true) }}"
shm_size: "{{ postgresql_container_shm_size | default(omit, true) }}"
ulimits: "{{ postgresql_container_ulimits | default(omit, true) }}"
restart_policy: "{{ postgresql_container_restart_policy | default(omit, true) }}"
state: "{{ postgresql_container_state }}"

View File

@ -0,0 +1,47 @@
---
- name: Ensure container '{{ postgresql_container_name }}' is {{ postgresql_container_state }} to initialise the database
community.docker.docker_container:
name: "{{ postgresql_container_name }}"
image: "{{ postgresql_container_image }}"
env: >-2
{{ postgresql_container_env | default({}, true)
| combine({'POSTGRES_PASSWORD': postgresql_superuser_password}) }}
user: "{{ postgresql_container_user | default(omit, true) }}"
ports: "{{ postgresql_container_ports | default(omit, true) }}"
labels: "{{ postgresql_container_labels | default(omit, true) }}"
volumes: "{{ postgresql_container_initdb_volumes }}"
recreate: "{{ postgresql_container_recreate | default(omit, true) }}"
networks: "{{ postgresql_container_networks | default(omit, true) }}"
etc_hosts: "{{ postgresql_container_etc_hosts | default(omit, true) }}"
memory: "{{ postgresql_container_memory | default(omit, true) }}"
memory_reservation: "{{ postgresql_container_memory_reservation | default(omit, true) }}"
oom_killer: "{{ postgresql_container_oom_killer | default(omit, true) }}"
oom_score_adj: "{{ postgresql_container_oom_score_adj | default(omit, true) }}"
shm_size: "{{ postgresql_container_shm_size | default(omit, true) }}"
ulimits: "{{ postgresql_container_ulimits | default(omit, true) }}"
restart_policy: "{{ postgresql_container_restart_policy | default(omit, true) }}"
state: "{{ postgresql_container_state }}"
register: postgresql_container_info
- name: Wait for container startup
block:
- name: Wait for container startup (socket)
ansible.builtin.wait_for:
path: "{{ postgresql_config_unix_socket_directories | first }}/.s.PGSQL.{{ postgresql_config_port }}"
when: "postgresql_config_connect_socket | bool"
- name: Wait for container startup (port)
ansible.builtin.wait_for:
host: >-2
{{ (pg_addresses == '*') | ternary(
omit,
postgresql_config_listen_addresses | first
) }}
port: "{{ postgresql_config_port }}"
when: "not postgresql_config_connect_socket | bool"
vars:
pg_addresses: "{{ postgresql_config_listen_addresses | join(',') }}"
- name: Ensure init container '{{ postgresql_container_name }}' is removed
community.docker.docker_container:
name: "{{ postgresql_container_name }}"
state: absent

View File

@ -0,0 +1,72 @@
---
- name: Ensure state is valid
ansible.builtin.fail:
msg: >-2
Invalid state '{{ postgresql_state }}'! Supported
states are {{ postgresql_states | join(', ') }}.
when: postgresql_state not in postgresql_states
- name: Ensure deployment method is valid
ansible.builtin.fail:
msg: >-2
Unsupported deployment method '{{ postgresql_deployment_method }}!
Supported deployment methods are {{ postgresql_deployment_methods | join(', ') }}.
when: postgresql_deployment_method not in postgresql_deployment_methods
- name: Ensure postgresql user '{{ postgresql_user }}' is {{ postgresql_state }}
ansible.builtin.user:
name: "{{ postgresql_user }}"
state: "{{ postgresql_state }}"
system: "{{ postgresql_user_system | default(omit, true) }}"
create_home: "{{ postgresql_user_create_home | default(omit, true) }}"
groups: "{{ postgresql_user_groups | default(omit, true) }}"
append: "{{ postgresql_user_append | default(omit, true) }}"
register: postgresql_user_info
- name: Ensure directories are {{ postgresql_state }}
ansible.builtin.file:
path: "{{ path.name }}"
state: "{{ (postgresql_state == 'present') | ternary('directory', 'absent') }}"
owner: "{{ path.owner | default(postgresql_user_id, true) }}"
group: "{{ path.group | default(postgresql_user_group_id, true) }}"
mode: "{{ path.mode | default('0755', true) }}"
loop:
- name: "{{ postgresql_config_path }}"
- name: "{{ postgresql_data_path }}"
mode: "0700"
loop_control:
loop_var: path
label: "{{ path.name }}"
- name: Check for existing PG_VERSION file
ansible.builtin.stat:
path: "{{ postgresql_data_path }}/PG_VERSION"
register: postgresql_data_dir_version_info
- name: Read existing PG_VERSION file
ansible.builtin.slurp:
path: "{{ postgresql_data_path }}/PG_VERSION"
register: postgresql_data_dir_version_content
when:
- postgresql_data_dir_version_info.stat.exists
- name: Prevent major version changes
ansible.builtin.fail:
msg: >-2
Mismatched postgresql version for the data directory!
Aborting...
when:
- postgresql_data_dir_version_info.stat.exists
- "(postgresql_data_dir_version_content.content | b64decode | int) != (postgresql_major_version | int)"
- name: Prepare authentication and authorization for database admin role
ansible.builtin.include_tasks:
file: "prepare.yml"
- name: Deploy postgresql using {{ postgresql_deployment_method }}
ansible.builtin.include_tasks:
file: "deploy-{{ postgresql_deployment_method }}.yml"
- name: Configure postgresql
ansible.builtin.include_tasks:
file: "configure.yml"

View File

@ -0,0 +1,35 @@
---
- name: Ensure postgresql config files are {{ postgresql_state }}
ansible.builtin.lineinfile:
path: "{{ file.name }}"
insertafter: "{{ file.insert_after | default(omit) }}"
insertbefore: "{{ file.insert_before | default(omit) }}"
line: "{{ file.line }}"
owner: "{{ postgresql_user_id }}"
group: "{{ postgresql_user_group_id }}"
create: true
loop_control:
loop_var: file
label: "{{ file.name }}"
loop:
- name: "{{ postgresql_pg_hba_conf_file }}"
insert_before: "BOF"
line: "# Ansible managed"
- name: "{{ postgresql_pg_ident_conf_file }}"
insert_before: "BOF"
line: "# Ansible managed"
- name: "{{ postgresql_pg_ident_conf_file }}"
insert_after: "# Ansible managed"
line: "{{ postgresql_admin_pg_ident_conf }}"
when: postgresql_state == 'present'
notify: postgresql_restart
- name: Configure permissions for postgresql admin role
community.postgresql.postgresql_pg_hba:
dest: "{{ postgresql_pg_hba_conf_file }}"
contype: "{{ postgresql_admin_role_contype }}"
users: "{{ postgresql_admin_role }}"
method: "{{ postgresql_admin_role_method }}"
options: "{{ postgresql_admin_pg_hba_conf_options }}"
when: postgresql_state == 'present'
notify: postgresql_restart