diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3b0a927fb..7738c2871 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,6 +8,7 @@ name: CI push: branches: - development + - decrease-ansible-deprecation-5-0-0 schedule: - cron: "0 4 * * 4" @@ -48,7 +49,6 @@ jobs: # - distro: rockylinux9 - distro: debian11 - distro: debian12 - - distro: ubuntu2004 - distro: ubuntu2204 - distro: ubuntu2404 @@ -61,10 +61,21 @@ jobs: - name: Set up Python. uses: actions/setup-python@v5 with: - python-version: '3.x' + python-version: '3.12' - - name: Install test dependencies. - run: pip3 install "ansible>10,<12" molecule molecule-plugins[docker] docker + - name: Upgrade pip & install deps + run: | + python -m pip install --upgrade pip wheel setuptools + # Meta-package Ansible 10/11 (include ansible-core ≥2.20) + pip install "ansible>10,<12" + # Testing tools + pip install "molecule" "molecule-plugins[docker]" docker ansible-compat + + - name: Show versions + run: | + python -V + ansible --version + molecule --version - name: Run Molecule tests. run: molecule -v test diff --git a/.gitignore b/.gitignore index 3a6bd3409..4c2909ca9 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ Icon test *.retry ANXS-postgresql.code-workspace +tests/roles diff --git a/README.md b/README.md index a9e0bee0d..fe3bba188 100644 --- a/README.md +++ b/README.md @@ -54,15 +54,14 @@ An example how to include this role as a task: #### Compatibility matrix -| Distribution / PostgreSQL | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -| ------------------------- | :--------: | :----------------: | :----------------: | :----------------: | :----------------: | :----------------: | :----------------: | -| Debian 11.x | :no_entry: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | -| Debian 12.x | :no_entry: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | -| Rockylinux 8.x | :no_entry: | :warning: | :warning: | :warning: | :warning: | :warning: | :warning: | -| Rockylinux 9.x | :no_entry: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :warning: | -| Ubuntu 20.04.x | :no_entry: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | -| Ubuntu 22.04.x | :no_entry: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | -| Ubuntu 24.04.x | :no_entry: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | +| Distribution / PostgreSQL | 11 | 12 | 13 | 14 | 15 | 16 | 17 | +| ------------------------- | :--------: | :--------: | :----------------: | :----------------: | :----------------: | :----------------: | :----------------: | +| Debian 11.x | :no_entry: | :no_entry: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | +| Debian 12.x | :no_entry: | :no_entry: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | +| Rockylinux 8.x | :no_entry: | :no_entry: | :warning: | :warning: | :warning: | :warning: | :warning: | +| Rockylinux 9.x | :no_entry: | :no_entry: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :warning: | +| Ubuntu 22.04.x | :no_entry: | :no_entry: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | +| Ubuntu 24.04.x | :no_entry: | :no_entry: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | - :white_check_mark: - works fine diff --git a/defaults/main.yml b/defaults/main.yml index 2776106c7..689f285a6 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -838,8 +838,22 @@ postgresql_install_repository: true # APT settings postgresql_apt_key_id: "ACCC4CF8" postgresql_apt_key_url: "https://www.postgresql.org/media/keys/ACCC4CF8.asc" -postgresql_apt_repository: "deb http://apt.postgresql.org/pub/repos/apt/ {{ ansible_distribution_release }}-pgdg main {{ postgresql_version }}" +# postgresql_apt_keyring: "/usr/share/postgresql-common/pgdg/apt.postgresql.org.gpg" + +# repsoitory base +postgresql_apt_repo_base: "https://apt.postgresql.org/pub/repos/apt" +postgresql_apt_repo_components: "main" + +# (codename + -pgdg) +postgresql_apt_suite: "{{ ansible_facts['distribution_release'] | default('') }}-pgdg" + +# Ligne de dépôt finale (utilise les variables ci-dessus) +postgresql_apt_repository: >- + deb [signed-by={{ postgresql_apt_keyring }}] + {{ postgresql_apt_repo_base }} {{ postgresql_apt_suite }} {{ postgresql_apt_repo_components }} + # Pin-Priority of PGDG repository +postgresql_apt_pin_enabled: true postgresql_apt_pin_priority: 500 # YUM repository locations diff --git a/handlers/main.yml b/handlers/main.yml index 6e1db981f..bcfab34fc 100644 --- a/handlers/main.yml +++ b/handlers/main.yml @@ -5,3 +5,7 @@ name: "{{ postgresql_service_name }}" state: restarted enabled: yes + + - name: Reload systemd daemon + ansible.builtin.command: systemctl daemon-reload + become: true diff --git a/molecule/README.md b/molecule/README.md index bcfb20564..27becf062 100644 --- a/molecule/README.md +++ b/molecule/README.md @@ -14,7 +14,6 @@ The default distribution is ubuntu2204. You can override th with setting the env * fedora40 * debian11 * debian12 -* ubuntu2004 * ubuntu2204 * ubuntu2404 @@ -47,7 +46,6 @@ $ ls -1 tests/ | grep vars vars.Debian.11.yml vars.Debian.12.yml vars.Fedora.40.yml -vars.Ubuntu.20.yml vars.Ubuntu.22.yml vars.Ubuntu.24.yml vars.yml diff --git a/molecule/default/collections.yml b/molecule/default/collections.yml new file mode 100644 index 000000000..85528abbb --- /dev/null +++ b/molecule/default/collections.yml @@ -0,0 +1,6 @@ +--- +collections: + - name: community.postgresql + # pin if you want: + # version: ">=3.8.0,<5.0.0" + - name: community.general diff --git a/molecule/default/molecule.yml b/molecule/default/molecule.yml index ca0c12a6c..528e45a6c 100644 --- a/molecule/default/molecule.yml +++ b/molecule/default/molecule.yml @@ -8,14 +8,6 @@ lint: | # yamllint . # ansible-lint platforms: - - name: postgresql-12 - image: "geerlingguy/docker-${MOLECULE_DISTRO:-debian11}-ansible:latest" - volumes: - - /sys/fs/cgroup:/sys/fs/cgroup:rw - privileged: true - pre_build_image: true - cgroupns_mode: host - command: ${MOLECULE_DOCKER_COMMAND:-""} - name: postgresql-13 image: "geerlingguy/docker-${MOLECULE_DISTRO:-debian11}-ansible:latest" volumes: @@ -66,8 +58,6 @@ provisioner: converge: ${MOLECULE_PLAYBOOK:-../../tests/playbook.yml} inventory: host_vars: - postgresql-12: - postgresql_version: 12 postgresql-13: postgresql_version: 13 postgresql-14: diff --git a/tasks/configure.yml b/tasks/configure.yml index c6e9be679..c37ddfb63 100644 --- a/tasks/configure.yml +++ b/tasks/configure.yml @@ -1,11 +1,13 @@ # file: postgresql/tasks/configure.yml - name: PostgreSQL | Drop the data directory | RedHat - file: + ansible.builtin.file: path: "{{ postgresql_data_directory }}" state: absent register: pgdata_dir_remove - when: ansible_os_family == "RedHat" and postgresql_cluster_reset + when: + - ansible_facts['os_family'] == 'RedHat' + - (postgresql_cluster_reset | default(false)) | bool - name: PostgreSQL | Make sure the postgres data directory exists file: @@ -45,84 +47,140 @@ register: pglog_dir_exist when: postgresql_log_directory != "pg_log" or postgresql_log_directory != "log" -- name: PostgreSQL | Ensure the locale for lc_collate and lc_ctype is generated | Debian - become: yes - locale_gen: name="{{ item }}" state=present - with_items: +- name: PostgreSQL | Ensure locales for lc_collate and lc_ctype are generated | Debian + become: true + ansible.builtin.locale_gen: + name: "{{ item }}" + state: present + loop: - "{{ postgresql_locale }}" - "{{ postgresql_ctype }}" - when: ansible_os_family == "Debian" and item != "C.UTF-8" + when: + - ansible_facts['os_family'] == 'Debian' + - item != 'C.UTF-8' - name: PostgreSQL | Ensure the locale is generated | RedHat - become: yes - command: localedef -c -i {{ item.parts[0] }} -f {{ item.parts[1] }} {{ item.locale_name }} + become: true + ansible.builtin.command: + cmd: >- + localedef -c -i {{ item.parts[0] }} -f {{ item.parts[1] }} {{ item.locale_name }} changed_when: false - with_items: + loop: - { parts: "{{ postgresql_locale_parts }}", locale_name: "{{ postgresql_locale }}" } - { parts: "{{ postgresql_ctype_parts }}", locale_name: "{{ postgresql_ctype }}" } - when: ansible_os_family == "RedHat" - ignore_errors: yes + when: + - ansible_facts['os_family'] == 'RedHat' + ignore_errors: true - name: PostgreSQL | Stop PostgreSQL | Debian - service: + become: true + ansible.builtin.service: name: "{{ postgresql_service_name }}" state: stopped - become: yes - when: ansible_os_family == "Debian" and postgresql_cluster_reset and pgdata_dir_exist.changed + when: + - ansible_facts['os_family'] == 'Debian' + - (postgresql_cluster_reset | default(false)) | bool + - (pgdata_dir_exist.changed | default(false)) | bool - name: PostgreSQL | Reset the cluster - drop the existing one | Debian - shell: pg_dropcluster {{ postgresql_version }} {{ postgresql_cluster_name }} - become: yes + become: true become_user: "{{ postgresql_service_user }}" - when: ansible_os_family == "Debian" and postgresql_cluster_reset and pgdata_dir_exist.changed + ansible.builtin.shell: >- + pg_dropcluster {{ postgresql_version }} {{ postgresql_cluster_name }} + args: + warn: false + when: + - ansible_facts['os_family'] == 'Debian' + - (postgresql_cluster_reset | default(false)) | bool + - (pgdata_dir_exist.changed | default(false)) | bool - name: PostgreSQL | Reset the cluster - create a new one (with specified encoding and locale) | Debian - shell: > - pg_createcluster --locale {{ postgresql_locale }} - -e {{ postgresql_encoding }} -d {{ postgresql_data_directory }} - {{ postgresql_version }} {{ postgresql_cluster_name }} - -- - {% if postgresql_data_checksums and postgresql_version is version_compare('9.3', '>=') %}--data-checksums{% endif %} - {% if postgresql_pwfile != "" %}--pwfile={{ postgresql_pwfile }} {% endif %} - {% if postgresql_wal_directory != "" and postgresql_version is version_compare('10', '<') %}--xlogdir={{ postgresql_wal_directory }} {% endif %} - {% if postgresql_wal_directory != "" and postgresql_version is version_compare('10', '>=') %}--waldir={{ postgresql_wal_directory }} {% endif %} - become: yes + become: true become_user: "{{ postgresql_service_user }}" - when: ansible_os_family == "Debian" and postgresql_cluster_reset and pgdata_dir_exist.changed + ansible.builtin.shell: >- + pg_createcluster + --locale {{ postgresql_locale | quote }} + -e {{ postgresql_encoding | quote }} + -d {{ postgresql_data_directory | quote }} + {{ postgresql_version | quote }} {{ postgresql_cluster_name | quote }} + -- + {% if (postgresql_data_checksums | default(false)) | bool and postgresql_version is version('9.3', '>=') -%} + --data-checksums + {%- endif -%} + {% if (postgresql_pwfile | default('')) | length > 0 -%} + --pwfile={{ postgresql_pwfile | quote }} + {%- endif -%} + {% if (postgresql_wal_directory | default('')) | length > 0 and postgresql_version is version('10', '<') -%} + --xlogdir={{ postgresql_wal_directory | quote }} + {%- endif -%} + {% if (postgresql_wal_directory | default('')) | length > 0 and postgresql_version is version('10', '>=') -%} + --waldir={{ postgresql_wal_directory | quote }} + {%- endif -%} + args: + warn: false + when: + - ansible_facts['os_family'] == 'Debian' + - (postgresql_cluster_reset | default(false)) | bool + - (pgdata_dir_exist.changed | default(false)) | bool - name: PostgreSQL | Update systemd | Debian - command: systemctl daemon-reload - become: yes - when: ansible_os_family == "Debian" and postgresql_cluster_reset and pgdata_dir_exist.changed + become: true + ansible.builtin.command: systemctl daemon-reload + args: + warn: false + when: + - ansible_facts['os_family'] == 'Debian' + - (postgresql_cluster_reset | default(false)) | bool + - (pgdata_dir_exist.changed | default(false)) | bool - name: PostgreSQL | Start PostgreSQL | Debian - service: + become: true + ansible.builtin.service: name: "{{ postgresql_service_name }}" state: started - become: yes - when: ansible_os_family == "Debian" and postgresql_cluster_reset and pgdata_dir_exist.changed + when: + - ansible_facts['os_family'] == 'Debian' + - (postgresql_cluster_reset | default(false)) | bool + - (pgdata_dir_exist.changed | default(false)) | bool - name: PostgreSQL | Check whether the postgres data directory is initialized | RedHat - stat: + ansible.builtin.stat: path: "{{ postgresql_data_directory }}/PG_VERSION" - when: ansible_os_family == "RedHat" and not postgresql_cluster_reset register: pgdata_dir_initialized + when: + - ansible_facts['os_family'] == 'RedHat' + - not ((postgresql_cluster_reset | default(false)) | bool) - name: PostgreSQL | Initialize the database | RedHat - command: > - {{ postgresql_bin_directory }}/initdb -D {{ postgresql_data_directory }} - --locale={{ postgresql_locale }} --encoding={{ postgresql_encoding }} - {% if postgresql_data_checksums and postgresql_version is version_compare('9.3', '>=') %}--data-checksums{% endif %} - {% if postgresql_pwfile != "" %}--pwfile={{ postgresql_pwfile }} {% endif %} - {% if postgresql_wal_directory != "" and postgresql_version is version_compare('10', '<') %}--xlogdir={{ postgresql_wal_directory }} {% endif %} - {% if postgresql_wal_directory != "" and postgresql_version is version_compare('10', '>=') %}--waldir={{ postgresql_wal_directory }} {% endif %} - {% if postgresql_wal_segsize != "" and postgresql_version is version_compare('11', '>=') %}--wal-segsize={{ postgresql_wal_segsize }} {% endif %} - become: yes + become: true become_user: "{{ postgresql_service_user }}" - when: ansible_os_family == "RedHat" and - (postgresql_cluster_reset or - pgdata_dir_exist.changed or - not pgdata_dir_initialized.stat.exists) + ansible.builtin.command: >- + {{ postgresql_bin_directory | quote }}/initdb + -D {{ postgresql_data_directory | quote }} + --locale={{ postgresql_locale | quote }} + --encoding={{ postgresql_encoding | quote }} + {% if (postgresql_data_checksums | default(false)) | bool and postgresql_version is version('9.3', '>=') -%} + --data-checksums + {%- endif -%} + {% if (postgresql_pwfile | default('')) | length > 0 -%} + --pwfile={{ postgresql_pwfile | quote }} + {%- endif -%} + {% if (postgresql_wal_directory | default('')) | length > 0 and postgresql_version is version('10', '<') -%} + --xlogdir={{ postgresql_wal_directory | quote }} + {%- endif -%} + {% if (postgresql_wal_directory | default('')) | length > 0 and postgresql_version is version('10', '>=') -%} + --waldir={{ postgresql_wal_directory | quote }} + {%- endif -%} + {% if (postgresql_wal_segsize | default('')) | length > 0 and postgresql_version is version('11', '>=') -%} + --wal-segsize={{ postgresql_wal_segsize | quote }} + {%- endif -%} + args: + warn: false + when: + - ansible_facts['os_family'] == 'RedHat' + - (postgresql_cluster_reset | default(false)) | bool + or (pgdata_dir_exist.changed | default(false)) | bool + or (not (pgdata_dir_initialized.stat.exists | default(false))) - name: PostgreSQL | Verify postgresql cluster version command: grep ^{{ postgresql_version }}$ {{ postgresql_data_directory }}/PG_VERSION @@ -147,14 +205,17 @@ register: postgresql_configuration_pt1 - name: PostgreSQL | Update configuration - pt. 2 (postgresql.conf) - template: + ansible.builtin.template: src: "postgresql.conf-{{ postgresql_version }}.j2" - # if using pgtune, save the template to ".untuned" - dest: "{{postgresql_conf_directory}}/postgresql.conf{% if postgresql_pgtune %}.untuned{% endif %}" + # If pgtune is enabled, write the base template to ".untuned" + dest: "{{ postgresql_conf_directory }}/postgresql.conf{% if (postgresql_pgtune | default(false)) | bool %}.untuned{% endif %}" owner: "{{ postgresql_service_user }}" group: "{{ postgresql_service_group }}" - mode: 0640 + mode: "0640" # quotes prevent YAML from misreading octal notation + backup: true # automatically saves a timestamped copy before overwriting register: postgresql_configuration_pt2 + notify: + - restart postgresql # triggers the handler only if this file changed - name: PostgreSQL | Update configuration - pt. 3 (pgtune) become: true @@ -205,19 +266,25 @@ mode: 0755 - name: PostgreSQL | Ensure the systemd directory for PostgreSQL exists | RedHat - file: - name: "/etc/systemd/system/postgresql-{{ postgresql_version }}.service.d" + ansible.builtin.file: + path: "/etc/systemd/system/postgresql-{{ postgresql_version }}.service.d" state: directory - mode: 0755 - when: ansible_os_family == "RedHat" + mode: "0755" + when: + - ansible_facts['os_family'] == 'RedHat' notify: restart postgresql - name: PostgreSQL | Use the conf directory when starting the Postgres service | RedHat - template: + ansible.builtin.template: src: etc_systemd_system_postgresql.service.d_custom.conf.j2 dest: "/etc/systemd/system/postgresql-{{ postgresql_version }}.service.d/custom.conf" - when: ansible_os_family == "RedHat" + owner: root + group: root + mode: "0644" + when: + - ansible_facts['os_family'] == 'RedHat' register: postgresql_systemd_custom_conf + notify: restart postgresql - name: PostgreSQL | Ensure the pid directory for PostgreSQL exists file: diff --git a/tasks/databases.yml b/tasks/databases.yml index 97cfb3adb..0da393ec4 100644 --- a/tasks/databases.yml +++ b/tasks/databases.yml @@ -6,28 +6,28 @@ state: started - name: PostgreSQL | Make sure the PostgreSQL databases are present - postgresql_db: - name: "{{item.name}}" + community.postgresql.postgresql_db: + name: "{{ item.name }}" owner: "{{ item.owner | default(postgresql_database_owner) }}" encoding: "{{ item.encoding | default(postgresql_encoding) }}" lc_collate: "{{ item.lc_collate | default(postgresql_locale) }}" lc_ctype: "{{ item.lc_ctype | default(postgresql_ctype) }}" - port: "{{postgresql_port}}" + login_port: "{{ postgresql_port }}" template: "template0" state: "{{ item.state | default('present') }}" - login_user: "{{postgresql_admin_user}}" + login_user: "{{ postgresql_admin_user }}" become: yes - become_user: "{{postgresql_admin_user}}" - with_items: "{{postgresql_databases}}" - when: postgresql_databases|length > 0 + become_user: "{{ postgresql_admin_user }}" + loop: "{{ postgresql_databases }}" + when: postgresql_databases | length > 0 - name: PostgreSQL | Add extensions to the databases become: yes become_user: "{{ postgresql_admin_user }}" - postgresql_ext: + community.postgresql.postgresql_ext: db: "{{ item.0.db }}" login_user: "{{ postgresql_service_user }}" - port: "{{ postgresql_port }}" + login_port: "{{ postgresql_port }}" name: "{{ item.1 }}" with_subelements: - "{{ postgresql_database_extensions }}" diff --git a/tasks/extensions/postgis.yml b/tasks/extensions/postgis.yml index 7eec8efc7..0d277f7ca 100644 --- a/tasks/extensions/postgis.yml +++ b/tasks/extensions/postgis.yml @@ -1,33 +1,50 @@ # file: postgresql/tasks/extensions/postgis.yml +# Purpose: Install PostGIS dependencies per OS family, future-proof (no deprecated patterns) -- include_vars: "{{ item }}" - with_first_found: - - "../vars/{{ ansible_distribution_release }}.yml" - - "../vars/empty.yml" +# Load distro-specific variables (e.g., vars/noble.yml, vars/jammy.yml, vars/bullseye.yml) +- name: Load distro-specific variables for PostGIS + ansible.builtin.include_vars: + file: "{{ item }}" + loop: "{{ query('first_found', params, errors='ignore', wantlist=True) }}" + vars: + params: + files: + - "{{ ansible_facts['distribution_release'] }}.yml" + - "empty.yml" + paths: + - "{{ role_path }}/vars" -- name: PostgreSQL | Extensions | Make sure the postgis extensions are installed | Debian - apt: +# Debian/Ubuntu: install PostGIS deps via apt +- name: PostgreSQL | Extensions | Ensure PostGIS deps are installed | Debian/Ubuntu + ansible.builtin.apt: name: "{{ postgresql_ext_postgis_deps }}" state: present - update_cache: yes - cache_valid_time: "{{apt_cache_valid_time | default (3600)}}" - when: ansible_os_family == "Debian" + update_cache: true + cache_valid_time: "{{ apt_cache_valid_time | default(3600) }}" + when: ansible_facts['os_family'] == 'Debian' notify: - restart postgresql -- name: PostgreSQL | Extensions | Make sure the postgis extensions are installed | RedHat - yum: +# RHEL-like (non-Fedora) using yum +- name: PostgreSQL | Extensions | Ensure PostGIS deps are installed | RHEL (yum) + ansible.builtin.yum: name: "{{ postgresql_ext_postgis_deps }}" state: present - update_cache: yes - when: ansible_pkg_mgr == "yum" and ansible_os_family == "RedHat" + update_cache: true + when: + - ansible_facts['os_family'] == 'RedHat' + - ansible_facts['pkg_mgr'] == 'yum' + - ansible_facts['distribution'] != 'Fedora' notify: - restart postgresql -- name: PostgreSQL | Extensions | Make sure the postgis extensions are installed | Fedora - dnf: +# Fedora using dnf +- name: PostgreSQL | Extensions | Ensure PostGIS deps are installed | Fedora (dnf) + ansible.builtin.dnf: name: "{{ postgresql_ext_postgis_deps }}" state: present - when: ansible_pkg_mgr == "dnf" and ansible_distribution == "Fedora" + when: + - ansible_facts['pkg_mgr'] == 'dnf' + - ansible_facts['distribution'] == 'Fedora' notify: - restart postgresql diff --git a/tasks/fdw.yml b/tasks/fdw.yml index e0ac6dbae..4879e5736 100644 --- a/tasks/fdw.yml +++ b/tasks/fdw.yml @@ -1,25 +1,50 @@ --- # file: postgresql/tasks/fdw.yml -# tasks for PostgreSQL Foreign Data Wrappers +# Purpose: Install packages required by PostgreSQL Foreign Data Wrappers (FDW) -- name: PostgreSQL | FDW | Load OS specific variables - include_vars: "{{ lookup('first_found', params) }}" +# Compute the best-matching vars file once (distribution, then family, then fallback) +- name: FDW | Compute OS-specific vars candidate list + ansible.builtin.set_fact: + fdw_candidates: "{{ query('first_found', fdw_params, errors='ignore', wantlist=True) }}" vars: - params: + fdw_params: files: - - "{{ ansible_distribution }}.yml" - - "{{ ansible_os_family }}.yml" + # Most specific: distribution (e.g., Ubuntu.yml, Debian.yml, Rocky.yml) + - "{{ ansible_facts['distribution'] }}.yml" + # Fallback: OS family (e.g., Debian.yml, RedHat.yml) + - "{{ ansible_facts['os_family'] }}.yml" + # Optional final fallback if you keep one + - "empty.yml" paths: - - ../vars + - "{{ role_path }}/vars" -- name: PostgreSQL | FDW | MySQL - package: +# Assert that at least one vars file exists before including +- name: FDW | Assert variables loaded + ansible.builtin.assert: + that: + - (fdw_candidates | length) > 0 + fail_msg: "No OS-specific vars found for FDW." + success_msg: "FDW vars file found: {{ fdw_candidates[0] | default('unknown') }}" + +# Include the first matching vars file +- name: FDW | Load OS-specific variables + ansible.builtin.include_vars: + file: "{{ fdw_candidates[0] }}" + +# Install MySQL FDW dependencies (e.g., mysql/postgresql-mysql-fdw packages) +- name: FDW | Install MySQL FDW packages + ansible.builtin.package: name: "{{ postgresql_fdw_mysql_packages }}" state: present - when: postgresql_fdw_mysql + when: + - (postgresql_fdw_mysql | default(false)) | bool + - (postgresql_fdw_mysql_packages | default([])) | length > 0 -- name: PostgreSQL | FDW | OGR - package: +# Install OGR FDW dependencies (e.g., gdal/ogr-related packages) +- name: FDW | Install OGR FDW packages + ansible.builtin.package: name: "{{ postgresql_fdw_ogr_packages }}" state: present - when: postgresql_fdw_ogr + when: + - (postgresql_fdw_ogr | default(false)) | bool + - (postgresql_fdw_ogr_packages | default([])) | length > 0 diff --git a/tasks/install_apt.yml b/tasks/install_apt.yml index 7ca888954..0c6e52b97 100644 --- a/tasks/install_apt.yml +++ b/tasks/install_apt.yml @@ -1,56 +1,104 @@ # file: postgresql/tasks/install_apt.yml +# Purpose: Install PostgreSQL from PGDG on Debian/Ubuntu in a future-proof way +# Notes: +# - No use of deprecated top-level facts (uses ansible_facts[...] instead) +# - No use of deprecated apt_key (uses a keyring + signed-by) +# - Uses HTTPS and proper keyring location to satisfy apt-secure +# - Adds default_release only when PGDG suite is available -# The standard ca-certs are needed because without them apt_key will fail to -# validate www.postgresql.org (or probably any other source). -- name: PostgreSQL | Make sure the CA certificates are available | apt - apt: - pkg: ca-certificates +# --- Pre-reqs for HTTPS and key handling --- +# We need CA certs, curl and gnupg to fetch and dearmor the repo key. +- name: Ensure APT HTTPS/key prerequisites are present + ansible.builtin.apt: + name: + - ca-certificates + - curl + - gnupg + - lsb-release state: present + update_cache: true + when: ansible_facts['pkg_mgr'] == 'apt' -- name: PostgreSQL | Add PostgreSQL repository apt-key | apt - apt_key: - id: "{{ postgresql_apt_key_id }}" - url: "{{ postgresql_apt_key_url }}" - state: present - keyring: /etc/apt/trusted.gpg.d/postgresql.gpg - when: postgresql_apt_key_url and postgresql_apt_key_id and postgresql_install_repository +# --- Derive the PGDG suite from the host codename --- +# Examples: bookworm -> bookworm-pgdg, bullseye -> bullseye-pgdg, jammy -> jammy-pgdg, noble -> noble-pgdg +- name: Compute PGDG suite from distribution codename + ansible.builtin.set_fact: + postgresql_apt_suite: "{{ ansible_facts['distribution_release'] }}-pgdg" + when: ansible_facts['pkg_mgr'] == 'apt' + +# --- Install the PGDG key into a keyring file --- +# This avoids deprecated apt_key usage and works with newer apt-secure expectations. +- name: Install PGDG keyring (dearmor GPG key into a file) + ansible.builtin.shell: | + install -d -m 0755 /usr/share/postgresql-common/pgdg + curl -fsSL {{ postgresql_apt_key_url }} \ + | gpg --dearmor -o /usr/share/postgresql-common/pgdg/apt.postgresql.org.gpg + args: + creates: /usr/share/postgresql-common/pgdg/apt.postgresql.org.gpg + when: ansible_facts['pkg_mgr'] == 'apt' -- name: PostgreSQL | Add PostgreSQL repository | apt - apt_repository: - repo: "{{ postgresql_apt_repository }}" +# --- Add the PGDG APT repository (HTTPS + signed-by) --- +# We explicitly build the repo line to ensure the correct suite and signed-by usage. +- name: Add PGDG APT repository + ansible.builtin.apt_repository: + repo: >- + deb [signed-by=/usr/share/postgresql-common/pgdg/apt.postgresql.org.gpg] + {{ postgresql_apt_repo_base }} + {{ postgresql_apt_suite }} main + filename: pgdg state: present - when: postgresql_apt_repository | default('') != '' and postgresql_install_repository + when: + - ansible_facts['pkg_mgr'] == 'apt' + - (postgresql_install_repository | default(true)) | bool -- name: PostgreSQL | Add PostgreSQL repository preferences | apt - template: +# --- Optional pinning (preferences) --- +- name: Add APT preferences for PGDG (pinning) + ansible.builtin.template: src: etc_apt_preferences.d_apt_postgresql_org_pub_repos_apt.pref.j2 dest: /etc/apt/preferences.d/apt_postgresql_org_pub_repos_apt.pref - when: postgresql_apt_pin_priority and postgresql_install_repository + when: + # cast to int, compare => boolean + - (postgresql_apt_pin_priority | default(0) | int) > 0 + - (postgresql_install_repository | default(true)) | bool -- name: PostgreSQL | Make sure the dependencies are installed | apt - apt: - pkg: "{{ postgresql_apt_dependencies }}" +# --- Ensure role-level APT dependencies are present --- +# This is where you can ensure build or runtime deps required before installing PostgreSQL. +- name: Ensure role APT dependencies are installed + ansible.builtin.apt: + name: "{{ postgresql_apt_dependencies | default([]) }}" state: present - update_cache: yes - cache_valid_time: "{{apt_cache_valid_time | default (3600)}}" + update_cache: true + cache_valid_time: "{{ apt_cache_valid_time | default(3600) }}" + when: + - ansible_facts['pkg_mgr'] == 'apt' + - (postgresql_apt_dependencies | default([])) | length > 0 -- name: PostgreSQL | Install PostgreSQL | apt - apt: +# --- Install PostgreSQL packages from PGDG --- +# Use default_release only if we know the suite; this avoids "invalid APT::Default-Release" errors. +- name: Install PostgreSQL server/client/contrib + ansible.builtin.apt: name: - - "postgresql-{{postgresql_version}}" - - "postgresql-client-{{postgresql_version}}" - - "postgresql-contrib-{{postgresql_version}}" + - "postgresql-{{ postgresql_version }}" + - "postgresql-client-{{ postgresql_version }}" + - "postgresql-contrib-{{ postgresql_version }}" state: present - update_cache: yes - default_release: "{{postgresql_default_release | default(ansible_distribution_release + '-pgdg')}}" - cache_valid_time: "{{apt_cache_valid_time | default (3600)}}" - environment: "{{postgresql_env}}" + update_cache: true + cache_valid_time: "{{ apt_cache_valid_time | default(3600) }}" + default_release: >- + {{ postgresql_default_release + | default(postgresql_apt_suite | default(omit)) }} + environment: "{{ postgresql_env | default({}) }}" + when: ansible_facts['pkg_mgr'] == 'apt' -- name: PostgreSQL | PGTune | apt - apt: +# --- Optional: install pgtune from PGDG --- +# Only if your role enables pgtune. +- name: Install pgtune + ansible.builtin.apt: name: pgtune state: present - update_cache: yes - cache_valid_time: "{{apt_cache_valid_time | default (3600)}}" - environment: "{{postgresql_env}}" - when: postgresql_pgtune + update_cache: true + cache_valid_time: "{{ apt_cache_valid_time | default(3600) }}" + environment: "{{ postgresql_env | default({}) }}" + when: + - ansible_facts['pkg_mgr'] == 'apt' + - (postgresql_pgtune | default(false)) | bool diff --git a/tasks/main.yml b/tasks/main.yml index d932f3acb..a9ce1524a 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -1,57 +1,90 @@ # file: postgresql/tasks/main.yml -- include_vars: "{{ item }}" - with_first_found: - - "../vars/{{ ansible_os_family }}_{{ ansible_distribution_major_version }}.yml" - - "../vars/{{ ansible_os_family }}.yml" - - "../vars/empty.yml" +# --- Load OS-specific variables --- +- name: Load OS-specific variables + ansible.builtin.include_vars: + file: "{{ item }}" + loop: "{{ query('first_found', params, errors='ignore', wantlist=True) }}" + vars: + os_family: "{{ ansible_facts.get('os_family', '') }}" + os_major: "{{ ansible_facts.get('distribution_major_version', '') }}" + params: + files: + - "{{ os_family }}_{{ os_major }}.yml" + - "{{ os_family }}.yml" + - "empty.yml" + paths: + - "{{ role_path }}/vars" tags: [always] -# -# Override defaults/main.yml with PostgreSQL version specific values -# -- include_vars: "{{ item }}" - with_first_found: - - "../vars/postgresql_{{ postgresql_version }}.yml" - - "../vars/empty.yml" +# --- Vars dépendantes de la version de PostgreSQL --- +- name: Load PostgreSQL version variables + ansible.builtin.include_vars: "{{ item }}" + loop: "{{ query('first_found', first_found_pg, wantlist=True) }}" + vars: + first_found_pg: + files: + - "postgresql_{{ postgresql_version }}.yml" + - "empty.yml" + paths: + - "{{ role_path }}/vars" tags: [always] -- import_tasks: install_apt.yml - when: ansible_pkg_mgr == "apt" +# --- Installers par famille/distro --- +- name: Install on APT-based systems + ansible.builtin.import_tasks: install_apt.yml + when: ansible_facts['pkg_mgr'] == 'apt' tags: [postgresql, postgresql-install] -- import_tasks: install_rhel.yml - when: (ansible_pkg_mgr == "yum" or ansible_pkg_mgr == "dnf") and (ansible_distribution == "RedHat" or ansible_distribution == "CentOS" or ansible_distribution == "OracleLinux" or ansible_distribution == "Rocky") +- name: Install on RHEL-like (non-Fedora) + ansible.builtin.import_tasks: install_rhel.yml + when: + - ansible_facts['pkg_mgr'] in ['yum', 'dnf'] + - ansible_facts['os_family'] == 'RedHat' + - ansible_facts['distribution'] not in ['Fedora'] tags: [postgresql, postgresql-install] -- import_tasks: install_fedora.yml - when: ansible_pkg_mgr == "dnf" and ansible_distribution == "Fedora" +- name: Install on Fedora + ansible.builtin.import_tasks: install_fedora.yml + when: + - ansible_facts['pkg_mgr'] == 'dnf' + - ansible_facts['distribution'] == 'Fedora' tags: [postgresql, postgresql-install] -- import_tasks: extensions.yml +# --- Reste du rôle --- +- name: Extensions + ansible.builtin.import_tasks: extensions.yml tags: [postgresql, postgresql-extensions] -- import_tasks: fdw.yml +- name: FDW + ansible.builtin.import_tasks: fdw.yml tags: [postgresql, postgresql-fdw] -- import_tasks: configure.yml +- name: Configure + ansible.builtin.import_tasks: configure.yml tags: [postgresql, postgresql-configure] -- import_tasks: users.yml +- name: Users + ansible.builtin.import_tasks: users.yml tags: [postgresql, postgresql-users] -- import_tasks: databases.yml +- name: Databases + ansible.builtin.import_tasks: databases.yml tags: [postgresql, postgresql-databases] -- import_tasks: schemas.yml +- name: Schemas + ansible.builtin.import_tasks: schemas.yml tags: [postgresql, postgresql-schemas] -- import_tasks: users_privileges.yml +- name: Users privileges + ansible.builtin.import_tasks: users_privileges.yml tags: [postgresql, postgresql-users] -- import_tasks: monit.yml - when: monit_protection is defined and monit_protection == true +- name: Monit (optional) + ansible.builtin.import_tasks: monit.yml + when: (monit_protection | default(false)) | bool tags: [postgresql, postgresql-monit] -- import_tasks: check_pg_version_mismatch.yml +- name: Check PG version mismatch + ansible.builtin.import_tasks: check_pg_version_mismatch.yml tags: [postgresql, postgresql-version-mismatch] diff --git a/tasks/schemas.yml b/tasks/schemas.yml index 7de47ba61..0fca38c38 100644 --- a/tasks/schemas.yml +++ b/tasks/schemas.yml @@ -1,17 +1,17 @@ # file: postgresql/tasks/schemas.yml - name: PostgreSQL | Add Schema to databases - postgresql_schema: - database: "{{ item.database }}" - schema: "{{ item.schema }}" + community.postgresql.postgresql_schema: + login_db: "{{ item.database }}" + name: "{{ item.schema }}" owner: "{{ item.owner | default(postgresql_database_owner) }}" login_host: "{{ item.host | default(omit) }}" login_password: "{{ item.password | default(omit) }}" login_user: "{{ postgresql_admin_user }}" - port: "{{ postgresql_port }}" + login_port: "{{ postgresql_port }}" state: "{{ item.state | default('present') }}" become: yes become_user: "{{ postgresql_admin_user }}" no_log: "{{ postgresql_hide_passwords }}" - with_items: "{{ postgresql_database_schemas }}" - when: postgresql_databases|length > 0 + loop: "{{ postgresql_database_schemas | default([]) }}" + when: (postgresql_database_schemas | default([])) | length > 0 diff --git a/tasks/users.yml b/tasks/users.yml index cca86fd60..58916383e 100644 --- a/tasks/users.yml +++ b/tasks/users.yml @@ -5,16 +5,25 @@ name: "{{ postgresql_service_name }}" state: started -- name: PostgreSQL | Make sure the PostgreSQL users are present - postgresql_user: - name: "{{item.name}}" - password: "{{ item.pass | default(omit) }}" - encrypted: "{{ item.encrypted | default(omit) }}" - port: "{{postgresql_port}}" +- name: PostgreSQL | Ensure users exist with role attributes (idempotent) + community.postgresql.postgresql_user: + name: "{{ item.name }}" + # If plain-text password, DO NOT set 'encrypted: true' + password: "{{ item.password | default(omit) }}" + # Only include this if the password is already hashed (md5... or SCRAM-SHA-256$...) + # encrypted: true + role_attr_flags: "{{ item.role_attr_flags | default(omit) }}" state: "{{ item.state | default('present') }}" - login_user: "{{postgresql_admin_user}}" - no_log: "{{ postgresql_hide_passwords }}" + no_password_changes: "{{ item.no_password_changes | default(omit) }}" + login_user: "{{ postgresql_admin_user }}" + login_password: "{{ postgresql_admin_password | default(omit) }}" + login_host: "{{ item.host | default(omit) }}" + login_port: "{{ postgresql_port }}" + login_db: "{{ item.login_db | default('postgres') }}" + no_log: "{{ postgresql_hide_passwords | default(true) }}" become: yes - become_user: "{{postgresql_admin_user}}" - with_items: "{{postgresql_users}}" - when: postgresql_users|length > 0 + become_user: "{{ postgresql_admin_user }}" + loop: "{{ postgresql_users | default([]) }}" + when: (postgresql_users | default([])) | length > 0 + loop_control: + label: "{{ item.name }}" diff --git a/tasks/users_privileges.yml b/tasks/users_privileges.yml index 94aaea245..a3e1b1ab2 100644 --- a/tasks/users_privileges.yml +++ b/tasks/users_privileges.yml @@ -1,16 +1,24 @@ # file: postgresql/tasks/users_privileges.yml +# Make sure the service is running first +- name: PostgreSQL | Ensure PostgreSQL is running + service: + name: "{{ postgresql_service_name }}" + state: started -- name: PostgreSQL | Update the user privileges - postgresql_user: - name: "{{item.name}}" - db: "{{item.db | default(omit)}}" - port: "{{postgresql_port}}" - priv: "{{item.priv | default(omit)}}" +# Grant database-level privileges +- name: PostgreSQL | Grant database privileges + community.postgresql.postgresql_privs: + type: database + login_db: "{{ item.db }}" # database to grant on + roles: "{{ item.name }}" # role receiving privileges + privs: "{{ item.privs | default('ALL') }}" + grant_option: "{{ item.grant_option | default(omit) }}" state: present - login_host: "{{item.host | default(omit)}}" - login_user: "{{postgresql_admin_user}}" - role_attr_flags: "{{item.role_attr_flags | default(omit)}}" + login_user: "{{ postgresql_admin_user }}" + login_password: "{{ postgresql_admin_password | default(omit) }}" + login_host: "{{ item.host | default(omit) }}" + login_port: "{{ postgresql_port }}" become: yes - become_user: "{{postgresql_admin_user}}" - with_items: "{{postgresql_user_privileges}}" - when: postgresql_users|length > 0 + become_user: "{{ postgresql_admin_user }}" + loop: "{{ postgresql_user_privileges | default([]) }}" + when: (postgresql_user_privileges | default([])) | length > 0 diff --git a/templates/postgresql.conf-14.j2 b/templates/postgresql.conf-14.j2 index 511d34093..39a62b822 100644 --- a/templates/postgresql.conf-14.j2 +++ b/templates/postgresql.conf-14.j2 @@ -731,7 +731,7 @@ jit_provider = '{{ postgresql_jit_provider }}' # JIT library to use # - Other Defaults - dynamic_library_path = '{{ postgresql_dynamic_library_path }}' -{% if postgresql_extension_destdir is defined and ansible_os_family == 'Debian' %} +{% if postgresql_extension_destdir is defined and ansible_facts['os_family'] == 'Debian' %} extension_destdir = '{{ postgresql_extension_destdir }}' # prepend path when loading extensions # and shared objects (added by Debian) {% endif %} diff --git a/templates/postgresql.conf-15.j2 b/templates/postgresql.conf-15.j2 index 7f4d61bc8..903ca9b39 100644 --- a/templates/postgresql.conf-15.j2 +++ b/templates/postgresql.conf-15.j2 @@ -746,7 +746,7 @@ jit_provider = '{{ postgresql_jit_provider }}' # JIT library to use # - Other Defaults - dynamic_library_path = '{{ postgresql_dynamic_library_path }}' -{% if postgresql_extension_destdir is defined and ansible_os_family == 'Debian' %} +{% if postgresql_extension_destdir is defined and ansible_facts['os_family'] == 'Debian' %} extension_destdir = '{{ postgresql_extension_destdir }}' # prepend path when loading extensions # and shared objects (added by Debian) {% endif %} diff --git a/templates/postgresql.conf-16.j2 b/templates/postgresql.conf-16.j2 index f7fd221b4..64c3b838b 100644 --- a/templates/postgresql.conf-16.j2 +++ b/templates/postgresql.conf-16.j2 @@ -755,7 +755,7 @@ jit_provider = '{{ postgresql_jit_provider }}' # JIT library to use # - Other Defaults - dynamic_library_path = '{{ postgresql_dynamic_library_path }}' -{% if postgresql_extension_destdir is defined and ansible_os_family == 'Debian' %} +{% if postgresql_extension_destdir is defined and ansible_facts['os_family'] == 'Debian' %} extension_destdir = '{{ postgresql_extension_destdir }}' # prepend path when loading extensions # and shared objects (added by Debian) {% endif %} diff --git a/templates/postgresql.conf-17.j2 b/templates/postgresql.conf-17.j2 index 16f9aae10..4aa176611 100644 --- a/templates/postgresql.conf-17.j2 +++ b/templates/postgresql.conf-17.j2 @@ -776,7 +776,7 @@ jit_provider = '{{ postgresql_jit_provider }}' # JIT library to use # - Other Defaults - dynamic_library_path = '{{ postgresql_dynamic_library_path }}' -{% if postgresql_extension_destdir is defined and ansible_os_family == 'Debian' %} +{% if postgresql_extension_destdir is defined and ansible_facts['os_family'] == 'Debian' %} extension_destdir = '{{ postgresql_extension_destdir }}' # prepend path when loading extensions # and shared objects (added by Debian) {% endif %} diff --git a/tests/docker/group_vars/all.yml b/tests/docker/group_vars/all.yml index ddaf391d2..6b2fe8071 100644 --- a/tests/docker/group_vars/all.yml +++ b/tests/docker/group_vars/all.yml @@ -1,7 +1,6 @@ --- postgresql_versions: - - 12 - 13 - 14 - 15 diff --git a/tests/playbook.yml b/tests/playbook.yml index 07ad2478a..ec508e66f 100644 --- a/tests/playbook.yml +++ b/tests/playbook.yml @@ -1,9 +1,15 @@ --- - hosts: all - #remote_user: root become: yes + gather_facts: true + vars_files: - ./vars.yml - - ./vars.{{ ansible_distribution }}.{{ ansible_distribution_major_version }}.yml + + pre_tasks: + - name: Load OS-specific vars (first match wins) + ansible.builtin.include_vars: + file: "vars.{{ ansible_facts.distribution }}.{{ ansible_facts.distribution_major_version }}.yml" + roles: - - postgresql + - role: "{{ lookup('env','MOLECULE_PROJECT_DIRECTORY') }}" diff --git a/tests/prepare.yml b/tests/prepare.yml index 65a564e83..00685ff7b 100644 --- a/tests/prepare.yml +++ b/tests/prepare.yml @@ -10,11 +10,16 @@ become: yes vars_files: - ./vars.yml - - ./vars.{{ ansible_distribution }}.{{ ansible_distribution_major_version }}.yml + + pre_tasks: + - name: Include OS-specific vars + ansible.builtin.include_vars: >- + ./vars.{{ ansible_facts.distribution }}.{{ ansible_facts.distribution_major_version }}.yml + tasks: - name: Pick the right sudoers group (wheel or sudo) depending on the distro set_fact: - sudoers_group: "{{ 'sudo' if (ansible_distribution == 'Ubuntu' or ansible_distribution == 'Debian') else 'wheel' }}" + sudoers_group: "{{ 'sudo' if (ansible_facts.get('distribution') in ['Ubuntu', 'Debian']) else 'wheel' }}" - name: Set up non root ansible user user: name: ansible @@ -39,7 +44,7 @@ update_cache: true name: - acl - when: (ansible_distribution == 'Ubuntu' and ansible_distribution_major_version | int < 18 ) + when: (ansible_facts.get('distribution') == 'Ubuntu') and ((ansible_facts.get('distribution_major_version') | default('0') | int) < 18) - name: Install prerequisite packages for molecule testing for Debian/Ubuntu >= 18 apt: @@ -47,7 +52,7 @@ name: - gpg-agent - acl - when: (ansible_distribution == 'Ubuntu' and ansible_distribution_major_version | int >= 18) or ansible_distribution == 'Debian' + when: ((ansible_facts.get('distribution') == 'Ubuntu') and ((ansible_facts.get('distribution_major_version') | default('0') | int) >= 18)) or (ansible_facts.get('distribution') == 'Debian') #--------------------- # RHEL based distros @@ -58,8 +63,8 @@ - langpacks-en - glibc-all-langpacks when: - - "ansible_os_family == 'RedHat'" - - "ansible_distribution_major_version == '8'" + - ansible_facts.os_family == 'RedHat' + - ansible_facts.distribution_major_version == '8' #--------------------- # Fedora based distros @@ -71,5 +76,5 @@ until: "'running' in systemctl_status.stdout" retries: 30 delay: 5 - when: (ansible_distribution == 'Fedora') + when: ansible_facts.distribution == 'Fedora' changed_when: false diff --git a/tests/vars.Ubuntu.20.yml b/tests/vars.Ubuntu.20.yml deleted file mode 100644 index ed97d539c..000000000 --- a/tests/vars.Ubuntu.20.yml +++ /dev/null @@ -1 +0,0 @@ ---- diff --git a/vars/postgresql_12.yml b/vars/postgresql_12.yml deleted file mode 100644 index b3ff92088..000000000 --- a/vars/postgresql_12.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -# PostgreSQL vars for v12 - -ssl_min_protocol_version: "TLSv1"