diff --git a/.config/.yamllint b/.config/.yamllint index 70e071fef..82c768c6d 100644 --- a/.config/.yamllint +++ b/.config/.yamllint @@ -2,7 +2,8 @@ extends: default rules: - line-length: disable + line-length: + max: 160 comments-indentation: disable comments: min-spaces-from-content: 1 diff --git a/.config/ansible-lint.yml b/.config/ansible-lint.yml index 4fa6241e4..6c6d8e171 100644 --- a/.config/ansible-lint.yml +++ b/.config/ansible-lint.yml @@ -15,7 +15,6 @@ skip_list: - package-latest - risky-file-permissions # TODO - role-name - - yaml[line-length] exclude_paths: - ../.venv diff --git a/.gitignore b/.gitignore index 06644ce02..8601db0d2 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,6 @@ .idea .venv .vscode/settings.json -__pycache__/ \ No newline at end of file +__pycache__/ +*.log +molecule/**/tmp diff --git a/deploy_pgcluster.yml b/deploy_pgcluster.yml index 3d9ac1d3b..038dee678 100644 --- a/deploy_pgcluster.yml +++ b/deploy_pgcluster.yml @@ -24,9 +24,20 @@ system_info: OS: "{{ ansible_distribution | default('N/A') }} {{ ansible_distribution_version | default('N/A') }}" Kernel: "{{ ansible_kernel | default('N/A') }}" - CPU model: "{{ ansible_processor[2] | default('N/A') }}, count: {{ ansible_processor_count | default('N/A') }}, cores: {{ ansible_processor_cores | default('N/A') }}" + CPU model: >- + {{ ansible_processor[2] | default('N/A') }}, + count: {{ ansible_processor_count | default('N/A') }}, + cores: {{ ansible_processor_cores | default('N/A') }} Memory: "{{ (ansible_memtotal_mb / 1024) | round(2) if ansible_memtotal_mb is defined else 'N/A' }} GB" - Disk space total: "{{ (ansible_mounts | map(attribute='size_total') | map('int') | sum / 1024 / 1024 / 1024) | round(2) if ansible_mounts is defined else 'N/A' }} GB" + Disk space total: >- + {{ + (ansible_mounts + | map(attribute='size_total') + | map('int') + | sum / 1024 / 1024 / 1024 + ) + | round(2) if ansible_mounts is defined else 'N/A' + }} GB Architecture: "{{ ansible_architecture | default('N/A') }}" Virtualization type: "{{ ansible_virtualization_type | default('N/A') }}" Product name: "{{ ansible_product_name | default('N/A') }}" diff --git a/molecule/default/verify.yml b/molecule/default/verify.yml index 850461090..222f84718 100644 --- a/molecule/default/verify.yml +++ b/molecule/default/verify.yml @@ -1,15 +1,36 @@ --- -- name: Molecule | Verify | All +# 🚀 This playbook is designed to verify the default Molecule configuration +# 🎯 The objective is to ensure that all tasks and roles are correctly set up and functioning as expected + +- name: Molecule.default.verify hosts: all + gather_facts: true tasks: - - name: Include vars of vars/main.yml + # 📝 Including the main variables file for the Molecule default configuration + # This file contains all the necessary variables for the playbook + - name: Molecule.default.verify | Include Main Variables ansible.builtin.include_vars: file: ../../vars/main.yml - - name: Molecule | Verify | Include all tests + # 🔄 Including OS-specific variables + # These variables are specific to the operating system on which the playbook is running + - name: Molecule.default.verify | Include OS-Specific Variables + ansible.builtin.include_vars: + file: "../../vars/{{ ansible_os_family }}.yml" + + # 🔄 Including all tests for the Molecule default configuration + # These tests ensure that all components of the configuration are functioning correctly + - name: Molecule.default.verify | Include All Tests ansible.builtin.include_tasks: "{{ item }}" with_fileglob: + - ../tests/variables/main.yml - ../tests/etcd/*.yml - ../tests/patroni/*.yml - ../tests/postgres/*.yml + - ../tests/roles/confd/main.yml + - ../tests/roles/deploy-finish/main.yml + - ../tests/roles/haproxy/main.yml + - ../tests/roles/patroni/main.yml + - ../tests/roles/pre-checks/main.yml + - ../tests/roles/swap/main.yml diff --git a/molecule/postgrespro/vars/postgrespro_vars.yml b/molecule/postgrespro/vars/postgrespro_vars.yml index a6343d0a1..bb71e7d2b 100644 --- a/molecule/postgrespro/vars/postgrespro_vars.yml +++ b/molecule/postgrespro/vars/postgrespro_vars.yml @@ -10,7 +10,7 @@ postgresql_bin_dir: "/opt/pgpro/std-{{ postgresql_version }}/bin" postgresql_unix_socket_dir: "/tmp" # YUM Repository -_baseurl: +_baseurl: # yamllint disable rule:line-length CentOS: "https://repo.postgrespro.ru//pgpro-archive/pgpro-{{ postgrespro_minor_version }}/centos/{{ ansible_distribution_major_version }}/os/x86_64/rpms/" RedHat: "https://repo.postgrespro.ru//pgpro-archive/pgpro-{{ postgrespro_minor_version }}/rhel/{{ ansible_distribution_major_version }}Server/os/x86_64/rpms/" OracleLinux: "https://repo.postgrespro.ru//pgpro-archive/pgpro-{{ postgrespro_minor_version }}/oraclelinux/{{ ansible_distribution_major_version }}Server/os/x86_64/rpms/" @@ -25,7 +25,10 @@ yum_repository: apt_repository_keys: - key: "https://repo.postgrespro.ru/keys/GPG-KEY-POSTGRESPRO" apt_repository: - - repo: "deb https://repo.postgrespro.ru//pgpro-archive/pgpro-{{ postgrespro_minor_version }}/{{ ansible_distribution | lower }}/ {{ ansible_distribution_release }} main" + - repo: >- + {{ 'deb https://repo.postgrespro.ru//pgpro-archive/pgpro-' ~ + postgrespro_minor_version ~ '/' ~ (ansible_distribution | lower) ~ '/' ~ + ' ' ~ ansible_distribution_release ~ ' main' }} install_postgresql_repo: false diff --git a/molecule/tests/roles/confd/main.yml b/molecule/tests/roles/confd/main.yml new file mode 100644 index 000000000..5a3b3b6ed --- /dev/null +++ b/molecule/tests/roles/confd/main.yml @@ -0,0 +1,13 @@ +--- +# 🚀 This task is designed to include variable tests for the main role in the Confd molecule tests +# 🎯 The goal is to ensure that all variable tests are executed in a systematic and organised manner + +# 🔄 Including variable tests for the main role in the Confd molecule tests +# We use a loop to include all YAML files in the 'variables' directory +# Each file is included as a task, ensuring that all variable tests are executed +- name: Molecule.tests.roles.confd.main | Include Variable Tests + run_once: true + ansible.builtin.include_tasks: "{{ molecule_tests_roles_confd_main_file }}" + loop: "{{ lookup('fileglob', 'variables/*.yml', wantlist=True) }}" + loop_control: + loop_var: molecule_tests_roles_confd_main_file diff --git a/molecule/tests/roles/confd/variables/haproxy.tmpl.yml b/molecule/tests/roles/confd/variables/haproxy.tmpl.yml new file mode 100644 index 000000000..7ebe5a3e3 --- /dev/null +++ b/molecule/tests/roles/confd/variables/haproxy.tmpl.yml @@ -0,0 +1,162 @@ +--- +# 🚀 These tasks aim to test the "ansible.builtin.lineinfile" task +# 🎯 The objective is to ensure that the lines are correctly replaced + +# 📂 Ensure tmp directory exists +- name: Molecule.tests.roles.confd.variables.haproxy.tmpl | Ensure tmp directory exists + run_once: true + delegate_to: localhost + ansible.builtin.file: + path: "./tmp" + state: directory + +# 🔄 Define a dummy template file +- name: Molecule.tests.roles.confd.variables.haproxy.tmpl | Set template file test data + ansible.builtin.set_fact: + haproxy_listen_port: + master: 5000 + replicas: 5001 + replicas_sync: 5002 + replicas_async: 5003 + stats: 7000 + inventory_hostname: una.name + cluster_vip: fake.vip.url.com + +# =============================== +# 💻 Case cluster_vip is defined +# =============================== + +# 📝 Establishing test data for haproxy.cluster_vip.defined.tmpl +- name: Molecule.tests.roles.confd.variables.haproxy.tmpl | Establish haproxy.tmpl Test Data + run_once: true + delegate_to: localhost + ansible.builtin.copy: + dest: "./tmp/haproxy.cluster_vip.defined.tmpl" + content: | + bind *:{{ haproxy_listen_port.stats }} + bind *:{{ haproxy_listen_port.master }} + bind *:{{ haproxy_listen_port.replicas }} + bind *:{{ haproxy_listen_port.replicas_sync }} + bind *:{{ haproxy_listen_port.replicas_async }} + +# 🚀 Execute the main task here +# This task updates the 'haproxy.tmpl' file +# replacing lines that start with 'bind' and include specific ports +# The new lines will either bind to the inventory_hostname or the cluster_vip, depending on the specific port. +- name: Molecule.tests.roles.confd.variables.haproxy.tmpl | Update haproxy.tmpl (replace "bind") + run_once: true + delegate_to: localhost + ansible.builtin.lineinfile: + path: ./tmp/haproxy.cluster_vip.defined.tmpl + regexp: "{{ bind_config_with_vip_item.regexp }}" + line: "{{ bind_config_with_vip_item.line }}" + backrefs: true + loop: + - regexp: '^.*bind.*:{{ haproxy_listen_port.stats }}$' + line: ' bind {{ inventory_hostname }}:{{ haproxy_listen_port.stats }}' + - regexp: '^.*bind.*:{{ haproxy_listen_port.master }}$' + line: ' bind {{ cluster_vip }}:{{ haproxy_listen_port.master }}' + - regexp: '^.*bind.*:{{ haproxy_listen_port.replicas }}$' + line: ' bind {{ cluster_vip }}:{{ haproxy_listen_port.replicas }}' + - regexp: '^.*bind.*:{{ haproxy_listen_port.replicas_sync }}$' + line: ' bind {{ cluster_vip }}:{{ haproxy_listen_port.replicas_sync }}' + - regexp: '^.*bind.*:{{ haproxy_listen_port.replicas_async }}$' + line: ' bind {{ cluster_vip }}:{{ haproxy_listen_port.replicas_async }}' + loop_control: + loop_var: bind_config_with_vip_item + label: "{{ bind_config_with_vip_item.line }}" + +# 🖨️ Debugging the established haproxy.tmpl +- name: Molecule.tests.roles.confd.variables.haproxy.tmpl | Debug haproxy.tmpl + run_once: true + delegate_to: localhost + ansible.builtin.command: + cmd: cat ./tmp/haproxy.cluster_vip.defined.tmpl + register: output +- name: Molecule.tests.roles.confd.variables.haproxy.tmpl | Debug haproxy.tmpl content + run_once: true + ansible.builtin.debug: + var: output.stdout_lines + +# ✅ Verifying the correctness of the established haproxy.tmpl +# If the lines are not replaced correctly, the test fails and an error message is displayed +- name: Molecule.tests.roles.confd.variables.haproxy.tmpl | Validate updated haproxy.tmpl + run_once: true + ansible.builtin.assert: + that: + - "output.stdout_lines[0] == ' bind una.name:7000'" + - "output.stdout_lines[1] == ' bind fake.vip.url.com:5000'" + - "output.stdout_lines[2] == ' bind fake.vip.url.com:5001'" + - "output.stdout_lines[3] == ' bind fake.vip.url.com:5002'" + - "output.stdout_lines[4] == ' bind fake.vip.url.com:5003'" + fail_msg: "Test failed: Lines are not replaced correctly in haproxy.tmpl." + success_msg: "Test passed: Lines are replaced correctly in haproxy.tmpl." + +# =================================== +# 💻 Case cluster_vip is not defined +# =================================== + +# 📝 Establishing test data for haproxy.cluster_vip.not.defined.tmpl +- name: Molecule.tests.roles.confd.variables.haproxy.tmpl | Establish haproxy.tmpl Test Data - 2nd round + run_once: true + delegate_to: localhost + ansible.builtin.copy: + dest: "./tmp/haproxy.cluster_vip.not.defined.tmpl" + content: | + bind *:{{ haproxy_listen_port.stats }} + bind *:{{ haproxy_listen_port.master }} + bind *:{{ haproxy_listen_port.replicas }} + bind *:{{ haproxy_listen_port.replicas_sync }} + bind *:{{ haproxy_listen_port.replicas_async }} + +# 🚀 Execute the new task here +# This task updates the 'haproxy.tmpl' file again, this time replacing lines that start with 'bind' and include specific ports +# The new lines will bind to the inventory_hostname. +- name: Molecule.tests.roles.confd.variables.haproxy.tmpl | Prepare haproxy.tmpl template file (replace "bind" - 2nd round) + run_once: true + delegate_to: localhost + ansible.builtin.lineinfile: + path: ./tmp/haproxy.cluster_vip.not.defined.tmpl + regexp: "{{ bind_config_without_vip_item.regexp }}" + line: "{{ bind_config_without_vip_item.line }}" + backrefs: true + loop: + - regexp: '^.*bind.*:{{ haproxy_listen_port.stats }}$' + line: ' bind {{ inventory_hostname }}:{{ haproxy_listen_port.stats }}' + - regexp: '^.*bind.*:{{ haproxy_listen_port.master }}$' + line: ' bind {{ inventory_hostname }}:{{ haproxy_listen_port.master }}' + - regexp: '^.*bind.*:{{ haproxy_listen_port.replicas }}$' + line: ' bind {{ inventory_hostname }}:{{ haproxy_listen_port.replicas }}' + - regexp: '^.*bind.*:{{ haproxy_listen_port.replicas_sync }}$' + line: ' bind {{ inventory_hostname }}:{{ haproxy_listen_port.replicas_sync }}' + - regexp: '^.*bind.*:{{ haproxy_listen_port.replicas_async }}$' + line: ' bind {{ inventory_hostname }}:{{ haproxy_listen_port.replicas_async }}' + loop_control: + loop_var: bind_config_without_vip_item + label: "{{ bind_config_without_vip_item.line }}" + +# 🖨️ Debugging the established haproxy.tmpl - 2nd round +- name: Molecule.tests.roles.confd.variables.haproxy.tmpl | Debug haproxy.tmpl - 2nd round + run_once: true + delegate_to: localhost + ansible.builtin.command: + cmd: cat ./tmp/haproxy.cluster_vip.not.defined.tmpl + register: output_2 +- name: Molecule.tests.roles.confd.variables.haproxy.tmpl | Debug haproxy.tmpl content - 2nd round + run_once: true + ansible.builtin.debug: + var: output_2.stdout_lines + +# ✅ Verifying the correctness of the established haproxy.tmpl - 2nd round +# If the lines are not replaced correctly, the test fails and an error message is displayed +- name: Molecule.tests.roles.confd.variables.haproxy.tmpl | Verify haproxy.tmpl - 2nd round + run_once: true + ansible.builtin.assert: + that: + - "output_2.stdout_lines[0] == ' bind una.name:7000'" + - "output_2.stdout_lines[1] == ' bind una.name:5000'" + - "output_2.stdout_lines[2] == ' bind una.name:5001'" + - "output_2.stdout_lines[3] == ' bind una.name:5002'" + - "output_2.stdout_lines[4] == ' bind una.name:5003'" + fail_msg: "Test failed: Lines are not replaced correctly in haproxy.tmpl - 2nd round." + success_msg: "Test passed: Lines are replaced correctly in haproxy.tmpl - 2nd round." diff --git a/molecule/tests/roles/deploy-finish/main.yml b/molecule/tests/roles/deploy-finish/main.yml new file mode 100644 index 000000000..3f1ae71f5 --- /dev/null +++ b/molecule/tests/roles/deploy-finish/main.yml @@ -0,0 +1,13 @@ +--- +# 🚀 This task is designed to include and execute a series of variable tests for the 'deploy-finish' role in our Molecule test suite. +# 🎯 The objective is to ensure that all variable tests are run in a systematic and efficient manner, thus ensuring the integrity of our deployment process. + +# 🔄 Including and executing variable tests for the 'deploy-finish' role +# We use a loop to iterate over all the YAML files in the 'variables' directory, and for each file, we include its tasks in the current playbook. +# This allows us to run a comprehensive set of variable tests in an automated and efficient manner. +- name: Molecule.tests.roles.deploy-finish.main | Include and Execute Variable Tests + run_once: true + ansible.builtin.include_tasks: "{{ molecule_tests_roles_deploy_finish_main_file }}" + loop: "{{ lookup('fileglob', 'variables/*.yml', wantlist=True) }}" + loop_control: + loop_var: molecule_tests_roles_deploy_finish_main_file diff --git a/molecule/tests/roles/deploy-finish/variables/haproxy_nodes.yml b/molecule/tests/roles/deploy-finish/variables/haproxy_nodes.yml new file mode 100644 index 000000000..9ed434853 --- /dev/null +++ b/molecule/tests/roles/deploy-finish/variables/haproxy_nodes.yml @@ -0,0 +1,71 @@ +--- +# 🚀 These tasks aim to test the haproxy_nodes variable +# 🎯 The objective is to guarantee that the list of nodes is correctly set + +# ============================================ +# 💻 Start haproxy_nodes Operations and Tests +# ============================================ + +# 📝 Establishing test data for haproxy_nodes +- name: Molecule.tests.rolesdeploy-finish.variables.haproxy_nodes | Establish haproxy_nodes Test Data + run_once: true + ansible.builtin.set_fact: + haproxy_nodes: >- + {{ + groups['balancers'] + | default([]) + | map('extract', hostvars, 'inventory_hostname') + | join(',') + }} + +# 🖨️ Debugging the established haproxy_nodes +- name: Molecule.tests.rolesdeploy-finish.variables.haproxy_nodes | Debug haproxy_nodes + run_once: true + ansible.builtin.debug: + var: haproxy_nodes + +# ✅ Verifying the correctness of the established haproxy_nodes +# If the haproxy_nodes is not set, the test fails and an error message is displayed +- name: Molecule.tests.rolesdeploy-finish.variables.haproxy_nodes | Verify haproxy_nodes + run_once: true + ansible.builtin.assert: + that: + - "haproxy_nodes is not none" + - "haproxy_nodes != 'N/A'" + - "haproxy_nodes == 'una.name,10.172.0.21,10.172.0.22'" + fail_msg: "Test failed: haproxy_nodes is not set correctly." + success_msg: "Test passed: haproxy_nodes is set correctly." + +# ===================================================== +# 💻 Start postgres_cluster_nodes Operations and Tests +# ===================================================== + +# 📝 Establishing test data for postgres_cluster_nodes +- name: Molecule.tests.rolesdeploy-finish.variables.haproxy_nodes | Establish postgres_cluster_nodes Test Data + run_once: true + ansible.builtin.set_fact: + postgres_cluster_nodes: >- + {{ + groups['postgres_cluster'] + | default([]) + | map('extract', hostvars, 'inventory_hostname') + | join(',') + }} + +# 🖨️ Debugging the established postgres_cluster_nodes +- name: Molecule.tests.rolesdeploy-finish.variables.haproxy_nodes | Debug postgres_cluster_nodes + run_once: true + ansible.builtin.debug: + var: postgres_cluster_nodes + +# ✅ Verifying the correctness of the established postgres_cluster_nodes +# If the postgres_cluster_nodes is not set, the test fails and an error message is displayed +- name: Molecule.tests.rolesdeploy-finish.variables.haproxy_nodes | Verify postgres_cluster_nodes + run_once: true + ansible.builtin.assert: + that: + - "postgres_cluster_nodes is not none" + - "postgres_cluster_nodes != 'N/A'" + - "postgres_cluster_nodes == 'una.name,10.172.0.21,10.172.0.22'" + fail_msg: "Test failed: postgres_cluster_nodes is not set correctly." + success_msg: "Test passed: postgres_cluster_nodes is set correctly." diff --git a/molecule/tests/roles/haproxy/main.yml b/molecule/tests/roles/haproxy/main.yml new file mode 100644 index 000000000..0eb59b4d0 --- /dev/null +++ b/molecule/tests/roles/haproxy/main.yml @@ -0,0 +1,13 @@ +--- +# 🚀 The purpose of this task is to incorporate a series of variable tests for HAProxy, a reliable, high performance TCP/HTTP load balancer +# 🎯 The objective is to ensure that the variables used in the HAProxy configuration are correctly defined and functional + +# 🔄 Including variable tests for HAProxy +# This task iterates over all the YAML files in the 'variables' directory, and includes each file's tasks in the current playbook +# If a variable test fails, it will be immediately apparent, aiding in debugging and ensuring the robustness of the HAProxy configuration +- name: Molecule.tests.roles.haproxy.main | Incorporate Variable Tests + run_once: true + ansible.builtin.include_tasks: "{{ molecule_tests_roles_haproxy_main_file }}" + loop: "{{ lookup('fileglob', 'variables/*.yml', wantlist=True) }}" + loop_control: + loop_var: molecule_tests_roles_haproxy_main_file diff --git a/molecule/tests/roles/haproxy/variables/haproxy.cfg.yml b/molecule/tests/roles/haproxy/variables/haproxy.cfg.yml new file mode 100644 index 000000000..2c0c945fc --- /dev/null +++ b/molecule/tests/roles/haproxy/variables/haproxy.cfg.yml @@ -0,0 +1,163 @@ +--- +# 🚀 These tasks aim to test the "ansible.builtin.lineinfile" task +# 🎯 The objective is to ensure that the lines are correctly replaced + +# 📂 Ensure tmp directory exists +- name: Molecule.tests.roles.confd.variables.haproxy.cfg | Ensure tmp directory exists + run_once: true + delegate_to: localhost + ansible.builtin.file: + path: "./tmp" + state: directory + +# 🔄 Define a dummy template file +- name: Molecule.tests.roles.confd.variables.haproxy.cfg | Set template file test data + run_once: true + ansible.builtin.set_fact: + haproxy_listen_port: + master: 5000 + replicas: 5001 + replicas_sync: 5002 + replicas_async: 5003 + stats: 7000 + inventory_hostname: una.name + cluster_vip: fake.vip.url.com + +# =============================== +# 💻 Case cluster_vip is defined +# =============================== + +# 📝 Establishing test data for haproxy.cluster_vip.defined.cfg +- name: Molecule.tests.roles.confd.variables.haproxy.cfg | Establish haproxy.cfg Test Data + run_once: true + delegate_to: localhost + ansible.builtin.copy: + dest: "./tmp/haproxy.cluster_vip.defined.cfg" + content: | + bind *:{{ haproxy_listen_port.stats }} + bind *:{{ haproxy_listen_port.master }} + bind *:{{ haproxy_listen_port.replicas }} + bind *:{{ haproxy_listen_port.replicas_sync }} + bind *:{{ haproxy_listen_port.replicas_async }} + +# 🚀 Execute the main task here +# This task updates the 'haproxy.cfg' file +# replacing lines that start with 'bind' and include specific ports +# The new lines will either bind to the inventory_hostname or the cluster_vip, depending on the specific port. +- name: Molecule.tests.roles.confd.variables.haproxy.cfg | Update haproxy.cfg (replace "bind") + run_once: true + delegate_to: localhost + ansible.builtin.lineinfile: + path: ./tmp/haproxy.cluster_vip.defined.cfg + regexp: "{{ bind_config_with_vip_item.regexp }}" + line: "{{ bind_config_with_vip_item.line }}" + backrefs: true + loop: + - regexp: '^.*bind.*:{{ haproxy_listen_port.stats }}$' + line: ' bind {{ inventory_hostname }}:{{ haproxy_listen_port.stats }}' + - regexp: '^.*bind.*:{{ haproxy_listen_port.master }}$' + line: ' bind {{ cluster_vip }}:{{ haproxy_listen_port.master }}' + - regexp: '^.*bind.*:{{ haproxy_listen_port.replicas }}$' + line: ' bind {{ cluster_vip }}:{{ haproxy_listen_port.replicas }}' + - regexp: '^.*bind.*:{{ haproxy_listen_port.replicas_sync }}$' + line: ' bind {{ cluster_vip }}:{{ haproxy_listen_port.replicas_sync }}' + - regexp: '^.*bind.*:{{ haproxy_listen_port.replicas_async }}$' + line: ' bind {{ cluster_vip }}:{{ haproxy_listen_port.replicas_async }}' + loop_control: + loop_var: bind_config_with_vip_item + label: "{{ bind_config_with_vip_item.line }}" + +# 🖨️ Debugging the established haproxy.cfg +- name: Molecule.tests.roles.confd.variables.haproxy.cfg | Debug haproxy.cfg + run_once: true + delegate_to: localhost + ansible.builtin.command: + cmd: cat ./tmp/haproxy.cluster_vip.defined.cfg + register: output +- name: Molecule.tests.roles.confd.variables.haproxy.cfg | Debug haproxy.cfg content + run_once: true + ansible.builtin.debug: + var: output.stdout_lines + +# ✅ Verifying the correctness of the established haproxy.cfg +# If the lines are not replaced correctly, the test fails and an error message is displayed +- name: Molecule.tests.roles.confd.variables.haproxy.cfg | Validate updated haproxy.cfg + run_once: true + ansible.builtin.assert: + that: + - "output.stdout_lines[0] == ' bind una.name:7000'" + - "output.stdout_lines[1] == ' bind fake.vip.url.com:5000'" + - "output.stdout_lines[2] == ' bind fake.vip.url.com:5001'" + - "output.stdout_lines[3] == ' bind fake.vip.url.com:5002'" + - "output.stdout_lines[4] == ' bind fake.vip.url.com:5003'" + fail_msg: "Test failed: Lines are not replaced correctly in haproxy.cfg." + success_msg: "Test passed: Lines are replaced correctly in haproxy.cfg." + +# =================================== +# 💻 Case cluster_vip is not defined +# =================================== + +# 📝 Establishing test data for haproxy.cluster_vip.not.defined.cfg +- name: Molecule.tests.roles.confd.variables.haproxy.cfg | Establish haproxy.cfg Test Data - 2nd round + run_once: true + delegate_to: localhost + ansible.builtin.copy: + dest: "./tmp/haproxy.cluster_vip.not.defined.cfg" + content: | + bind *:{{ haproxy_listen_port.stats }} + bind *:{{ haproxy_listen_port.master }} + bind *:{{ haproxy_listen_port.replicas }} + bind *:{{ haproxy_listen_port.replicas_sync }} + bind *:{{ haproxy_listen_port.replicas_async }} + +# 🚀 Execute the new task here +# This task updates the 'haproxy.cfg' file again, this time replacing lines that start with 'bind' and include specific ports +# The new lines will bind to the inventory_hostname. +- name: Molecule.tests.roles.confd.variables.haproxy.cfg | Prepare haproxy.cfg template file (replace "bind" - 2nd round) + run_once: true + delegate_to: localhost + ansible.builtin.lineinfile: + path: ./tmp/haproxy.cluster_vip.not.defined.cfg + regexp: "{{ bind_config_without_vip_item.regexp }}" + line: "{{ bind_config_without_vip_item.line }}" + backrefs: true + loop: + - regexp: '^.*bind.*:{{ haproxy_listen_port.stats }}$' + line: ' bind {{ inventory_hostname }}:{{ haproxy_listen_port.stats }}' + - regexp: '^.*bind.*:{{ haproxy_listen_port.master }}$' + line: ' bind {{ inventory_hostname }}:{{ haproxy_listen_port.master }}' + - regexp: '^.*bind.*:{{ haproxy_listen_port.replicas }}$' + line: ' bind {{ inventory_hostname }}:{{ haproxy_listen_port.replicas }}' + - regexp: '^.*bind.*:{{ haproxy_listen_port.replicas_sync }}$' + line: ' bind {{ inventory_hostname }}:{{ haproxy_listen_port.replicas_sync }}' + - regexp: '^.*bind.*:{{ haproxy_listen_port.replicas_async }}$' + line: ' bind {{ inventory_hostname }}:{{ haproxy_listen_port.replicas_async }}' + loop_control: + loop_var: bind_config_without_vip_item + label: "{{ bind_config_without_vip_item.line }}" + +# 🖨️ Debugging the established haproxy.cfg - 2nd round +- name: Molecule.tests.roles.confd.variables.haproxy.cfg | Debug haproxy.cfg - 2nd round + run_once: true + delegate_to: localhost + ansible.builtin.command: + cmd: cat ./tmp/haproxy.cluster_vip.not.defined.cfg + register: output_2 +- name: Molecule.tests.roles.confd.variables.haproxy.cfg | Debug haproxy.cfg content - 2nd round + run_once: true + ansible.builtin.debug: + var: output_2.stdout_lines + +# ✅ Verifying the correctness of the established haproxy.cfg - 2nd round +# If the lines are not replaced correctly, the test fails and an error message is displayed +- name: Molecule.tests.roles.confd.variables.haproxy.cfg | Verify haproxy.cfg - 2nd round + run_once: true + ansible.builtin.assert: + that: + - "output_2.stdout_lines[0] == ' bind una.name:7000'" + - "output_2.stdout_lines[1] == ' bind una.name:5000'" + - "output_2.stdout_lines[2] == ' bind una.name:5001'" + - "output_2.stdout_lines[3] == ' bind una.name:5002'" + - "output_2.stdout_lines[4] == ' bind una.name:5003'" + fail_msg: "Test failed: Lines are not replaced correctly in haproxy.cfg - 2nd round." + success_msg: "Test passed: Lines are replaced correctly in haproxy.cfg - 2nd round." diff --git a/molecule/tests/roles/patroni/main.yml b/molecule/tests/roles/patroni/main.yml new file mode 100644 index 000000000..3b5cca5e6 --- /dev/null +++ b/molecule/tests/roles/patroni/main.yml @@ -0,0 +1,13 @@ +--- +# 🚀 The objective of this task is to include and execute variable tests for the main Patroni role +# 🎯 This ensures that all variable tests are run, providing comprehensive coverage and validation of the role's variables + +# 🔄 Including and executing variable tests for the main Patroni role +# We use a loop to iterate over all .yml files in the 'variables' directory +# Each file is included and its tasks are executed +- name: Molecule.tests.roles.patroni.main | Include and Execute Variable Tests + run_once: true + ansible.builtin.include_tasks: "{{ molecule_tests_roles_patroni_main_file }}" + loop: "{{ lookup('fileglob', 'variables/*.yml', wantlist=True) }}" + loop_control: + loop_var: molecule_tests_roles_patroni_main_file diff --git a/molecule/tests/roles/patroni/variables/custom_wal_dir.yml b/molecule/tests/roles/patroni/variables/custom_wal_dir.yml new file mode 100644 index 000000000..71b069314 --- /dev/null +++ b/molecule/tests/roles/patroni/variables/custom_wal_dir.yml @@ -0,0 +1,119 @@ +--- +# 🚀 These tasks aim to validate the "custom_wal_dir" task +# 🎯 The goal is to verify the correct display of renaming based on PostgreSQL version + +# 🔄 Set postgresql_version for the first test scenario +- name: Molecule.tests.roles.patroni.variables.custom_wal_dir | Define PG version as 11 for scenario 1 + run_once: true + ansible.builtin.set_fact: + postgresql_version: 11 + +# ====================================== +# 💻 Scenario: PostgreSQL version >= 10 +# ====================================== + +# 🔄 Determine base pg_wal_dir name +- name: Molecule.tests.roles.patroni.variables.custom_wal_dir | Set pg_wal_dir based on postgresql_version + run_once: true + ansible.builtin.set_fact: + pg_wal_dir: "{{ 'pg_wal' if postgresql_version is version('10', '>=') else 'pg_xlog' }}" + +# 🔄 Determine the name based on postgresql_version +- name: Molecule.tests.roles.patroni.variables.custom_wal_dir | Determine name for scenario 1 + run_once: true + ansible.builtin.set_fact: + name_postgresql_version_11: "Rename {{ pg_wal_dir }} to {{ pg_wal_dir }}_old" + +# 🔄 Determine the mv command based on postgresql_version +- name: Molecule.tests.roles.patroni.variables.custom_wal_dir | Determine mv command for 'pg_wal' or 'pg_xlog' for scenario 1 + run_once: true + ansible.builtin.set_fact: + mv_command_postgresql_version_11: "mv {{ postgresql_data_dir }}/{{ pg_wal_dir }} {{ postgresql_data_dir }}/{{ pg_wal_dir }}_old" + +# 🚀 Display the name +- name: Molecule.tests.roles.patroni.variables.custom_wal_dir | Display name for scenario 1 + run_once: true + ansible.builtin.debug: + var: name_postgresql_version_11 + +# 🚀 Display the command +- name: Molecule.tests.roles.patroni.variables.custom_wal_dir | Display command for scenario 1 + run_once: true + ansible.builtin.debug: + var: mv_command_postgresql_version_11 + +# ✅ Verify if the name has been determined correctly +- name: Molecule.tests.roles.patroni.variables.custom_wal_dir | Validate name for scenario 1 + run_once: true + ansible.builtin.assert: + that: + - name_postgresql_version_11 == 'Rename pg_wal to pg_wal_old' + fail_msg: "Test failed: The name has not been determined correctly." + success_msg: "Test passed: The name has been determined correctly." + +# ✅ Verify if the command has been determined correctly +- name: Molecule.tests.roles.patroni.variables.custom_wal_dir | Validate command for scenario 1 + run_once: true + ansible.builtin.assert: + that: + - mv_command_postgresql_version_11 == 'mv /var/lib/pgsql/11/data/pg_wal /var/lib/pgsql/11/data/pg_wal_old' + fail_msg: "Test failed: The command has not been determined correctly." + success_msg: "Test passed: The command has been determined correctly." + +# ===================================== +# 💻 Scenario: PostgreSQL version < 10 +# ===================================== + +# 🔄 Set postgresql_version for the second test scenario +- name: Molecule.tests.roles.patroni.variables.custom_wal_dir | Define PG version as 9.6 for scenario 2 + run_once: true + ansible.builtin.set_fact: + postgresql_version: 9.6 + +# 🔄 Determine base pg_wal_dir name +- name: Molecule.tests.roles.patroni.variables.custom_wal_dir | Set pg_wal_dir based on postgresql_version + run_once: true + ansible.builtin.set_fact: + pg_wal_dir: "{{ 'pg_wal' if postgresql_version is version('10', '>=') else 'pg_xlog' }}" + +# 🔄 Determine the name based on postgresql_version +- name: Molecule.tests.roles.patroni.variables.custom_wal_dir | Determine name for scenario 2 + run_once: true + ansible.builtin.set_fact: + name_postgresql_version_9: "Rename {{ pg_wal_dir }} to {{ pg_wal_dir }}_old" + +# 🔄 Determine the mv command based on postgresql_version +- name: Molecule.tests.roles.patroni.variables.custom_wal_dir | Determine mv command for scenario 2 + run_once: true + ansible.builtin.set_fact: + mv_command_postgresql_version_9: "mv {{ postgresql_data_dir }}/{{ pg_wal_dir }} {{ postgresql_data_dir }}/{{ pg_wal_dir }}_old" + +# 🚀 Display the name +- name: Molecule.tests.roles.patroni.variables.custom_wal_dir | Display name for scenario 2 + run_once: true + ansible.builtin.debug: + var: name_postgresql_version_9 + +# 🚀 Display the command +- name: Molecule.tests.roles.patroni.variables.custom_wal_dir | Display command for scenario 2 + run_once: true + ansible.builtin.debug: + var: mv_command_postgresql_version_9 + +# ✅ Verify if the name has been determined correctly +- name: Molecule.tests.roles.patroni.variables.custom_wal_dir | Validate name for scenario 2 + run_once: true + ansible.builtin.assert: + that: + - name_postgresql_version_9 == 'Rename pg_xlog to pg_xlog_old' + fail_msg: "Test failed: The name has not been determined correctly in scenario 2." + success_msg: "Test passed: The name has been determined correctly in scenario 2." + +# ✅ Verify if the command has been determined correctly +- name: Molecule.tests.roles.patroni.variables.custom_wal_dir | Validate command for scenario 2 + run_once: true + ansible.builtin.assert: + that: + - mv_command_postgresql_version_9 == 'mv /var/lib/pgsql/9.6/data/pg_xlog /var/lib/pgsql/9.6/data/pg_xlog_old' + fail_msg: "Test failed: The command has not been determined correctly." + success_msg: "Test passed: The command has been determined correctly." diff --git a/molecule/tests/roles/pre-checks/main.yml b/molecule/tests/roles/pre-checks/main.yml new file mode 100644 index 000000000..525908bf8 --- /dev/null +++ b/molecule/tests/roles/pre-checks/main.yml @@ -0,0 +1,13 @@ +--- +# 🚀 This task is designed to include variable tests in the main pre-checks for Molecule tests +# 🎯 The objective is to ensure that all variable tests are properly included and executed + +# 🔄 Including variable tests in the main pre-checks for Molecule tests +# For each YAML file in the 'variables' directory, we include its tasks in the main pre-checks +# If a file does not exist or cannot be read, the task will fail and an error message will be displayed +- name: Molecule.tests.roles.pre-checks.main | Include Variable Tests in Main Pre-checks + run_once: true + ansible.builtin.include_tasks: "{{ molecule_tests_roles_pre_checks_main_file }}" + loop: "{{ lookup('fileglob', 'variables/*.yml', wantlist=True) }}" + loop_control: + loop_var: molecule_tests_roles_pre_checks_main_file diff --git a/molecule/tests/roles/pre-checks/variables/pgbouncer.yml b/molecule/tests/roles/pre-checks/variables/pgbouncer.yml new file mode 100644 index 000000000..20423f952 --- /dev/null +++ b/molecule/tests/roles/pre-checks/variables/pgbouncer.yml @@ -0,0 +1,153 @@ +--- +# 🚀 These tasks aim to compute the overall pool size for PgBouncer, a PostgreSQL connection pooler +# 🎯 The objective is to guarantee that each database's pool size is precisely defined and calculated + +# 📝 Establishing test data for PgBouncer pools. Two databases are specified, each with a pool size indicated in pool_parameters +- name: Molecule.tests.roles.pre-checks.variables.pgbouncer | Establish PgBouncer Pools Test Data + run_once: true + ansible.builtin.set_fact: + pgbouncer_pools: + - dbname: db1 + pool_parameters: "pool_size=10" + - dbname: db2 + pool_parameters: "pool_size=20" + +# ================================================== +# 💾 Start pgbouncer_pool_size operations and tests +# ================================================== + +# 🔄 Calculating the overall pool size (pgbouncer_pool_size) +# For each database in pgbouncer_pools, we extract the pool size from pool_parameters and add it to the total +# If pool size is undefined, we use the default pool size (pgbouncer_default_pool_size), or 0 if that's also undefined +- name: Molecule.tests.roles.pre-checks.variables.pgbouncer | Compute Overall Pool Size + run_once: true + ansible.builtin.set_fact: + pgbouncer_pool_size: "{{ + (pgbouncer_pool_size | default(0) | int) + + + (pool_item.pool_parameters + | regex_search('pool_size=(\\d+)', multiline=False) + | regex_replace('[^0-9]', '') + | default(pgbouncer_default_pool_size | default(0), true) + | int) + }}" + loop: "{{ pgbouncer_pools | default([]) }}" + loop_control: + loop_var: pool_item + +# 🖨️ Debugging the calculated overall pool size +- name: Molecule.tests.roles.pre-checks.variables.pgbouncer | Debug Overall Pool Size + run_once: true + ansible.builtin.debug: + var: pgbouncer_pool_size + +# ✅ Verifying the correctness of the calculated overall pool size +# The expected overall pool size is 30 (10 from db1 and 20 from db2) +# If the calculated overall pool size is not 30, the test fails and an error message is displayed +- name: Molecule.tests.roles.pre-checks.variables.pgbouncer | Verify Pool Size Calculation + run_once: true + ansible.builtin.assert: + that: + - pgbouncer_pool_size | int == 30 + fail_msg: "Test failed: pgbouncer_pool_size is not equal to 30." + success_msg: "Test passed: pgbouncer_pool_size is equal to 30." + +# ======================================================== +# 🚀 Start pgbouncer_total_pool_size operations and tests +# ======================================================== + +# 🖨️ Debugging test variables +- name: Molecule.tests.roles.pre-checks.variables.pgbouncer | Debug Test Variables + run_once: true + ansible.builtin.debug: + var: "{{ debug_item }}" + loop: + - postgresql_databases + - pgbouncer_pools + - pgbouncer_default_pool_size + loop_control: + loop_var: debug_item + +# 📝 Establishing test data for PostgreSQL databases +- name: Molecule.tests.roles.pre-checks.variables.pgbouncer | Establish PostgreSQL Databases Test Data + run_once: true + ansible.builtin.set_fact: + postgresql_databases: + - db: db1 + - db: db2 + - db: db3 + +# 🔄 Calculating the overall pool size (pgbouncer_total_pool_size) across all databases +- name: Molecule.tests.roles.pre-checks.variables.pgbouncer | Compute Total Pool Size + run_once: true + ansible.builtin.set_fact: + pgbouncer_total_pool_size: >- + {{ + (pgbouncer_pool_size | int) + + + (postgresql_databases + | default([]) + | rejectattr('db', 'in', pgbouncer_pools | map(attribute='dbname') | list) + | length + ) * (pgbouncer_default_pool_size | default(0) | int) + }} + when: pgbouncer_pool_size is defined + +# 🖨️ Debugging the calculated overall pool size +- name: Molecule.tests.roles.pre-checks.variables.pgbouncer | Debug Total Pool Size + run_once: true + ansible.builtin.debug: + var: pgbouncer_total_pool_size + +# ✅ Verifying the correctness of the calculated overall pool size +# The expected overall pool size is 50 +# 10 from db1 +# 20 from db2 +# 20 from db3 as db3 is not defined in pgbouncer_pools and hence, its pool size is pgbouncer_default_pool_size which is 20 +# If the calculated overall pool size is not 50, the test fails and an error message is displayed +- name: Molecule.tests.roles.pre-checks.variables.pgbouncer | Verify Total Pool Size Calculation + run_once: true + ansible.builtin.assert: + that: + - pgbouncer_total_pool_size | int == 50 + fail_msg: "Test failed: pgbouncer_total_pool_size is not equal to 50." + success_msg: "Test passed: pgbouncer_total_pool_size is equal to 50." + +# ====================================================================== +# 📊 Start pgbouncer_total_pool_size (postgresql_databases not defined) +# ====================================================================== + +# 📝 Overriding postgresql_databases to an empty list for the next test +- name: Molecule.tests.roles.pre-checks.variables.pgbouncer | Override postgresql_databases + run_once: true + ansible.builtin.set_fact: + postgresql_databases: [] + +# 🔄 Compute the aggregate pool size (pgbouncer_total_pool_size) across all databases with postgresql_databases not defined. +- name: Molecule.tests.roles.pre-checks.variables.pgbouncer | Compute Total Pool Size (postgresql_databases not defined) + run_once: true + ansible.builtin.set_fact: + pgbouncer_total_pool_size: >- + {{ + (pgbouncer_pool_size | int) + + + (postgresql_databases + | default([]) + | rejectattr('db', 'in', pgbouncer_pools | map(attribute='dbname') | list) + | length + ) * (pgbouncer_default_pool_size | default(0) | int) + }} + when: pgbouncer_pool_size is defined + +# ✅ Test if the computed aggregate pool size is correct with postgresql_databases not defined. +# In this case, we expect the aggregate pool size to be 30 +# 10 from db1 +# 20 from db2 +# 0 from db3 as postgresql_databases is not defined +- name: Molecule.tests.roles.pre-checks.variables.pgbouncer | Verify Total Pool Size Calculation (postgresql_databases not defined) + run_once: true + ansible.builtin.assert: + that: + - pgbouncer_total_pool_size | int == 30 + fail_msg: "Test failed: pgbouncer_total_pool_size is not equal to 30." + success_msg: "Test passed: pgbouncer_total_pool_size is equal to 30." diff --git a/molecule/tests/roles/pre-checks/variables/timescaledb.yml b/molecule/tests/roles/pre-checks/variables/timescaledb.yml new file mode 100644 index 000000000..6ceb581f3 --- /dev/null +++ b/molecule/tests/roles/pre-checks/variables/timescaledb.yml @@ -0,0 +1,64 @@ +--- +# 📝 These tasks aim to ensure that 'timescaledb' is included in the 'shared_preload_libraries' of PostgreSQL parameters +# 🎯 The objective is to guarantee that TimescaleDB, a time-series database built on PostgreSQL, is properly loaded and available + +# 🔄 Ensuring 'timescaledb' is included in 'shared_preload_libraries' +# If 'timescaledb' is not already in 'shared_preload_libraries', it is added +- name: Molecule.tests.roles.pre-checks.variables.timescaledb | Ensure 'timescaledb' is in 'shared_preload_libraries' + run_once: true + ansible.builtin.set_fact: + postgresql_parameters: >- + {{ postgresql_parameters | rejectattr('option', 'equalto', 'shared_preload_libraries') | list + + [{'option': 'shared_preload_libraries', 'value': new_value}] }} + vars: + shared_preload_libraries_item: >- + {{ + postgresql_parameters + | selectattr('option', 'equalto', 'shared_preload_libraries') + | list | last | default({'value': ''}) + }} + new_value: >- + {{ + (shared_preload_libraries_item.value ~ (',' if shared_preload_libraries_item.value else '') + if 'timescaledb' not in shared_preload_libraries_item.value.split(',') else shared_preload_libraries_item.value) + ~ ('timescaledb' if 'timescaledb' not in shared_preload_libraries_item.value.split(',') else '') + }} + +# 📝 Setting 'shared_preload_libraries_item' as a fact for further use +- name: Molecule.tests.roles.pre-checks.variables.timescaledb | Set 'shared_preload_libraries_item' as a fact + run_once: true + ansible.builtin.set_fact: + shared_preload_libraries_item: >- + {{ + postgresql_parameters + | selectattr('option', 'equalto', 'shared_preload_libraries') + | list | last | default({'value': ''}) + }} + +# ✅ Verifying that 'timescaledb' is included in 'shared_preload_libraries' +# If 'timescaledb' is not included, the test fails and an error message is displayed +- name: Molecule.tests.roles.pre-checks.variables.timescaledb | Assert that 'timescaledb' is in 'shared_preload_libraries' + run_once: true + ansible.builtin.assert: + that: + - "'timescaledb' in shared_preload_libraries_item.value.split(',')" + fail_msg: "'timescaledb' is not in 'shared_preload_libraries'" + success_msg: "'timescaledb' is in 'shared_preload_libraries'" + +# 📝 Setting 'origin_shared_preload_libraries_item' as a fact for further use +- name: Molecule.tests.roles.pre-checks.variables.timescaledb | Set 'origin_shared_preload_libraries_item' as a fact + run_once: true + ansible.builtin.set_fact: # yamllint disable rule:line-length + origin_shared_preload_libraries_item: "{{ postgresql_parameters | selectattr('option', 'equalto', 'shared_preload_libraries') | list | last | default({'value': ''}) }}" + +# ✅ Verifying that the new 'shared_preload_libraries_item' equals 'origin_shared_preload_libraries_item' +# If they are not equal, the test fails and an error message is displayed +- name: Molecule.tests.roles.pre-checks.variables.timescaledb | Assert that new 'shared_preload_libraries_item' equals 'origin_shared_preload_libraries_item' + run_once: true + ansible.builtin.assert: + that: + - shared_preload_libraries_item == origin_shared_preload_libraries_item + fail_msg: > + Assertion failed: shared_preload_libraries_item is "{{ shared_preload_libraries_item }}", + but expected "{{ origin_shared_preload_libraries_item }}" + success_msg: "shared_preload_libraries_item is correct" diff --git a/molecule/tests/roles/swap/conditions/create.yml b/molecule/tests/roles/swap/conditions/create.yml new file mode 100644 index 000000000..0660f7a84 --- /dev/null +++ b/molecule/tests/roles/swap/conditions/create.yml @@ -0,0 +1,32 @@ +--- +# 🚀 These tasks aim to set up the conditions for creating a swap file +# 🎯 The objective is to ensure that a swap file is created only if it does not already exist or if its size is not as expected + +# 📝 Loading system variables that may be needed for the swap file creation process +- name: Molecule.tests.roles.swap.conditions.create | Load System Variables + run_once: true + ansible.builtin.include_vars: + file: ../../../../../vars/system.yml + +# 🔄 Setting up a test condition where no swap file exists +# We initialize swap_exists with an empty stdout and stdout_lines +- name: Molecule.tests.roles.swap.conditions.create | Establish Swap File Non-Existence Test Condition + run_once: true + ansible.builtin.set_fact: + swap_exists: + stdout: "" + stdout_lines: [] + +# ✅ Verifying the condition for creating a swap file +# A swap file should be created if either of the following is true: +# 1. No swap file exists (indicated by an empty stdout in swap_exists) +# 2. The total size of all swap files (calculated from stdout_lines in swap_exists) is not equal to the expected swap file size (swap_file_size_mb) +- name: Molecule.tests.roles.swap.conditions.create | Verify Swap File Creation Condition + run_once: true + ansible.builtin.assert: + that: > + (swap_exists.stdout is defined and swap_exists.stdout | length < 1) or + (swap_exists.stdout_lines is defined and + (swap_exists.stdout_lines | map('trim') | map('int') | sum / 1024 / 1024) | round | int != swap_file_size_mb|int) + fail_msg: "Test failed: Condition for creating swap file is false." + success_msg: "Test passed: Condition for creating swap file is true." diff --git a/molecule/tests/roles/swap/conditions/delete.yml b/molecule/tests/roles/swap/conditions/delete.yml new file mode 100644 index 000000000..c59525055 --- /dev/null +++ b/molecule/tests/roles/swap/conditions/delete.yml @@ -0,0 +1,30 @@ +--- +# 🚀 These tasks aim to test the conditions for deleting a swap file +# 🎯 The objective is to verify that the swap file exists and its size is not equal to the desired size + +# 📝 Loading system variables that might be used in the following tasks +- name: Molecule.tests.roles.swap.conditions.delete | Load System Variables + run_once: true + ansible.builtin.include_vars: + file: ../../../../../vars/system.yml + +# 📝 Setting up test data for swap file existence and size +# Here, we assume that a 1MB swap file exists +- name: Molecule.tests.roles.swap.conditions.delete | Set Swap File Test Data + run_once: true + ansible.builtin.set_fact: + swap_exists: + stdout: "1048576" # 1MB in kilobytes + stdout_lines: + - "1048576" + +# ✅ Verifying the condition for deleting the swap file +# The swap file should be deleted if it exists and its size is not equal to the desired size (swap_file_size_mb) +# If the condition is not met, the test fails and an error message is displayed +- name: Molecule.tests.roles.swap.conditions.delete | Verify Condition for Deleting Swap File + run_once: true + ansible.builtin.assert: + that: (swap_exists.stdout is defined and swap_exists.stdout | length > 1) and + ((swap_exists.stdout_lines|map('trim')|map('int')|sum / 1024 / 1024)|round|int != swap_file_size_mb|int) + fail_msg: "Test failed: Condition for deleting swap file is false." + success_msg: "Test passed: Condition for deleting swap file is true." diff --git a/molecule/tests/roles/swap/main.yml b/molecule/tests/roles/swap/main.yml new file mode 100644 index 000000000..726c5cbd8 --- /dev/null +++ b/molecule/tests/roles/swap/main.yml @@ -0,0 +1,13 @@ +--- +# 🚀 This task aims to include variable tests for the main swap role in Molecule +# 🎯 The objective is to ensure all conditions are tested by looping through each YAML file in the 'conditions' directory + +# 🔄 Including variable tests for the main swap role in Molecule +# For each YAML file in the 'conditions' directory, we include its tasks in the current playbook +# If a YAML file is not found or cannot be read, the playbook execution will fail at this point +- name: Molecule.tests.roles.swap.main | Include Variable Tests from Conditions Directory + run_once: true + ansible.builtin.include_tasks: "{{ molecule_tests_roles_swap_main_file }}" + loop: "{{ lookup('fileglob', 'conditions/*.yml', wantlist=True) }}" + loop_control: + loop_var: molecule_tests_roles_swap_main_file diff --git a/molecule/tests/variables/asserts/apt_repository.yml b/molecule/tests/variables/asserts/apt_repository.yml new file mode 100644 index 000000000..b9cae4b42 --- /dev/null +++ b/molecule/tests/variables/asserts/apt_repository.yml @@ -0,0 +1,48 @@ +--- +# 🚀 These tasks aim to validate the apt_repository variable +# 🎯 The objective is to ensure that the repository URLs are correctly defined + +# ======================================== +# 💻 Start Repository Operations and Tests +# ======================================== + +# 🔄 Setting up a valid repository for testing +# We define a minor version of PostgreSQL Pro and the distribution details of the system +- name: Molecule.tests.variables.asserts.apt_repository | Set Valid Repository Test Data + run_once: true + ansible.builtin.set_fact: + postgrespro_minor_version: 12.4.1 + ansible_distribution: Debian + ansible_distribution_release: "stretch" + +# 📝 Constructing the repository URL for testing +# The URL is constructed based on the minor version of PostgreSQL Pro and the system's distribution details +- name: Molecule.tests.variables.asserts.apt_repository | Construct Repository URL Test Data + run_once: true + ansible.builtin.set_fact: + apt_repository: + repo: >- + {{ 'deb https://repo.postgrespro.ru//pgpro-archive/pgpro-' ~ + postgrespro_minor_version ~ '/' ~ (ansible_distribution | lower) ~ '/' ~ + ' ' ~ ansible_distribution_release ~ ' main' }} + +# 🖨️ Debugging the constructed repository URL +# The constructed URL is printed for debugging purposes +- name: Molecule.tests.variables.asserts.apt_repository | Debug Constructed Repository URL + run_once: true + ansible.builtin.debug: + var: apt_repository['repo'] + +# ✅ Verifying the correctness of the constructed repository URL +# The constructed URL is compared with the expected URL +# If the URLs do not match, the test fails and an error message is displayed +- name: Molecule.tests.variables.asserts.apt_repository | Verify Constructed Repository URL + run_once: true + ansible.builtin.assert: + that: + - "'repo' in apt_repository" + - "apt_repository['repo'] is not none" + - "apt_repository['repo'] != 'N/A'" + - "apt_repository['repo'] == 'deb https://repo.postgrespro.ru//pgpro-archive/pgpro-12.4.1/debian/ stretch main'" + fail_msg: "Test failed: The constructed repository URL does not match the expected URL." + success_msg: "Test passed: The constructed repository URL matches the expected URL." diff --git a/molecule/tests/variables/asserts/baseurl.yml b/molecule/tests/variables/asserts/baseurl.yml new file mode 100644 index 000000000..e03f10bd2 --- /dev/null +++ b/molecule/tests/variables/asserts/baseurl.yml @@ -0,0 +1,43 @@ +--- +# 🚀 These tasks aim to validate the _baseurl variable for OracleLinux +# 🎯 The objective is to ensure that the URLs for OracleLinux are correctly set in _baseurl + +# ========================================================= +# 💻 Start OracleLinux URL Validation Operations and Tests +# ========================================================= + +# 🔄 Setting up a valid OracleLinux version and distribution for the test +# We define a specific minor version of PostgreSQL Pro and a major version of the Linux distribution +- name: Molecule.tests.variables.asserts.baseurl | Set OracleLinux Test Data (valid) + run_once: true + ansible.builtin.set_fact: + postgrespro_minor_version: 12.4.1 + ansible_distribution_major_version: 9 + +# 📝 Constructing the expected _baseurl for OracleLinux +# The URL is built based on the defined PostgreSQL Pro minor version and Linux distribution major version +- name: Molecule.tests.variables.asserts.baseurl | Construct Expected OracleLinux URL + run_once: true + ansible.builtin.set_fact: + _baseurl: # yamllint disable rule:line-length + OracleLinux: "https://repo.postgrespro.ru//pgpro-archive/pgpro-{{ postgrespro_minor_version }}/oraclelinux/{{ ansible_distribution_major_version }}Server/os/x86_64/rpms/" + +# 🖨️ Displaying the constructed OracleLinux URL for debugging purposes +- name: Molecule.tests.variables.asserts.baseurl | Debug Constructed OracleLinux URL + run_once: true + ansible.builtin.debug: + var: _baseurl['OracleLinux'] + +# ✅ Verifying the correctness of the constructed OracleLinux URL +# We check that the URL is correctly set in _baseurl and matches the expected URL +# If any of these conditions is not met, the test fails and an error message is displayed +- name: Molecule.tests.variables.asserts.baseurl | Verify OracleLinux URL in _baseurl + run_once: true + ansible.builtin.assert: + that: + - "'OracleLinux' in _baseurl" + - "_baseurl['OracleLinux'] is not none" + - "_baseurl['OracleLinux'] != 'N/A'" + - "_baseurl['OracleLinux'] == 'https://repo.postgrespro.ru//pgpro-archive/pgpro-12.4.1/oraclelinux/9Server/os/x86_64/rpms/'" + fail_msg: "Test failed: OracleLinux URL is not set correctly in _baseurl." + success_msg: "Test passed: OracleLinux URL is set correctly in _baseurl." diff --git a/molecule/tests/variables/asserts/pg_probackup.yml b/molecule/tests/variables/asserts/pg_probackup.yml new file mode 100644 index 000000000..c0c65544d --- /dev/null +++ b/molecule/tests/variables/asserts/pg_probackup.yml @@ -0,0 +1,51 @@ +--- +# 🚀 These tasks aim to validate the values of pg_probackup related variables +# 🎯 The objective is to ensure that the values of these variables are correctly set + +# 📝 Setting the expected value for pg_probackup[0].value +- name: Molecule.tests.variables.asserts.pg_probackup | Establish Expected Value for pg_probackup[0].value + run_once: true + ansible.builtin.set_fact: # yamllint disable rule:line-length + origin_pg_probackup_command_value: "pg_probackup-{{ pg_probackup_version }} restore -B {{ pg_probackup_dir }} --instance {{ pg_probackup_instance }} -j {{ pg_probackup_threads }} {{ pg_probackup_add_keys }}" + +# 🖨️ Debugging the actual value of pg_probackup[0].value +- name: Molecule.tests.variables.asserts.pg_probackup | Debug Actual Value of pg_probackup[0].value + run_once: true + ansible.builtin.debug: + var: pg_probackup[0].value + +# ✅ Verifying the correctness of pg_probackup[0].value +# If the actual value is not equal to the expected value, the test fails and an error message is displayed +- name: Molecule.tests.variables.asserts.pg_probackup | Verify Value of pg_probackup[0].value + run_once: true + ansible.builtin.assert: + that: + - pg_probackup[0].value == origin_pg_probackup_command_value + fail_msg: > + Assertion failed: pg_probackup[0].value is "{{ pg_probackup[0].value }}", + but expected "{{ origin_pg_probackup_command_value}}" + success_msg: "pg_probackup[0].value is correct" + +# 📝 Setting the expected value for pg_probackup_patroni_cluster_bootstrap_command +- name: Molecule.tests.variables.asserts.pg_probackup | Establish Expected Value for pg_probackup_patroni_cluster_bootstrap_command + run_once: true + ansible.builtin.set_fact: # yamllint disable rule:line-length + origin_pg_probackup_patroni_cluster_bootstrap_command: "pg_probackup-{{ pg_probackup_version }} restore -B {{ pg_probackup_dir }} --instance {{ pg_probackup_instance }} -j {{ pg_probackup_threads }} {{ pg_probackup_add_keys }}" + +# 🖨️ Debugging the actual value of pg_probackup_patroni_cluster_bootstrap_command +- name: Molecule.tests.variables.asserts.pg_probackup | Debug Actual Value of pg_probackup_patroni_cluster_bootstrap_command + run_once: true + ansible.builtin.debug: + var: pg_probackup_patroni_cluster_bootstrap_command + +# ✅ Verifying the correctness of pg_probackup_patroni_cluster_bootstrap_command +# If the actual value is not equal to the expected value, the test fails and an error message is displayed +- name: Molecule.tests.variables.asserts.pg_probackup | Verify Value of pg_probackup_patroni_cluster_bootstrap_command + run_once: true + ansible.builtin.assert: + that: + - pg_probackup_patroni_cluster_bootstrap_command == origin_pg_probackup_patroni_cluster_bootstrap_command + fail_msg: > + Assertion failed: pg_probackup_patroni_cluster_bootstrap_command is "{{ pg_probackup_patroni_cluster_bootstrap_command }}", + but expected "{{ origin_pg_probackup_patroni_cluster_bootstrap_command }}" + success_msg: "pg_probackup_patroni_cluster_bootstrap_command is correct" diff --git a/molecule/tests/variables/asserts/system_info.yml b/molecule/tests/variables/asserts/system_info.yml new file mode 100644 index 000000000..21a7d35d4 --- /dev/null +++ b/molecule/tests/variables/asserts/system_info.yml @@ -0,0 +1,92 @@ +--- +# 🚀 These tasks aim to test the system_info variable +# 🎯 The objective is to guarantee that the system informations are correctly set + +# ======================================== +# 💻 Start CPU model Operations and Tests +# ======================================== + +# 🔄 Define a valid CPU model +- name: Molecule.tests.variables.asserts.system_info | Set CPU Model Test Data (valid) + run_once: true + ansible.builtin.set_fact: + ansible_processor: ["GenuineIntel", "Intel(R) Core(TM) i7-8700 CPU @ 3.20GHz", "Intel(R) Core(TM) i7-8700 CPU @ 3.20GHz"] + ansible_processor_count: 6 + ansible_processor_cores: 12 + +# 📝 Establishing test data for CPU model +- name: Molecule.tests.variables.asserts.system_info | Establish CPU Model Test Data + run_once: true + ansible.builtin.set_fact: + system_info: + CPU model: >- + {{ ansible_processor[2] | default('N/A') }}, + count: {{ ansible_processor_count | default('N/A') }}, + cores: {{ ansible_processor_cores | default('N/A') }} + +# 🖨️ Debugging the established CPU model +- name: Molecule.tests.variables.asserts.system_info | Debug CPU Model + run_once: true + ansible.builtin.debug: + var: system_info['CPU model'] + +# ✅ Verifying the correctness of the established CPU model +# If the CPU model is not set, the test fails and an error message is displayed +- name: Molecule.tests.variables.asserts.system_info | Verify CPU Model + run_once: true + ansible.builtin.assert: + that: + - "'CPU model' in system_info" + - "system_info['CPU model'] is not none" + - "system_info['CPU model'] != 'N/A'" + - "system_info['CPU model'] == 'Intel(R) Core(TM) i7-8700 CPU @ 3.20GHz, count: 6, cores: 12'" + fail_msg: "Test failed: CPU model is not set correctly in system_info." + success_msg: "Test passed: CPU model is set correctly in system_info." + +# ================================================== +# 💾 Start Disk space total Operations and Tests +# ================================================== + +# 🔄 Define a valid Disk space total +- name: Molecule.tests.variables.asserts.system_info | Set Disk space total Test Data (valid) + run_once: true + ansible.builtin.set_fact: + ansible_mounts: + - mount: "/" + size_total: "53687091200" # 50 GB + - mount: "/home" + size_total: "107374182400" # 100 GB + +# 📝 Establishing test data for Disk space total +- name: Molecule.tests.variables.asserts.system_info | Establish Disk space total Test Data + run_once: true + ansible.builtin.set_fact: + system_info: + Disk space total: >- + {{ + (ansible_mounts + | map(attribute='size_total') + | map('int') + | sum / 1024 / 1024 / 1024 + ) + | round(2) if ansible_mounts is defined else 'N/A' + }} GB + +# 🖨️ Debugging the established Disk space total +- name: Molecule.tests.variables.asserts.system_info | Debug Disk space total + run_once: true + ansible.builtin.debug: + var: system_info['Disk space total'] + +# ✅ Verifying the correctness of the established Disk space total +# If the Disk space total is not set, the test fails and an error message is displayed +- name: Molecule.tests.variables.asserts.system_info | Verify Disk space total + run_once: true + ansible.builtin.assert: + that: + - "'Disk space total' in system_info" + - "system_info['Disk space total'] is not none" + - "system_info['Disk space total'] != 'N/A'" + - "system_info['Disk space total'] == '150.0 GB'" + fail_msg: "Test failed: Disk space total is not set correctly in system_info." + success_msg: "Test passed: Disk space total is set correctly in system_info." diff --git a/molecule/tests/variables/asserts/vip_manager_package_repo.yml b/molecule/tests/variables/asserts/vip_manager_package_repo.yml new file mode 100644 index 000000000..749a68779 --- /dev/null +++ b/molecule/tests/variables/asserts/vip_manager_package_repo.yml @@ -0,0 +1,84 @@ +--- +# 🚀 These tasks aim to define and verify 'vip_manager_package_repo' for different OS +# 🎯 The objective is to ensure accurate url + +# 🔄 Define the vip_manager_version +# This is the version of the VIP Manager that we will be using in our tests +- name: Molecule.tests.variables.asserts.vip_manager_package_repo| Common | Setting vip_manager_version + run_once: true + ansible.builtin.set_fact: + vip_manager_version: "1.0.0" + +# =============================================== +# 💾 Start Debian-specific operations and tests +# =============================================== + +# 📁 Incorporate Debian-specific variables +# This task includes variables that are specific to Debian OS +- name: Molecule.tests.variables.asserts.vip_manager_package_repo | Debian | Incorporate RedHat-specific Variables + run_once: true + ansible.builtin.include_vars: + file: ../../../../vars/Debian.yml + +# 🔄 Define the origin_vip_manager_package_repo for Debian +# This task defines the expected URL for the VIP Manager package for Debian OS +- name: Molecule.tests.variables.asserts.vip_manager_package_repo | Debian | Define origin_vip_manager_package_repo + run_once: true + ansible.builtin.set_fact: # yamllint disable rule:line-length + origin_vip_manager_package_repo: "https://github.com/cybertec-postgresql/vip-manager/releases/download/v{{ vip_manager_version }}/vip-manager_{{ vip_manager_version }}_Linux_x86_64.deb" + +# 🖨️ Debug the vip_manager_package_repo for Debian +# This task prints the actual URL for the VIP Manager package for Debian OS +- name: Molecule.tests.variables.asserts.vip_manager_package_repo | Debian | Debug vip_manager_package_repo + run_once: true + ansible.builtin.debug: + var: vip_manager_package_repo + +# ✅ Validate the vip_manager_package_repo for Debian +# This task checks if the actual URL matches the expected URL for the VIP Manager package for Debian OS +- name: Molecule.tests.variables.asserts.vip_manager_package_repo | Debian | Validate vip_manager_package_repo + run_once: true + ansible.builtin.assert: + that: + - vip_manager_package_repo == origin_vip_manager_package_repo + fail_msg: > + Assertion failed: vip_manager_package_repo is "{{ vip_manager_package_repo }}", + but expected "{{ origin_vip_manager_package_repo }}" + success_msg: "vip_manager_package_repo is correct" + +# =============================================== +# 🚀 Start RedHat-specific operations and tests +# =============================================== + +# 📁 Incorporate RedHat-specific variables +# This task includes variables that are specific to RedHat OS +- name: Molecule.tests.variables.asserts.vip_manager_package_repo | RedHat | Incorporate RedHat-specific Variables + run_once: true + ansible.builtin.include_vars: + file: ../../../../vars/RedHat.yml + +# 🔄 Define the origin_vip_manager_package_repo for RedHat +# This task defines the expected URL for the VIP Manager package for RedHat OS +- name: Molecule.tests.variables.asserts.vip_manager_package_repo | RedHat | Define origin_vip_manager_package_repo + run_once: true + ansible.builtin.set_fact: # yamllint disable rule:line-length + origin_vip_manager_package_repo: "https://github.com/cybertec-postgresql/vip-manager/releases/download/v{{ vip_manager_version }}/vip-manager_{{ vip_manager_version }}_Linux_x86_64.rpm" + +# 🖨️ Debug the vip_manager_package_repo for RedHat +# This task prints the actual URL for the VIP Manager package for RedHat OS +- name: Molecule.tests.variables.asserts.vip_manager_package_repo | RedHat | Debug vip_manager_package_repo + run_once: true + ansible.builtin.debug: + var: vip_manager_package_repo + +# ✅ Validate the vip_manager_package_repo for RedHat +# This task checks if the actual URL matches the expected URL for the VIP Manager package for RedHat OS +- name: Molecule.tests.variables.asserts.vip_manager_package_repo | RedHat | Validate vip_manager_package_repo + run_once: true + ansible.builtin.assert: + that: + - vip_manager_package_repo == origin_vip_manager_package_repo + fail_msg: > + Assertion failed: vip_manager_package_repo is "{{ vip_manager_package_repo }}", + but expected "{{ origin_vip_manager_package_repo }}" + success_msg: "vip_manager_package_repo is correct" diff --git a/molecule/tests/variables/asserts/wal_g_cron_jobs.yml b/molecule/tests/variables/asserts/wal_g_cron_jobs.yml new file mode 100644 index 000000000..959ee5fe2 --- /dev/null +++ b/molecule/tests/variables/asserts/wal_g_cron_jobs.yml @@ -0,0 +1,136 @@ +--- +# 🚀 These tasks aim to define and verify 'wal_g_cron_jobs' for different OS +# 🎯 The objective is to ensure accurate setup and validation of wal-g cron jobs + +# 📝 Validate the structure and content of wal_g_cron_jobs. We expect two jobs, each with specific attributes. +- name: Molecule.tests.variables.asserts.wal_g_cron_jobs | Validate Structure and Content of wal_g_cron_jobs + run_once: true + ansible.builtin.assert: + that: + - wal_g_cron_jobs | length == 2 + - wal_g_cron_jobs[0].name == "WAL-G: Create daily backup" + - wal_g_cron_jobs[0].user == "postgres" + - wal_g_cron_jobs[0].file == "/etc/cron.d/walg" + - wal_g_cron_jobs[0].job == wal_g_backup_command | join('') + - wal_g_cron_jobs[1].name == "WAL-G: Delete old backups" + - wal_g_cron_jobs[1].user == "postgres" + - wal_g_cron_jobs[1].file == "/etc/cron.d/walg" + - wal_g_cron_jobs[1].job == wal_g_delete_command | join('') + fail_msg: "Test failed: wal_g_cron_jobs does not have the expected structure or content." + success_msg: "Test passed: wal_g_cron_jobs has the expected structure and content." + +# 📝 Validate the content of wal_g_backup_command and wal_g_delete_command. We expect specific commands for each job. +- name: Molecule.tests.variables.asserts.wal_g_cron_jobs | Validate Content of wal_g_backup_command and wal_g_delete_command + run_once: true + ansible.builtin.assert: + that: + - wal_g_backup_command[0] == "[ $(curl -s -o /dev/null -w '%{http_code}' http://{{ inventory_hostname }}:{{ patroni_restapi_port }}) = '200' ]" + - wal_g_backup_command[1] == " && wal-g backup-push {{ postgresql_data_dir }} > {{ postgresql_log_dir }}/walg_backup.log 2>&1" + - wal_g_delete_command[0] == "[ $(curl -s -o /dev/null -w '%{http_code}' http://{{ inventory_hostname }}:{{ patroni_restapi_port }}) = '200' ]" + - wal_g_delete_command[1] == " && wal-g delete retain FULL 4 --confirm > {{ postgresql_log_dir }}/walg_delete.log 2>&1" + fail_msg: "Test failed: wal_g_backup_command or wal_g_delete_command do not have the expected content." + success_msg: "Test passed: wal_g_backup_command and wal_g_delete_command have the expected content." + +# ================================================== +# 💾 Start Debian-specific operations and tests +# ================================================== + +# 📁 Load Debian-specific variables for the tests +- name: Molecule.tests.variables.asserts.wal_g_cron_jobs | Debian | Load Debian-specific Variables + run_once: true + ansible.builtin.include_vars: + file: ../../../../vars/Debian.yml + +# 🔄 Define the expected first wal_g_cron job for Debian +- name: Molecule.tests.variables.asserts.wal_g_cron_jobs | Debian | Define Expected First wal_g_cron Job + run_once: true + ansible.builtin.set_fact: # yamllint disable rule:line-length + origin_wal_g_cron_jobs_create_job: "[ $(curl -s -o /dev/null -w '%{http_code}' http://{{ inventory_hostname }}:{{ patroni_restapi_port }}) = '200' ] && wal-g backup-push {{ postgresql_data_dir }} > {{ postgresql_log_dir }}/walg_backup.log 2>&1" + +# 🖨️ Display the first wal_g_cron job for Debian for debugging purposes +- name: Molecule.tests.variables.asserts.wal_g_cron_jobs | Debian | Debug First wal_g_cron Job + run_once: true + ansible.builtin.debug: + var: wal_g_cron_jobs[0].job + +# ✅ Verify that the first wal_g_cron job for Debian matches the expected job +- name: Molecule.tests.variables.asserts.wal_g_cron_jobs | Debian | Verify First wal_g_cron Job + run_once: true + ansible.builtin.assert: + that: + - wal_g_cron_jobs[0].job == origin_wal_g_cron_jobs_create_job + fail_msg: "Test failed: wal_g_cron_jobs[0].job is not as expected." + success_msg: "Test passed: wal_g_cron_jobs[0].job is as expected." + +# 🔄 Define the expected second wal_g_cron job for Debian +- name: Molecule.tests.variables.asserts.wal_g_cron_jobs | Debian | Define Expected Second wal_g_cron Job + run_once: true + ansible.builtin.set_fact: # yamllint disable rule:line-length + origin_wal_g_cron_jobs_delete_job: "[ $(curl -s -o /dev/null -w '%{http_code}' http://{{ inventory_hostname }}:{{ patroni_restapi_port }}) = '200' ] && wal-g delete retain FULL 4 --confirm > {{ postgresql_log_dir }}/walg_delete.log 2>&1" + +# 🖨️ Display the second wal_g_cron job for Debian for debugging purposes +- name: Molecule.tests.variables.asserts.wal_g_cron_jobs | Debian | Debug Second wal_g_cron Job + run_once: true + ansible.builtin.debug: + var: wal_g_cron_jobs[1].job + +# ✅ Verify that the second wal_g_cron job for Debian matches the expected job +- name: Molecule.tests.variables.asserts.wal_g_cron_jobs | Debian | Verify Second wal_g_cron Job + run_once: true + ansible.builtin.assert: + that: + - wal_g_cron_jobs[1].job == origin_wal_g_cron_jobs_delete_job + fail_msg: "Test failed: wal_g_cron_jobs[1].job is not as expected." + success_msg: "Test passed: wal_g_cron_jobs[1].job is as expected." + +# ================================================== +# 🚀 Start RedHat-specific operations and tests +# ================================================== + +# 📁 Load RedHat-specific variables for the tests +- name: Molecule.tests.variables.asserts.wal_g_cron_jobs | RedHat | Load RedHat-specific Variables + run_once: true + ansible.builtin.include_vars: + file: ../../../../vars/RedHat.yml + +# 🔄 Define the expected first wal_g_cron job for RedHat +- name: Molecule.tests.variables.asserts.wal_g_cron_jobs | RedHat | Define Expected First wal_g_cron Job + run_once: true + ansible.builtin.set_fact: # yamllint disable rule:line-length + origin_wal_g_cron_jobs_create_job: "[ $(curl -s -o /dev/null -w '%{http_code}' http://{{ inventory_hostname }}:{{ patroni_restapi_port }}) = '200' ] && wal-g backup-push {{ postgresql_data_dir }} > {{ postgresql_log_dir }}/walg_backup.log 2>&1" + +# 🖨️ Display the first wal_g_cron job for RedHat for debugging purposes +- name: Molecule.tests.variables.asserts.wal_g_cron_jobs | RedHat | Debug First wal_g_cron Job + run_once: true + ansible.builtin.debug: + var: wal_g_cron_jobs[0].job + +# ✅ Verify that the first wal_g_cron job for RedHat matches the expected job +- name: Molecule.tests.variables.asserts.wal_g_cron_jobs | RedHat | Verify First wal_g_cron Job + run_once: true + ansible.builtin.assert: + that: + - wal_g_cron_jobs[0].job == origin_wal_g_cron_jobs_create_job + fail_msg: "Test failed: wal_g_cron_jobs[0].job is not as expected." + success_msg: "Test passed: wal_g_cron_jobs[0].job is as expected." + +# 🔄 Define the expected second wal_g_cron job for RedHat +- name: Molecule.tests.variables.asserts.wal_g_cron_jobs | RedHat | Define Expected Second wal_g_cron Job + run_once: true + ansible.builtin.set_fact: # yamllint disable rule:line-length + origin_wal_g_cron_jobs_delete_job: "[ $(curl -s -o /dev/null -w '%{http_code}' http://{{ inventory_hostname }}:{{ patroni_restapi_port }}) = '200' ] && wal-g delete retain FULL 4 --confirm > {{ postgresql_log_dir }}/walg_delete.log 2>&1" + +# 🖨️ Display the second wal_g_cron job for RedHat for debugging purposes +- name: Molecule.tests.variables.asserts.wal_g_cron_jobs | RedHat | Debug Second wal_g_cron Job + run_once: true + ansible.builtin.debug: + var: wal_g_cron_jobs[1].job + +# ✅ Verify that the second wal_g_cron job for RedHat matches the expected job +- name: Molecule.tests.variables.asserts.wal_g_cron_jobs | RedHat | Verify Second wal_g_cron Job + run_once: true + ansible.builtin.assert: + that: + - wal_g_cron_jobs[1].job == origin_wal_g_cron_jobs_delete_job + fail_msg: "Test failed: wal_g_cron_jobs[1].job is not as expected." + success_msg: "Test passed: wal_g_cron_jobs[1].job is as expected." diff --git a/molecule/tests/variables/main.yml b/molecule/tests/variables/main.yml new file mode 100644 index 000000000..03982bb26 --- /dev/null +++ b/molecule/tests/variables/main.yml @@ -0,0 +1,13 @@ +--- +# 🚀 This task aims to include all assert tasks for the main variables in Molecule tests +# 🎯 The objective is to ensure that all assert tasks are executed for comprehensive testing + +# 🔄 Including all assert tasks found in the 'asserts' directory +# For each .yml file in the 'asserts' directory, we include the tasks defined in the file +# This allows us to modularize our tests and keep our codebase organized +- name: Molecule.tests.variables.main | Include All Assert Tasks for Comprehensive Testing + run_once: true + ansible.builtin.include_tasks: "{{ molecule_tests_variables_main_file }}" + loop: "{{ lookup('fileglob', 'asserts/*.yml', wantlist=True) }}" + loop_control: + loop_var: molecule_tests_variables_main_file diff --git a/roles/confd/tasks/main.yml b/roles/confd/tasks/main.yml index d9d10b25e..bff37a697 100644 --- a/roles/confd/tasks/main.yml +++ b/roles/confd/tasks/main.yml @@ -97,34 +97,46 @@ - name: Prepare haproxy.tmpl template file (replace "bind" for stats) ansible.builtin.lineinfile: path: /etc/confd/templates/haproxy.tmpl - regexp: "{{ item.regexp }}" - line: "{{ item.line }}" + regexp: "{{ line_item.regexp }}" + line: "{{ line_item.line }}" backrefs: true loop: - - { regexp: '^.*bind.*:{{ haproxy_listen_port.stats }}$', line: ' bind {{ hostvars[inventory_hostname].inventory_hostname }}:{{ haproxy_listen_port.stats }}' } - - { regexp: '^.*bind.*:{{ haproxy_listen_port.master }}$', line: ' bind {{ cluster_vip }}:{{ haproxy_listen_port.master }}' } - - { regexp: '^.*bind.*:{{ haproxy_listen_port.replicas }}$', line: ' bind {{ cluster_vip }}:{{ haproxy_listen_port.replicas }}' } - - { regexp: '^.*bind.*:{{ haproxy_listen_port.replicas_sync }}$', line: ' bind {{ cluster_vip }}:{{ haproxy_listen_port.replicas_sync }}' } - - { regexp: '^.*bind.*:{{ haproxy_listen_port.replicas_async }}$', line: ' bind {{ cluster_vip }}:{{ haproxy_listen_port.replicas_async }}' } + - regexp: '^.*bind.*:{{ haproxy_listen_port.stats }}$' + line: ' bind {{ hostvars[inventory_hostname].inventory_hostname }}:{{ haproxy_listen_port.stats }}' + - regexp: '^.*bind.*:{{ haproxy_listen_port.master }}$' + line: ' bind {{ cluster_vip }}:{{ haproxy_listen_port.master }}' + - regexp: '^.*bind.*:{{ haproxy_listen_port.replicas }}$' + line: ' bind {{ cluster_vip }}:{{ haproxy_listen_port.replicas }}' + - regexp: '^.*bind.*:{{ haproxy_listen_port.replicas_sync }}$' + line: ' bind {{ cluster_vip }}:{{ haproxy_listen_port.replicas_sync }}' + - regexp: '^.*bind.*:{{ haproxy_listen_port.replicas_async }}$' + line: ' bind {{ cluster_vip }}:{{ haproxy_listen_port.replicas_async }}' loop_control: - label: "{{ item.line }}" + loop_var: line_item + label: "{{ line_item.line }}" notify: "restart confd" when: cluster_vip is defined and cluster_vip | length > 0 - name: Prepare haproxy.tmpl template file (replace "bind" for stats) ansible.builtin.lineinfile: path: /etc/confd/templates/haproxy.tmpl - regexp: "{{ item.regexp }}" - line: "{{ item.line }}" + regexp: "{{ line_item_2.regexp }}" + line: "{{ line_item_2.line }}" backrefs: true loop: - - { regexp: '^.*bind.*:{{ haproxy_listen_port.stats }}$', line: ' bind {{ hostvars[inventory_hostname].inventory_hostname }}:{{ haproxy_listen_port.stats }}' } - - { regexp: '^.*bind.*:{{ haproxy_listen_port.master }}$', line: ' bind {{ hostvars[inventory_hostname].inventory_hostname }}:{{ haproxy_listen_port.master }}' } - - { regexp: '^.*bind.*:{{ haproxy_listen_port.replicas }}$', line: ' bind {{ hostvars[inventory_hostname].inventory_hostname }}:{{ haproxy_listen_port.replicas }}' } - - { regexp: '^.*bind.*:{{ haproxy_listen_port.replicas_sync }}$', line: ' bind {{ hostvars[inventory_hostname].inventory_hostname }}:{{ haproxy_listen_port.replicas_sync }}' } - - { regexp: '^.*bind.*:{{ haproxy_listen_port.replicas_async }}$', line: ' bind {{ hostvars[inventory_hostname].inventory_hostname }}:{{ haproxy_listen_port.replicas_async }}' } + - regexp: '^.*bind.*:{{ haproxy_listen_port.stats }}$' + line: ' bind {{ hostvars[inventory_hostname].inventory_hostname }}:{{ haproxy_listen_port.stats }}' + - regexp: '^.*bind.*:{{ haproxy_listen_port.master }}$' + line: ' bind {{ hostvars[inventory_hostname].inventory_hostname }}:{{ haproxy_listen_port.master }}' + - regexp: '^.*bind.*:{{ haproxy_listen_port.replicas }}$' + line: ' bind {{ hostvars[inventory_hostname].inventory_hostname }}:{{ haproxy_listen_port.replicas }}' + - regexp: '^.*bind.*:{{ haproxy_listen_port.replicas_sync }}$' + line: ' bind {{ hostvars[inventory_hostname].inventory_hostname }}:{{ haproxy_listen_port.replicas_sync }}' + - regexp: '^.*bind.*:{{ haproxy_listen_port.replicas_async }}$' + line: ' bind {{ hostvars[inventory_hostname].inventory_hostname }}:{{ haproxy_listen_port.replicas_async }}' loop_control: - label: "{{ item.line }}" + loop_var: line_item_2 + label: "{{ line_item_2.line }}" notify: "restart confd" when: cluster_vip is not defined or cluster_vip | length < 1 when: add_balancer is defined and add_balancer|bool diff --git a/roles/deploy-finish/tasks/main.yml b/roles/deploy-finish/tasks/main.yml index 7d5e52695..230e0d6b2 100644 --- a/roles/deploy-finish/tasks/main.yml +++ b/roles/deploy-finish/tasks/main.yml @@ -128,8 +128,20 @@ - name: Create list of nodes run_once: true ansible.builtin.set_fact: - haproxy_nodes: "{% for host in groups['balancers'] | default([]) %}{{ hostvars[host]['inventory_hostname'] }}{% if not loop.last %},{% endif %}{% endfor %}" - postgres_cluster_nodes: "{% for host in groups['postgres_cluster'] %}{{ hostvars[host]['inventory_hostname'] }}{% if not loop.last %},{% endif %}{% endfor %}" + haproxy_nodes: >- + {{ + groups['balancers'] + | default([]) + | map('extract', hostvars, 'inventory_hostname') + | join(',') + }} + postgres_cluster_nodes: >- + {{ + groups['postgres_cluster'] + | default([]) + | map('extract', hostvars, 'inventory_hostname') + | join(',') + }} - name: PostgreSQL Cluster connection info run_once: true diff --git a/roles/haproxy/tasks/main.yml b/roles/haproxy/tasks/main.yml index 13f02d564..45b1cfd2d 100644 --- a/roles/haproxy/tasks/main.yml +++ b/roles/haproxy/tasks/main.yml @@ -429,34 +429,46 @@ - name: Prepare haproxy.cfg conf file (replace "bind") ansible.builtin.lineinfile: path: /etc/haproxy/haproxy.cfg - regexp: "{{ item.regexp }}" - line: "{{ item.line }}" + regexp: "{{ bind_config_without_vip_item.regexp }}" + line: "{{ bind_config_without_vip_item.line }}" backrefs: true loop: - - { regexp: '^.*bind.*:{{ haproxy_listen_port.stats }}$', line: ' bind {{ hostvars[inventory_hostname].inventory_hostname }}:{{ haproxy_listen_port.stats }}' } - - { regexp: '^.*bind.*:{{ haproxy_listen_port.master }}$', line: ' bind {{ hostvars[inventory_hostname].inventory_hostname }}:{{ haproxy_listen_port.master }}' } - - { regexp: '^.*bind.*:{{ haproxy_listen_port.replicas }}$', line: ' bind {{ hostvars[inventory_hostname].inventory_hostname }}:{{ haproxy_listen_port.replicas }}' } - - { regexp: '^.*bind.*:{{ haproxy_listen_port.replicas_sync }}$', line: ' bind {{ hostvars[inventory_hostname].inventory_hostname }}:{{ haproxy_listen_port.replicas_sync }}' } - - { regexp: '^.*bind.*:{{ haproxy_listen_port.replicas_async }}$', line: ' bind {{ hostvars[inventory_hostname].inventory_hostname }}:{{ haproxy_listen_port.replicas_async }}' } + - regexp: '^.*bind.*:{{ haproxy_listen_port.stats }}$' + line: ' bind {{ hostvars[inventory_hostname].inventory_hostname }}:{{ haproxy_listen_port.stats }}' + - regexp: '^.*bind.*:{{ haproxy_listen_port.master }}$' + line: ' bind {{ hostvars[inventory_hostname].inventory_hostname }}:{{ haproxy_listen_port.master }}' + - regexp: '^.*bind.*:{{ haproxy_listen_port.replicas }}$' + line: ' bind {{ hostvars[inventory_hostname].inventory_hostname }}:{{ haproxy_listen_port.replicas }}' + - regexp: '^.*bind.*:{{ haproxy_listen_port.replicas_sync }}$' + line: ' bind {{ hostvars[inventory_hostname].inventory_hostname }}:{{ haproxy_listen_port.replicas_sync }}' + - regexp: '^.*bind.*:{{ haproxy_listen_port.replicas_async }}$' + line: ' bind {{ hostvars[inventory_hostname].inventory_hostname }}:{{ haproxy_listen_port.replicas_async }}' loop_control: - label: "{{ item.line }}" + loop_var: bind_config_without_vip_item + label: "{{ bind_config_without_vip_item.line }}" notify: "restart haproxy" when: cluster_vip is not defined or cluster_vip | length < 1 - name: Prepare haproxy.cfg conf file (replace "bind" for stats) ansible.builtin.lineinfile: path: /etc/haproxy/haproxy.cfg - regexp: "{{ item.regexp }}" - line: "{{ item.line }}" + regexp: "{{ bind_config_with_vip_item.regexp }}" + line: "{{ bind_config_with_vip_item.line }}" backrefs: true loop: - - { regexp: '^.*bind.*:{{ haproxy_listen_port.stats }}$', line: ' bind {{ hostvars[inventory_hostname].inventory_hostname }}:{{ haproxy_listen_port.stats }}' } - - { regexp: '^.*bind.*:{{ haproxy_listen_port.master }}$', line: ' bind {{ cluster_vip }}:{{ haproxy_listen_port.master }}' } - - { regexp: '^.*bind.*:{{ haproxy_listen_port.replicas }}$', line: ' bind {{ cluster_vip }}:{{ haproxy_listen_port.replicas }}' } - - { regexp: '^.*bind.*:{{ haproxy_listen_port.replicas_sync }}$', line: ' bind {{ cluster_vip }}:{{ haproxy_listen_port.replicas_sync }}' } - - { regexp: '^.*bind.*:{{ haproxy_listen_port.replicas_async }}$', line: ' bind {{ cluster_vip }}:{{ haproxy_listen_port.replicas_async }}' } + - regexp: '^.*bind.*:{{ haproxy_listen_port.stats }}$' + line: ' bind {{ hostvars[inventory_hostname].inventory_hostname }}:{{ haproxy_listen_port.stats }}' + - regexp: '^.*bind.*:{{ haproxy_listen_port.master }}$' + line: ' bind {{ cluster_vip }}:{{ haproxy_listen_port.master }}' + - regexp: '^.*bind.*:{{ haproxy_listen_port.replicas }}$' + line: ' bind {{ cluster_vip }}:{{ haproxy_listen_port.replicas }}' + - regexp: '^.*bind.*:{{ haproxy_listen_port.replicas_sync }}$' + line: ' bind {{ cluster_vip }}:{{ haproxy_listen_port.replicas_sync }}' + - regexp: '^.*bind.*:{{ haproxy_listen_port.replicas_async }}$' + line: ' bind {{ cluster_vip }}:{{ haproxy_listen_port.replicas_async }}' loop_control: - label: "{{ item.line }}" + loop_var: bind_config_with_vip_item + label: "{{ bind_config_with_vip_item.line }}" notify: "restart haproxy" when: cluster_vip is defined and cluster_vip | length > 0 when: add_balancer is defined and add_balancer|bool diff --git a/roles/patroni/tasks/custom_wal_dir.yml b/roles/patroni/tasks/custom_wal_dir.yml index b20c99372..2c06d272c 100644 --- a/roles/patroni/tasks/custom_wal_dir.yml +++ b/roles/patroni/tasks/custom_wal_dir.yml @@ -43,8 +43,13 @@ dest: "{{ postgresql_wal_dir }}/" delegate_to: "{{ inventory_hostname }}" - - name: "Rename {{ 'pg_wal' if postgresql_version is version('10', '>=') else 'pg_xlog' }} to {{ 'pg_wal' if postgresql_version is version('10', '>=') else 'pg_xlog' }}_old" - ansible.builtin.command: mv {{ postgresql_data_dir }}/{{ 'pg_wal' if postgresql_version is version('10', '>=') else 'pg_xlog' }} {{ postgresql_data_dir }}/{{ 'pg_wal' if postgresql_version is version('10', '>=') else 'pg_xlog' }}_old + # 🔄 Determine base pg_wal_dir name + - name: Roles.patroni.custom_wal_dir | Set pg_wal_dir based on postgresql_version + ansible.builtin.set_fact: + pg_wal_dir: "{{ 'pg_wal' if postgresql_version is version('10', '>=') else 'pg_xlog' }}" + + - name: "Rename {{ pg_wal_dir }} to {{ pg_wal_dir }}_old" + ansible.builtin.command: mv {{ postgresql_data_dir }}/{{ pg_wal_dir }} {{ postgresql_data_dir }}/{{ pg_wal_dir }}_old register: mv_result when: sym.stat.exists and not sym.stat.islnk|bool diff --git a/roles/patroni/tasks/main.yml b/roles/patroni/tasks/main.yml index a83c9877d..1e81670bb 100644 --- a/roles/patroni/tasks/main.yml +++ b/roles/patroni/tasks/main.yml @@ -319,33 +319,45 @@ - name: Prepare patroni.yml conf file (replace "name","listen","connect_address") ansible.builtin.lineinfile: path: /etc/patroni/patroni.yml - regexp: "{{ item.regexp }}" - line: "{{ item.line }}" + regexp: "{{ patroni_config_without_cluster_vip_item.regexp }}" + line: "{{ patroni_config_without_cluster_vip_item.line }}" backrefs: true loop: - - { regexp: '^name:', line: 'name: {{ ansible_hostname }}' } - - { regexp: '^ listen: .*:{{ patroni_restapi_port }}$', line: ' listen: {{ hostvars[inventory_hostname].inventory_hostname }}:{{ patroni_restapi_port }}' } - - { regexp: '^ connect_address: .*:{{ patroni_restapi_port }}$', line: ' connect_address: {{ hostvars[inventory_hostname].inventory_hostname }}:{{ patroni_restapi_port }}' } - - { regexp: '^ listen: ((?!{{ patroni_restapi_port }}).)*$', line: ' listen: {{ hostvars[inventory_hostname].inventory_hostname }},127.0.0.1:{{ postgresql_port }}' } - - { regexp: '^ connect_address: ((?!{{ patroni_restapi_port }}).)*$', line: ' connect_address: {{ hostvars[inventory_hostname].inventory_hostname }}:{{ postgresql_port }}' } + - regexp: '^name:' + line: 'name: {{ ansible_hostname }}' + - regexp: '^ listen: .*:{{ patroni_restapi_port }}$' + line: ' listen: {{ hostvars[inventory_hostname].inventory_hostname }}:{{ patroni_restapi_port }}' + - regexp: '^ connect_address: .*:{{ patroni_restapi_port }}$' + line: ' connect_address: {{ hostvars[inventory_hostname].inventory_hostname }}:{{ patroni_restapi_port }}' + - regexp: '^ listen: ((?!{{ patroni_restapi_port }}).)*$' + line: ' listen: {{ hostvars[inventory_hostname].inventory_hostname }},127.0.0.1:{{ postgresql_port }}' + - regexp: '^ connect_address: ((?!{{ patroni_restapi_port }}).)*$' + line: ' connect_address: {{ hostvars[inventory_hostname].inventory_hostname }}:{{ postgresql_port }}' loop_control: - label: "{{ item.line }}" + loop_var: patroni_config_without_cluster_vip_item + label: "{{ patroni_config_without_cluster_vip_item.line }}" when: with_haproxy_load_balancing|bool or pgbouncer_install|bool or (cluster_vip is not defined or cluster_vip | length < 1) - name: Prepare patroni.yml conf file (replace "name","listen","connect_address") ansible.builtin.lineinfile: path: /etc/patroni/patroni.yml - regexp: "{{ item.regexp }}" - line: "{{ item.line }}" + regexp: "{{ patroni_config_with_cluster_vip_item.regexp }}" + line: "{{ patroni_config_with_cluster_vip_item.line }}" backrefs: true loop: - - { regexp: '^name:', line: 'name: {{ ansible_hostname }}' } - - { regexp: '^ listen: .*:{{ patroni_restapi_port }}$', line: ' listen: {{ hostvars[inventory_hostname].inventory_hostname }}:{{ patroni_restapi_port }}' } - - { regexp: '^ connect_address: .*:{{ patroni_restapi_port }}$', line: ' connect_address: {{ hostvars[inventory_hostname].inventory_hostname }}:{{ patroni_restapi_port }}' } - - { regexp: '^ listen: ((?!{{ patroni_restapi_port }}).)*$', line: ' listen: {{ hostvars[inventory_hostname].inventory_hostname }},{{ cluster_vip }},127.0.0.1:{{ postgresql_port }}' } - - { regexp: '^ connect_address: ((?!{{ patroni_restapi_port }}).)*$', line: ' connect_address: {{ hostvars[inventory_hostname].inventory_hostname }}:{{ postgresql_port }}' } + - regexp: '^name:' + line: 'name: {{ ansible_hostname }}' + - regexp: '^ listen: .*:{{ patroni_restapi_port }}$' + line: ' listen: {{ hostvars[inventory_hostname].inventory_hostname }}:{{ patroni_restapi_port }}' + - regexp: '^ connect_address: .*:{{ patroni_restapi_port }}$' + line: ' connect_address: {{ hostvars[inventory_hostname].inventory_hostname }}:{{ patroni_restapi_port }}' + - regexp: '^ listen: ((?!{{ patroni_restapi_port }}).)*$' + line: ' listen: {{ hostvars[inventory_hostname].inventory_hostname }},{{ cluster_vip }},127.0.0.1:{{ postgresql_port }}' + - regexp: '^ connect_address: ((?!{{ patroni_restapi_port }}).)*$' + line: ' connect_address: {{ hostvars[inventory_hostname].inventory_hostname }}:{{ postgresql_port }}' loop_control: - label: "{{ item.line }}" + loop_var: patroni_config_with_cluster_vip_item + label: "{{ patroni_config_with_cluster_vip_item.line }}" when: not with_haproxy_load_balancing|bool and not pgbouncer_install|bool and (cluster_vip is defined and cluster_vip | length > 0) when: existing_pgcluster is defined and existing_pgcluster|bool tags: patroni, patroni_conf diff --git a/roles/pre-checks/tasks/pgbouncer.yml b/roles/pre-checks/tasks/pgbouncer.yml index c2e8f518c..4b31c321d 100644 --- a/roles/pre-checks/tasks/pgbouncer.yml +++ b/roles/pre-checks/tasks/pgbouncer.yml @@ -17,24 +17,36 @@ - name: PgBouncer | Calculate pool_size run_once: true ansible.builtin.set_fact: - pgbouncer_pool_size: "{{ pgbouncer_pool_size | default(0) | int + (item.pool_parameters | regex_search('pool_size=(\\d+)', multiline=False) | regex_replace('[^0-9]', '') | default(pgbouncer_default_pool_size | default(0), true) | int) }}" + pgbouncer_pool_size: "{{ + (pgbouncer_pool_size | default(0) | int) + + + (pool_item.pool_parameters + | regex_search('pool_size=(\\d+)', multiline=False) + | regex_replace('[^0-9]', '') + | default(pgbouncer_default_pool_size | default(0), true) + | int) + }}" loop: "{{ pgbouncer_pools | default([]) }}" + loop_control: + loop_var: pool_item -# This task calculates the total pool size across all databases. -# If 'postgresql_databases' is not defined or is an empty list, the total pool size will be equal to the 'pgbouncer_pool_size'. -# Otherwise, it adds the 'pgbouncer_pool_size' to the product of the default pool size (or 0 if not defined) and the number of databases not already defined in 'pgbouncer_pools'. -# This means it checks the 'postgresql_databases' against 'pgbouncer_pools' and for each database that does not have a corresponding pool, it adds the 'pgbouncer_default_pool_size' (or 0 if not defined) to the 'pgbouncer_pool_size'. +# This task computes the total pool size across all databases. +# If 'postgresql_databases' isn't defined or is empty, 'pgbouncer_pool_size' is the total pool size. +# If 'postgresql_databases' is defined, the task does the following: +# 1. It checks each database against 'pgbouncer_pools'. +# 2. For databases without a corresponding pool, it adds 'pgbouncer_default_pool_size' (or 0 if not defined) to 'pgbouncer_pool_size'. - name: PgBouncer | Calculate total pool_size run_once: true ansible.builtin.set_fact: pgbouncer_total_pool_size: >- {{ - (pgbouncer_pool_size|int) - if (postgresql_databases is not defined or postgresql_databases|default([]) == []) - else - ((pgbouncer_pool_size|int) + (pgbouncer_pool_size | int) + - (postgresql_databases|default([]) | rejectattr('db', 'in', pgbouncer_pools|map(attribute='dbname')|list)|length) * (pgbouncer_default_pool_size|default(0)|int)) + (postgresql_databases + | default([]) + | rejectattr('db', 'in', pgbouncer_pools | map(attribute='dbname') | list) + | length + ) * (pgbouncer_default_pool_size | default(0) | int) }} when: pgbouncer_pool_size is defined diff --git a/roles/pre-checks/tasks/timescaledb.yml b/roles/pre-checks/tasks/timescaledb.yml index 85cc4f08e..39a344398 100644 --- a/roles/pre-checks/tasks/timescaledb.yml +++ b/roles/pre-checks/tasks/timescaledb.yml @@ -23,7 +23,12 @@ + [{'option': 'shared_preload_libraries', 'value': new_value}] }} vars: # Find the last item in postgresql_parameters where the option is 'shared_preload_libraries' - shared_preload_libraries_item: "{{ postgresql_parameters | selectattr('option', 'equalto', 'shared_preload_libraries') | list | last | default({'value': ''}) }}" + shared_preload_libraries_item: >- + {{ + postgresql_parameters + | selectattr('option', 'equalto', 'shared_preload_libraries') + | list | last | default({'value': ''}) + }} # Determine the new value based on whether 'timescaledb' is already present new_value: >- {{ diff --git a/roles/swap/tasks/main.yml b/roles/swap/tasks/main.yml index 8e548a506..ef5a387ee 100644 --- a/roles/swap/tasks/main.yml +++ b/roles/swap/tasks/main.yml @@ -62,8 +62,10 @@ fstype: swap opts: sw state: present - when: (swap_exists.stdout is defined and swap_exists.stdout | length < 1) or - (swap_exists.stdout_lines is defined and (swap_exists.stdout_lines | map('trim') | map('int') | sum / 1024 / 1024) | round | int != swap_file_size_mb|int) + when: > + (swap_exists.stdout is defined and swap_exists.stdout | length < 1) or + (swap_exists.stdout_lines is defined and + (swap_exists.stdout_lines | map('trim') | map('int') | sum / 1024 / 1024) | round | int != swap_file_size_mb|int) tags: swap, swap_create ... diff --git a/vars/Debian.yml b/vars/Debian.yml index 2c939e98e..ec5b9d3e8 100644 --- a/vars/Debian.yml +++ b/vars/Debian.yml @@ -60,7 +60,9 @@ postgresql_packages: # Extra packages etcd_package_repo: "https://github.com/etcd-io/etcd/releases/download/v{{ etcd_version }}/etcd-v{{ etcd_version }}-linux-amd64.tar.gz" -vip_manager_package_repo: "https://github.com/cybertec-postgresql/vip-manager/releases/download/v{{ vip_manager_version }}/vip-manager_{{ vip_manager_version }}_Linux_x86_64.deb" +vip_manager_package_repo: + "https://github.com/cybertec-postgresql/vip-manager/releases/download/v{{ vip_manager_version }}/vip-manager_{{ vip_manager_version }}_Linux_x86_64.deb" + # (if with_haproxy_load_balancing: true) haproxy_installation_method: "deb" # (default)"deb" or "src" haproxy_install_repo: true # or 'false' diff --git a/vars/RedHat.yml b/vars/RedHat.yml index 96f523993..e1e414c69 100644 --- a/vars/RedHat.yml +++ b/vars/RedHat.yml @@ -80,12 +80,15 @@ postgresql_packages: # Extra packages etcd_package_repo: "https://github.com/etcd-io/etcd/releases/download/v{{ etcd_version }}/etcd-v{{ etcd_version }}-linux-amd64.tar.gz" -vip_manager_package_repo: "https://github.com/cybertec-postgresql/vip-manager/releases/download/v{{ vip_manager_version }}/vip-manager_{{ vip_manager_version }}_Linux_x86_64.rpm" +vip_manager_package_repo: + "https://github.com/cybertec-postgresql/vip-manager/releases/download/v{{ vip_manager_version }}/vip-manager_{{ vip_manager_version }}_Linux_x86_64.rpm" + # (if with_haproxy_load_balancing: true) haproxy_installation_method: "rpm" # (default)"rpm" or "src" haproxy_install_repo: true # or 'false' # only for RedHat/CentOS version 7. -# for RedHat/CentOS/OracleLinux 7 the haproxy version 1.8 (LTS) will be installed from the "rh-haproxy18" package from the Software Collections (SCL) repository. -# you can preload the haproxy rpm packages to your YUM repository (in this case specify "haproxy_install_repo: false"). +# For RedHat/CentOS/OracleLinux 7 the haproxy version 1.8 (LTS) +# will be installed from the "rh-haproxy18" package from the Software Collections (SCL) repository. +# You can preload the haproxy rpm packages to your YUM repository (in this case specify "haproxy_install_repo: false"). confd_package_repo: "https://github.com/kelseyhightower/confd/releases/download/v0.16.0/confd-0.16.0-linux-amd64" # (optional) if haproxy_installation_method: 'src' @@ -182,8 +185,9 @@ patroni_pip_requirements_file: patroni_pip_package_file: - "patroni-1.6.0.tar.gz" # https://pypi.org/project/patroni/#files -# ( if patroni_installation_type: "rpm" and installation_method: "file" ) -patroni_rpm_package_file: "patroni-2.1.4-1.rhel8.x86_64.rpm" # (package for RHEL/CentOS 8) https://download.postgresql.org/pub/repos/yum/common/redhat/rhel-8-x86_64/ +# ( if patroni_installation_type: "rpm" and installation_method: "file" ) +# (package for RHEL/CentOS 8) https://download.postgresql.org/pub/repos/yum/common/redhat/rhel-8-x86_64/ +patroni_rpm_package_file: "patroni-2.1.4-1.rhel8.x86_64.rpm" # additional packages etcd_package_file: "etcd-v3.3.27-linux-amd64.tar.gz" # https://github.com/etcd-io/etcd/releases diff --git a/vars/main.yml b/vars/main.yml index 389d8aba0..925426768 100644 --- a/vars/main.yml +++ b/vars/main.yml @@ -385,14 +385,17 @@ basebackup: - { option: "max-rate", value: "100M" } - { option: "checkpoint", value: "fast" } pg_probackup: - - { option: "command", value: "pg_probackup-{{ pg_probackup_version }} restore -B {{ pg_probackup_dir }} --instance {{ pg_probackup_instance }} -j {{ pg_probackup_threads }} {{ pg_probackup_add_keys }}" } + - { option: "command", value: "{{ pg_probackup_restore_command }}" } - { option: "no_params", value: "true" } # "restore_command" written to recovery.conf when configuring follower (create replica) postgresql_restore_command: "" # postgresql_restore_command: "wal-g wal-fetch %f %p" # restore WAL-s using WAL-G # postgresql_restore_command: "pgbackrest --stanza={{ pgbackrest_stanza }} archive-get %f %p" # restore WAL-s using pgbackrest -# postgresql_restore_command: "pg_probackup-{{ pg_probackup_version }} archive-get -B {{ pg_probackup_dir }} --instance {{ pg_probackup_instance }} --wal-file-path=%p --wal-file-name=%f" # restore WAL-s using pg_probackup + +# postgresql_restore_command: "pg_probackup-{{ pg_probackup_version }} archive-get -B +# {{ pg_probackup_dir }} --instance {{ pg_probackup_instance }} --wal-file-path=%p +# --wal-file-name=%f" # restore WAL-s using pg_probackup # pg_probackup pg_probackup_install: false # or 'true' @@ -402,7 +405,15 @@ pg_probackup_instance: "pg_probackup_instance_name" pg_probackup_dir: "/mnt/backup_dir" pg_probackup_threads: "4" pg_probackup_add_keys: "--recovery-target=latest --skip-external-dirs --no-validate" -pg_probackup_patroni_cluster_bootstrap_command: "pg_probackup-{{ pg_probackup_version }} restore -B {{ pg_probackup_dir }} --instance {{ pg_probackup_instance }} -j {{ pg_probackup_threads }} {{ pg_probackup_add_keys }}" +# ⚠️ Ensure there is a space at the beginning of each part to prevent commands from concatenating. +pg_probackup_command_parts: + - "pg_probackup-{{ pg_probackup_version }}" + - " restore -B {{ pg_probackup_dir }}" + - " --instance {{ pg_probackup_instance }}" + - " -j {{ pg_probackup_threads }}" + - " {{ pg_probackup_add_keys }}" +pg_probackup_restore_command: "{{ pg_probackup_command_parts | join('') }}" +pg_probackup_patroni_cluster_bootstrap_command: "{{ pg_probackup_command_parts | join('') }}" # WAL-G wal_g_install: false # or 'true' @@ -423,6 +434,15 @@ wal_g_json: # config https://github.com/wal-g/wal-g#configuration wal_g_archive_command: "wal-g wal-push %p" wal_g_patroni_cluster_bootstrap_command: "wal-g backup-fetch {{ postgresql_data_dir }} LATEST" +# Define job_parts outside of wal_g_cron_jobs +# ⚠️ Ensure there is a space at the beginning of each part to prevent commands from concatenating. +wal_g_backup_command: + - "[ $(curl -s -o /dev/null -w '%{http_code}' http://{{ inventory_hostname }}:{{ patroni_restapi_port }}) = '200' ]" + - " && wal-g backup-push {{ postgresql_data_dir }} > {{ postgresql_log_dir }}/walg_backup.log 2>&1" +wal_g_delete_command: + - "[ $(curl -s -o /dev/null -w '%{http_code}' http://{{ inventory_hostname }}:{{ patroni_restapi_port }}) = '200' ]" + - " && wal-g delete retain FULL 4 --confirm > {{ postgresql_log_dir }}/walg_delete.log 2>&1" + wal_g_cron_jobs: - name: "WAL-G: Create daily backup" user: "postgres" @@ -432,7 +452,7 @@ wal_g_cron_jobs: day: "*" month: "*" weekday: "*" - job: "[ $(curl -s -o /dev/null -w '%{http_code}' http://{{ inventory_hostname }}:{{ patroni_restapi_port }}) = '200' ] && wal-g backup-push {{ postgresql_data_dir }} > {{ postgresql_log_dir }}/walg_backup.log 2>&1" + job: "{{ wal_g_backup_command | join('') }}" - name: "WAL-G: Delete old backups" # retain 4 full backups (adjust according to your company's backup retention policy) user: "postgres" file: /etc/cron.d/walg @@ -441,7 +461,7 @@ wal_g_cron_jobs: day: "*" month: "*" weekday: "*" - job: "[ $(curl -s -o /dev/null -w '%{http_code}' http://{{ inventory_hostname }}:{{ patroni_restapi_port }}) = '200' ] && wal-g delete retain FULL 4 --confirm > {{ postgresql_log_dir }}/walg_delete.log 2>&1" + job: "{{ wal_g_delete_command | join('') }}" # pgBackRest pgbackrest_install: false # or 'true'