diff --git a/.drone.jsonnet b/.drone.jsonnet index a4051ea..2f70f7e 100644 --- a/.drone.jsonnet +++ b/.drone.jsonnet @@ -41,7 +41,7 @@ local PipelineLinting = { }, }; -local PipelineDeployment(scenario='centos7') = { +local PipelineDeployment(scenario='rocky9') = { kind: 'pipeline', name: 'testing-' + scenario, platform: { @@ -115,8 +115,7 @@ local PipelineDocumentation = { ref: ['refs/heads/main', 'refs/tags/**', 'refs/pull/**'], }, depends_on: [ - 'testing-centos7', - 'testing-rocky8', + 'testing-rocky9', ], }; @@ -154,8 +153,7 @@ local PipelineNotification = { [ PipelineLinting, - PipelineDeployment(scenario='centos7'), - PipelineDeployment(scenario='rocky8'), + PipelineDeployment(scenario='rocky9'), PipelineDocumentation, PipelineNotification, ] diff --git a/.drone.yml b/.drone.yml index dec633f..3b9fe7c 100644 --- a/.drone.yml +++ b/.drone.yml @@ -36,7 +36,7 @@ trigger: --- kind: pipeline -name: testing-centos7 +name: testing-rocky9 platform: os: linux @@ -53,40 +53,7 @@ steps: - name: ansible-molecule image: thegeeklab/molecule:4 commands: - - molecule test -s centos7 - environment: - HCLOUD_TOKEN: - from_secret: hcloud_token - -trigger: - ref: - - refs/heads/main - - refs/tags/** - - refs/pull/** - -depends_on: - - linting - ---- -kind: pipeline -name: testing-rocky8 - -platform: - os: linux - arch: amd64 - -concurrency: - limit: 1 - -workspace: - base: /drone/src - path: ${DRONE_REPO_NAME} - -steps: - - name: ansible-molecule - image: thegeeklab/molecule:4 - commands: - - molecule test -s rocky8 + - molecule test -s rocky9 environment: HCLOUD_TOKEN: from_secret: hcloud_token @@ -141,8 +108,7 @@ trigger: - refs/pull/** depends_on: - - testing-centos7 - - testing-rocky8 + - testing-rocky9 --- kind: pipeline @@ -182,6 +148,6 @@ depends_on: --- kind: signature -hmac: 7353897e25c84160f6b3c20326eef030f8ca14b29e6222577987b44ed52e74c9 +hmac: 254bcb40ef9b131e680482b851975b18ebecb5ce9ce316bfe00b5d672c7e6de1 ... diff --git a/defaults/main.yml b/defaults/main.yml index 3bfc0fd..d3067a1 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -1,93 +1,76 @@ --- -postgres_repository_enabled: False -postgres_version: 14 -postgres_repository_filename: "Postgresql-{{ postgres_version | regex_replace('\\.') }}" +postgres_image: "docker.io/library/postgres:latest" +postgres_hostname: postgres +postgres_uid: 999 +postgres_gid: 999 + +# @var postgres_volumes:description: > Define required docker volumes. +# @end +# @var postgres_volumes:example: > +# postgres_volumes: +# - name: data +# # target location inside the container +# dest: /var/lib/postgresql/data +# type: volume +# @end +postgres_volumes: + - name: "postgres-initdb" + dest: "/docker-entrypoint-initdb.d" + - name: "postgres-data" + dest: /var/lib/postgresql/data + +# @var postgres_network:description: > +# Name of the container network. If the name ends with `.network`, the network will be created with the specified configuration. +# Otherwise, the network must already exist and the container will be attached to the network. +# @end +postgres_network: "postgres.network" +postgres_network_ipv6_enabled: False +# @var postgres_network_ipv6_subnet:value: $ "_unset_" +# @var postgres_network_ipv6_subnet:example: $ "fd00:0:0:2::/64" +# @var postgres_network_ipv6_gateway:value: $ "_unset_" +# @var postgres_network_ipv6_gateway:example: $ "fd00:0:0:2::1" + +# @var postgres_network_ipv4_subnet:value: $ "_unset_" +# @var postgres_network_ipv4_gateway:value: $ "_unset_" + +# @var postgres_exposed_ports:description: > +# Ports you want to publish outside of Docker. Postgres is running on `5432` inside of the container. +# @end +postgres_exposed_ports: [] + +postgres_cap_add: [] +postgres_cap_drop: [] + +postgres_podman_args: + - --pids-limit=-1 + - --health-cmd='["pg_isready", "-d", "{{ postgres_db }}"]' + - --health-interval=5s + - --health-timeout=5s + - --health-retries=6 + - --health-on-failure=kill + - --workdir=/var/lib/postgresql/data + +postgres_log_level: warning + postgres_user: postgres -postgres_group: postgres +postgres_password: postgres +postgres_db: postgres -# @var postgres_base_dir: $ "_osdefault_" +# @var postgres_app_user:description: > +# Application user name without superuser privileges. Full access to `postgres_app_db` +# will be granted to this user. +# @end +# @var postgres_app_user:value: $ "_unset_" +# @var postgres_app_password:description: Application user password. +# @var postgres_app_password:value: $ "_unset_" +# @var postgres_app_db:description: Application database name. +# @var postgres_app_db:value: $ "_unset_" -postgres_log_destination: - - stderr -postgres_log_directory: log -postgres_log_filename: postgresql.log -postgres_log_rotation_age: 1d -postgres_log_rotation_size: 0 - -postgres_connection_port: 5432 -postgres_connection_addresses: - - localhost -postgres_socket_directories: - - /var/run/postgresql - -postgres_password_encryption: scram-sha-256 +postgres_host_auth_method: scram-sha-256 +postgres_initdb_args: + - --auth-host=scram-sha-256 postgres_tls_enabled: False -postgres_tls_cert_filename: "mycert.pem" -postgres_tls_key_filename: "mykey.pem" -postgres_tls_cert_source: mycert.pem -postgres_tls_key_source: mykey.pem - -postgres_users: [] -# @var postgres_users:example: > -# postgres_users: -# - name: jdoe #required; the rest are optional -# password: # defaults to not set -# encrypted: # defaults to 'yes' -# priv: # defaults to not set -# role_attr_flags: # defaults to not set -# db: # defaults to not set -# login_host: # defaults to 'localhost' -# login_password: # defaults to not set -# login_user: # defaults to '{{ postgres_user }}' -# login_unix_socket: # defaults to 1st of postgres_socket_directories -# port: # defaults to not set -# state: # defaults to 'present' -# pam_user: # defaults to not set -# @end - -postgres_users_extra: [] - -postgres_dbs: [] -# @var postgres_db:example: > -# postgres_db: -# - name: "my_app" -# lc_collate: "en_US.UTF-8" -# lc_ctype: "en_US.UTF-8" -# encoding: "UTF-8" -# template: "template0" -# login_host: "localhost" -# login_password: "_omit_" -# login_user: "{{ postgres_user }}" -# login_unix_socket: "_omit_" -# port: "_omit_" -# owner: "_omit_" -# state: "present" -# @end - -postgres_dbs_extra: [] - -postgres_clean_hba_file: True -postgres_hba_entries: - - contype: local - databases: - - all - users: - - all - auth_method: trust - - contype: host - databases: - - all - users: - - all - address: "127.0.0.1/32" - auth_method: "{{ postgres_password_encryption }}" - - contype: host - databases: - - all - users: - - all - address: "::1/128" - auth_method: "{{ postgres_password_encryption }}" - -postgres_hba_entries_extra: [] +postgres_tls_ca_file: "/var/lib/postgresql/tls/CA.pem" +postgres_tls_cert_file: "/var/lib/postgresql/tls/cert.pem" +postgres_tls_key_file: "/var/lib/postgresql/tls/key.pem" diff --git a/files/init-user-db.sh b/files/init-user-db.sh new file mode 100644 index 0000000..a77f42a --- /dev/null +++ b/files/init-user-db.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env sh +set -e + +if [ -n "$POSTGRES_APP_USER" ] && [ -n "$POSTGRES_APP_PASSWORD" ] && [ -n "$POSTGRES_APP_DB" ]; then + echo "Create app user and database" + psql -v ON_ERROR_STOP=1 --username "${POSTGRES_USER:-postgres}" <<-EOSQL + CREATE USER $POSTGRES_APP_USER WITH ENCRYPTED PASSWORD '${POSTGRES_APP_PASSWORD}'; + CREATE DATABASE $POSTGRES_APP_DB; + GRANT ALL PRIVILEGES ON DATABASE $POSTGRES_APP_DB TO $POSTGRES_APP_USER; + ALTER DATABASE $POSTGRES_APP_DB OWNER TO $POSTGRES_APP_USER; +EOSQL +fi diff --git a/handlers/main.yml b/handlers/main.yml index 637eb88..629eb1b 100644 --- a/handlers/main.yml +++ b/handlers/main.yml @@ -1,17 +1,7 @@ --- -- block: - - name: Restart service - service: - name: "{{ __postgres_daemon }}" - state: restarted - enabled: yes - daemon_reload: yes - listen: __postgres_restart - - - name: Reload service - service: - name: "{{ __postgres_daemon }}" - state: reloaded - listen: __postgres_reload - become: True - become_user: root +- name: Restart PostgreSQL + ansible.builtin.service: + name: postgres + state: restarted + daemon_reload: True + listen: __postgres_restart diff --git a/meta/main.yml b/meta/main.yml index a925d1f..e567478 100644 --- a/meta/main.yml +++ b/meta/main.yml @@ -16,15 +16,17 @@ galaxy_info: # @end description: Setup a PostgreSQL database server license: MIT - min_ansible_version: 2.10 + min_ansible_version: "2.10" platforms: - name: EL versions: - - 7 + - "9" galaxy_tags: - db - postgres - postgresql dependencies: [] collections: + - xoxys.general - community.general + - containers.podman diff --git a/molecule/centos7/converge.yml b/molecule/centos7/converge.yml deleted file mode 100644 index 376d574..0000000 --- a/molecule/centos7/converge.yml +++ /dev/null @@ -1,15 +0,0 @@ ---- -- name: Converge - hosts: all - vars: - postgres_repository_enabled: True - postgres_users: - - name: "pgdemo" - password: "secure" - priv: ALL - db: "demo" - postgres_dbs: - - name: demo - - roles: - - role: xoxys.postgres diff --git a/molecule/centos7/create.yml b/molecule/centos7/create.yml deleted file mode 100644 index 8b945cd..0000000 --- a/molecule/centos7/create.yml +++ /dev/null @@ -1,120 +0,0 @@ ---- -- name: Create - hosts: localhost - connection: local - gather_facts: false - no_log: "{{ molecule_no_log }}" - vars: - ssh_port: 22 - ssh_user: root - ssh_path: "{{ lookup('env', 'MOLECULE_EPHEMERAL_DIRECTORY') }}/ssh_key" - tasks: - - name: Create SSH key - user: - name: "{{ lookup('env', 'USER') }}" - generate_ssh_key: true - ssh_key_file: "{{ ssh_path }}" - force: true - register: generated_ssh_key - - - name: Register the SSH key name - set_fact: - ssh_key_name: "molecule-generated-{{ 12345 | random | to_uuid }}" - - - name: Register SSH key for test instance(s) - hcloud_ssh_key: - name: "{{ ssh_key_name }}" - public_key: "{{ generated_ssh_key.ssh_public_key }}" - state: present - - - name: Create molecule instance(s) - hcloud_server: - name: "{{ item.name }}" - server_type: "{{ item.server_type }}" - ssh_keys: - - "{{ ssh_key_name }}" - image: "{{ item.image }}" - location: "{{ item.location | default(omit) }}" - datacenter: "{{ item.datacenter | default(omit) }}" - user_data: "{{ item.user_data | default(omit) }}" - api_token: "{{ lookup('env', 'HCLOUD_TOKEN') }}" - state: present - register: server - loop: "{{ molecule_yml.platforms }}" - async: 7200 - poll: 0 - - - name: Wait for instance(s) creation to complete - async_status: - jid: "{{ item.ansible_job_id }}" - register: hetzner_jobs - until: hetzner_jobs.finished - retries: 300 - loop: "{{ server.results }}" - - - name: Create volume(s) - hcloud_volume: - name: "{{ item.name }}" - server: "{{ item.name }}" - location: "{{ item.location | default(omit) }}" - size: "{{ item.volume_size | default(10) }}" - api_token: "{{ lookup('env', 'HCLOUD_TOKEN') }}" - state: "present" - loop: "{{ molecule_yml.platforms }}" - when: item.volume | default(False) | bool - register: volumes - async: 7200 - poll: 0 - - - name: Wait for volume(s) creation to complete - async_status: - jid: "{{ item.ansible_job_id }}" - register: hetzner_volumes - until: hetzner_volumes.finished - retries: 300 - when: volumes.changed - loop: "{{ volumes.results }}" - - # Mandatory configuration for Molecule to function. - - - name: Populate instance config dict - set_fact: - instance_conf_dict: - { - "instance": "{{ item.hcloud_server.name }}", - "ssh_key_name": "{{ ssh_key_name }}", - "address": "{{ item.hcloud_server.ipv4_address }}", - "user": "{{ ssh_user }}", - "port": "{{ ssh_port }}", - "identity_file": "{{ ssh_path }}", - "volume": "{{ item.item.item.volume | default(False) | bool }}", - } - loop: "{{ hetzner_jobs.results }}" - register: instance_config_dict - when: server.changed | bool - - - name: Convert instance config dict to a list - set_fact: - instance_conf: "{{ instance_config_dict.results | map(attribute='ansible_facts.instance_conf_dict') | list }}" - when: server.changed | bool - - - name: Dump instance config - copy: - content: | - # Molecule managed - - {{ instance_conf | to_nice_yaml(indent=2) }} - dest: "{{ molecule_instance_config }}" - when: server.changed | bool - - - name: Wait for SSH - wait_for: - port: "{{ ssh_port }}" - host: "{{ item.address }}" - search_regex: SSH - delay: 10 - loop: "{{ lookup('file', molecule_instance_config) | from_yaml }}" - - - name: Wait for VM to settle down - pause: - seconds: 30 diff --git a/molecule/centos7/molecule.yml b/molecule/centos7/molecule.yml deleted file mode 100644 index bc4ce9f..0000000 --- a/molecule/centos7/molecule.yml +++ /dev/null @@ -1,24 +0,0 @@ ---- -dependency: - name: galaxy - options: - role-file: molecule/requirements.yml - requirements-file: molecule/requirements.yml - env: - ANSIBLE_GALAXY_DISPLAY_PROGRESS: "false" -driver: - name: delegated -platforms: - - name: centos7-postgres - image: centos-7 - server_type: cx11 -lint: | - /usr/local/bin/flake8 -provisioner: - name: ansible - env: - ANSIBLE_FILTER_PLUGINS: ${ANSIBLE_FILTER_PLUGINS:-./plugins/filter} - ANSIBLE_LIBRARY: ${ANSIBLE_LIBRARY:-./library} - log: False -verifier: - name: testinfra diff --git a/molecule/default b/molecule/default index 62ea184..afa9fc6 120000 --- a/molecule/default +++ b/molecule/default @@ -1 +1 @@ -rocky8 \ No newline at end of file +rocky9 \ No newline at end of file diff --git a/molecule/requirements.yml b/molecule/requirements.yml index 46da115..927757f 100644 --- a/molecule/requirements.yml +++ b/molecule/requirements.yml @@ -1,6 +1,12 @@ --- collections: - - name: https://gitea.rknet.org/ansible/xoxys.general/releases/download/v2.1.1/xoxys-general-2.1.1.tar.gz + - name: https://gitea.rknet.org/ansible/xoxys.general + type: git - name: community.general + - name: containers.podman -roles: [] +roles: + - src: https://gitea.rknet.org/ansible/xoxys.podman + name: xoxys.podman + scm: git + version: main diff --git a/molecule/rocky8/converge.yml b/molecule/rocky8/converge.yml deleted file mode 100644 index 376d574..0000000 --- a/molecule/rocky8/converge.yml +++ /dev/null @@ -1,15 +0,0 @@ ---- -- name: Converge - hosts: all - vars: - postgres_repository_enabled: True - postgres_users: - - name: "pgdemo" - password: "secure" - priv: ALL - db: "demo" - postgres_dbs: - - name: demo - - roles: - - role: xoxys.postgres diff --git a/molecule/rocky8/destroy.yml b/molecule/rocky8/destroy.yml deleted file mode 100644 index 6454c71..0000000 --- a/molecule/rocky8/destroy.yml +++ /dev/null @@ -1,78 +0,0 @@ ---- -- name: Destroy - hosts: localhost - connection: local - gather_facts: false - no_log: "{{ molecule_no_log }}" - tasks: - - name: Check existing instance config file - stat: - path: "{{ molecule_instance_config }}" - register: cfg - - - name: Populate the instance config - set_fact: - instance_conf: "{{ (lookup('file', molecule_instance_config) | from_yaml) if cfg.stat.exists else [] }}" - - - name: Destroy molecule instance(s) - hcloud_server: - name: "{{ item.instance }}" - api_token: "{{ lookup('env', 'HCLOUD_TOKEN') }}" - state: absent - register: server - loop: "{{ instance_conf }}" - async: 7200 - poll: 0 - - - name: Wait for instance(s) deletion to complete - async_status: - jid: "{{ item.ansible_job_id }}" - register: hetzner_jobs - until: hetzner_jobs.finished - retries: 300 - loop: "{{ server.results }}" - - - pause: - seconds: 5 - - - name: Destroy volume(s) - hcloud_volume: - name: "{{ item.instance }}" - server: "{{ item.instance }}" - api_token: "{{ lookup('env', 'HCLOUD_TOKEN') }}" - state: "absent" - register: volumes - loop: "{{ instance_conf }}" - when: item.volume | default(False) | bool - async: 7200 - poll: 0 - - - name: Wait for volume(s) deletion to complete - async_status: - jid: "{{ item.ansible_job_id }}" - register: hetzner_volumes - until: hetzner_volumes.finished - retries: 300 - when: volumes.changed - loop: "{{ volumes.results }}" - - - name: Remove registered SSH key - hcloud_ssh_key: - name: "{{ instance_conf[0].ssh_key_name }}" - state: absent - when: (instance_conf | default([])) | length > 0 - - # Mandatory configuration for Molecule to function. - - - name: Populate instance config - set_fact: - instance_conf: {} - - - name: Dump instance config - copy: - content: | - # Molecule managed - - {{ instance_conf | to_nice_yaml(indent=2) }} - dest: "{{ molecule_instance_config }}" - when: server.changed | bool diff --git a/molecule/rocky8/prepare.yml b/molecule/rocky8/prepare.yml deleted file mode 100644 index 183f4d3..0000000 --- a/molecule/rocky8/prepare.yml +++ /dev/null @@ -1,15 +0,0 @@ ---- -- name: Prepare - hosts: all - gather_facts: false - tasks: - - name: Bootstrap python for Ansible - raw: | - command -v python3 python || ( - (test -e /usr/bin/dnf && sudo dnf install -y python3) || - (test -e /usr/bin/apt && (apt -y update && apt install -y python-minimal)) || - (test -e /usr/bin/yum && sudo yum -y -qq install python3) || - echo "Warning: Python not boostrapped due to unknown platform." - ) - become: true - changed_when: false diff --git a/molecule/rocky8/tests/test_default.py b/molecule/rocky8/tests/test_default.py deleted file mode 100644 index e672871..0000000 --- a/molecule/rocky8/tests/test_default.py +++ /dev/null @@ -1,28 +0,0 @@ -import os - -import testinfra.utils.ansible_runner - -testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner( - os.environ["MOLECULE_INVENTORY_FILE"] -).get_hosts("all") - - -def test_postgres_is_installed(host): - postgres = host.package("postgresql14-server") - assert postgres.is_installed - - -def test_postgres_running_and_enabled(host): - postgres = host.service("postgresql-14") - assert postgres.is_running - assert postgres.is_enabled - - -def test_postgres_auth(host): - login = host.run("PGPASSWORD=secure psql -U pgdemo -h localhost -c 'select 1' -q demo") - assert login.succeeded - - -def test_postgres_socket(host): - # Verify the socket is listening for HTTP traffic - assert host.socket("tcp://127.0.0.1:5432").is_listening diff --git a/molecule/rocky9/converge.yml b/molecule/rocky9/converge.yml new file mode 100644 index 0000000..3430ee2 --- /dev/null +++ b/molecule/rocky9/converge.yml @@ -0,0 +1,10 @@ +--- +- name: Converge + hosts: all + roles: + - role: xoxys.podman + - role: xoxys.postgres + vars: + postgres_exposed_ports: + - 127.0.0.1:5432:5432 + postgres_log_level: info diff --git a/molecule/rocky8/create.yml b/molecule/rocky9/create.yml similarity index 99% rename from molecule/rocky8/create.yml rename to molecule/rocky9/create.yml index 8b945cd..719600d 100644 --- a/molecule/rocky8/create.yml +++ b/molecule/rocky9/create.yml @@ -117,4 +117,4 @@ - name: Wait for VM to settle down pause: - seconds: 30 + seconds: 30 \ No newline at end of file diff --git a/molecule/rocky9/default b/molecule/rocky9/default new file mode 120000 index 0000000..331d858 --- /dev/null +++ b/molecule/rocky9/default @@ -0,0 +1 @@ +default \ No newline at end of file diff --git a/molecule/centos7/destroy.yml b/molecule/rocky9/destroy.yml similarity index 98% rename from molecule/centos7/destroy.yml rename to molecule/rocky9/destroy.yml index 6454c71..ed0b2ed 100644 --- a/molecule/centos7/destroy.yml +++ b/molecule/rocky9/destroy.yml @@ -75,4 +75,4 @@ {{ instance_conf | to_nice_yaml(indent=2) }} dest: "{{ molecule_instance_config }}" - when: server.changed | bool + when: server.changed | bool \ No newline at end of file diff --git a/molecule/rocky8/molecule.yml b/molecule/rocky9/molecule.yml similarity index 91% rename from molecule/rocky8/molecule.yml rename to molecule/rocky9/molecule.yml index 63f80ba..2299330 100644 --- a/molecule/rocky8/molecule.yml +++ b/molecule/rocky9/molecule.yml @@ -9,8 +9,8 @@ dependency: driver: name: delegated platforms: - - name: rocky8-postgres - image: rocky-8 + - name: rocky9-postgres + image: rocky-9 server_type: cx11 lint: | /usr/local/bin/flake8 diff --git a/molecule/centos7/prepare.yml b/molecule/rocky9/prepare.yml similarity index 100% rename from molecule/centos7/prepare.yml rename to molecule/rocky9/prepare.yml diff --git a/molecule/centos7/tests/test_default.py b/molecule/rocky9/tests/test_default.py similarity index 65% rename from molecule/centos7/tests/test_default.py rename to molecule/rocky9/tests/test_default.py index e672871..1ecfe27 100644 --- a/molecule/centos7/tests/test_default.py +++ b/molecule/rocky9/tests/test_default.py @@ -7,19 +7,16 @@ testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner( ).get_hosts("all") -def test_postgres_is_installed(host): - postgres = host.package("postgresql14-server") - assert postgres.is_installed - - def test_postgres_running_and_enabled(host): - postgres = host.service("postgresql-14") + postgres = host.service("postgres") assert postgres.is_running assert postgres.is_enabled def test_postgres_auth(host): - login = host.run("PGPASSWORD=secure psql -U pgdemo -h localhost -c 'select 1' -q demo") + login = host.run( + "podman exec --env PGPASSWORD=postgres systemd-postgres psql -Upostgres -c 'select 1' -q postgres" # noqa :E501 + ) assert login.succeeded diff --git a/tasks/config.yml b/tasks/config.yml deleted file mode 100644 index 873415d..0000000 --- a/tasks/config.yml +++ /dev/null @@ -1,34 +0,0 @@ ---- -- block: - - name: Setup global config file - template: - src: postgresql/data/postgresql.conf.j2 - dest: "{{ __postgres_config_path }}/postgresql.conf" - mode: 0600 - notify: __postgres_restart - - - name: Create local users for pam auth - user: - name: "{{ item.name }}" - password: "{{ item.password | password_hash('sha512', 65534 | random(seed=inventory_hostname) | string) }}" - state: "{{ item.state | default('present') }}" - loop: "{{ postgres_users }}" - loop_control: - label: "{{ item.name }}" - when: item.pam_user | default(False) - - - name: Setup client authentication - postgresql_pg_hba: - dest: "{{ __postgres_config_path }}/pg_hba.conf" - contype: "{{ item.contype | default('local') }}" - users: "{{ item.users | default(['all']) | join(',') }}" - address: "{{ item.address | default('all') }}" - databases: "{{ item.databases | default(['all']) | join(',') }}" - method: "{{ item.auth_method | default(postgres_password_encryption) }}" - state: "{{ item.state | default('present') }}" - loop: "{{ postgres_hba_entries + postgres_hba_entries_extra }}" - loop_control: - label: "{{ item.address | default('samehost') }}:{{ item.databases | default(['all']) | join(',') }}:{{ item.users | default(['all']) | join(',') }}" - notify: __postgres_restart - become: True - become_user: root diff --git a/tasks/install.yml b/tasks/install.yml deleted file mode 100644 index eb9f44d..0000000 --- a/tasks/install.yml +++ /dev/null @@ -1,48 +0,0 @@ ---- -- block: - - name: Install PostgreSQL - package: - name: "{{ item }}" - state: present - loop: "{{ __postgres_packages }}" - - - name: Ensure data directory exists - file: - path: "{{ __postgres_data_dir }}" - owner: "{{ postgres_user }}" - group: "{{ postgres_group }}" - state: directory - mode: 0700 - - - name: Setup custom systemd service - template: - src: etc/systemd/system/postgresql.service.j2 - dest: "/etc/systemd/system/{{ __postgres_daemon }}.service" - mode: 0644 - notify: __postgres_restart - register: __postgres_systemd - - - name: Force systemd to re-read configs - service: - daemon_reload: True - when: __postgres_systemd.changed - - - name: Check if database is initialized - stat: - path: "{{ __postgres_data_dir }}/PG_VERSION" - register: __pgdata_dir_version - - - name: Ensure database is initialized - command: "/usr/pgsql-{{ __postgres_version }}/bin/postgresql-{{ __postgres_version }}-setup initdb" - when: not __pgdata_dir_version.stat.exists - - - name: Override default pg_hba.conf with a clean one - template: - src: templates/postgresql/data/pg_hba.conf.j2 - dest: "{{ __postgres_config_path }}/pg_hba.conf" - mode: 0644 - when: - - not __pgdata_dir_version.stat.exists - - postgres_clean_hba_file | bool - become: True - become_user: root diff --git a/tasks/main.yml b/tasks/main.yml index fde0d29..9f42235 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -1,20 +1,62 @@ --- -- import_tasks: prepare.yml +- name: Create network specs + ansible.builtin.template: + src: etc/containers/systemd/postgres.network.j2 + dest: "/etc/containers/systemd/{{ postgres_network }}" + owner: root + group: root + mode: "0640" + when: postgres_network | splitext | last == ".network" + notify: __postgres_restart -- include_tasks: "{{ task_files }}" - vars: - task_files: "{{ lookup('first_found', params, errors='ignore') }}" - params: - files: - - "prepare_{{ ansible_os_family | lower }}_{{ ansible_distribution_major_version }}.yml" - - "prepare_{{ ansible_os_family | lower }}.yml" - paths: - - "tasks" - when: task_files +- name: Create container volumes + containers.podman.podman_volume: + name: "{{ item.name }}" + options: "{{ item.options | default(omit) }}" + state: "{{ item.state | default('present') }}" + loop: "{{ postgres_volumes }}" + loop_control: + label: "{{ item.name }}" + when: item.type | default("volume") | lower == "volume" + register: __postgres_volumes_raw -- import_tasks: install.yml -- import_tasks: config.yml -- import_tasks: tls.yml - when: postgres_tls_enabled | bool - tags: tls_renewal -- import_tasks: post_tasks.yml +- name: Register container volumes map + ansible.builtin.set_fact: + __postgres_volumes_map: "{{ __postgres_volumes_raw.results | json_query('[].volume') | items2dict(key_name='Name', value_name='Mountpoint') }}" + +- name: Deploy postgres env files + ansible.builtin.template: + src: "etc/containers/systemd/{{ item }}.j2" + dest: "/etc/containers/systemd/{{ item }}" + owner: root + group: root + mode: "0640" + loop: + - postgres.env + - postgres.sys.env + notify: __postgres_restart + +- name: Create container specs + ansible.builtin.template: + src: etc/containers/systemd/postgres.container.j2 + dest: "/etc/containers/systemd/postgres.container" + owner: root + group: root + mode: "0640" + notify: __postgres_restart + +- name: Deploy init-user-db + ansible.builtin.copy: + src: init-user-db.sh + dest: "{{ __postgres_volumes_map['postgres-initdb'] }}" + owner: "{{ postgres_uid }}" + group: "{{ postgres_gid }}" + mode: "0644" + when: "'postgres-initdb' in __postgres_volumes_map" + +- name: Ensure service state + ansible.builtin.service: + name: "postgres.service" + state: started + daemon_reload: True + enabled: True diff --git a/tasks/post_tasks.yml b/tasks/post_tasks.yml deleted file mode 100644 index 27f269d..0000000 --- a/tasks/post_tasks.yml +++ /dev/null @@ -1,53 +0,0 @@ ---- -- name: Force all notified handlers to activate pg_hba.conf - meta: flush_handlers - -- name: Ensure postgres databases are present - postgresql_db: - name: "{{ item.name }}" - lc_collate: "{{ item.lc_collate | default('en_US.UTF-8') }}" - lc_ctype: "{{ item.lc_ctype | default('en_US.UTF-8') }}" - encoding: "{{ item.encoding | default('UTF-8') }}" - template: "{{ item.template | default('template0') }}" - login_host: "{{ item.login_host | default(omit) }}" - login_password: "{{ item.login_password | default(omit) }}" - login_user: "{{ item.login_user | default(postgres_user) }}" - login_unix_socket: "{{ item.login_unix_socket | default(omit) }}" - port: "{{ item.port | default(omit) }}" - owner: "{{ item.owner | default(omit) }}" - state: "{{ item.state | default('present') }}" - loop: "{{ postgres_dbs + postgres_dbs_extra }}" - loop_control: - label: "{{ item.name }}" - become: True - become_user: "{{ postgres_user }}" - -- name: Ensure PostgreSQL users are present - environment: - PGOPTIONS: "-c password_encryption={{ postgres_password_encryption }}" - postgresql_user: - name: "{{ item.name }}" - password: "{{ item.password | default(omit) }}" - encrypted: "{{ item.encrypted | default('yes') }}" - priv: "{{ item.priv | default(omit) }}" - role_attr_flags: "{{ item.role_attr_flags | default(omit) }}" - db: "{{ item.db | default(omit) }}" - login_host: "{{ item.login_host | default(omit) }}" - login_password: "{{ item.login_password | default(omit) }}" - login_user: "{{ item.login_user | default(postgres_user) }}" - login_unix_socket: "{{ item.login_unix_socket | default(postgres_socket_directories[0]) }}" - port: "{{ item.port | default(omit) }}" - state: "{{ item.state | default('present') }}" - loop: "{{ postgres_users + postgres_users_extra }}" - loop_control: - label: "{{ item.name }}" - become: True - become_user: "{{ postgres_user }}" - -- name: Ensure PostgreSQL is up and running - service: - name: "{{ __postgres_daemon }}" - state: started - enabled: yes - become: True - become_user: root diff --git a/tasks/prepare.yml b/tasks/prepare.yml deleted file mode 100644 index 3cfa149..0000000 --- a/tasks/prepare.yml +++ /dev/null @@ -1,13 +0,0 @@ ---- -- name: Add PostgreSQL repository - yum_repository: - name: "postgresql-{{ __postgres_version }}" - file: "{{ postgres_repository_filename }}" - description: "PostgreSQL {{ __postgres_version }} yum repository" - baseurl: "https://download.postgresql.org/pub/repos/yum/{{ __postgres_version }}/redhat/rhel-{{ ansible_distribution_major_version }}-{{ ansible_architecture }}" - gpgcheck: yes - enabled: yes - gpgkey: "https://download.postgresql.org/pub/repos/yum/RPM-GPG-KEY-PGDG-{{ __postgres_version }}" - become: True - become_user: root - when: postgres_repository_enabled | bool diff --git a/tasks/prepare_redhat_7.yml b/tasks/prepare_redhat_7.yml deleted file mode 100644 index 3fd9e96..0000000 --- a/tasks/prepare_redhat_7.yml +++ /dev/null @@ -1,9 +0,0 @@ ---- -- name: Install rh7 dependencies - package: - name: "{{ item }}" - state: present - loop: - - python-psycopg2 - become: True - become_user: root diff --git a/tasks/prepare_redhat_8.yml b/tasks/prepare_redhat_8.yml deleted file mode 100644 index df3563e..0000000 --- a/tasks/prepare_redhat_8.yml +++ /dev/null @@ -1,24 +0,0 @@ ---- -- block: - - name: Disable default Postgres module - copy: - dest: /etc/dnf/modules.d/postgresql.module - content: | - [postgresql] - name=postgresql - stream= - profiles= - state=disabled - mode: 0644 - owner: root - group: root - when: postgres_repository_enabled | bool - - - name: Install rh8 dependencies - package: - name: "{{ item }}" - state: present - loop: - - python3-psycopg2 - become: True - become_user: root diff --git a/tasks/tls.yml b/tasks/tls.yml deleted file mode 100644 index 434b197..0000000 --- a/tasks/tls.yml +++ /dev/null @@ -1,29 +0,0 @@ ---- -- block: - - name: Create tls folder structure - file: - path: "{{ item }}" - state: directory - owner: "{{ postgres_user }}" - group: "{{ postgres_group }}" - recurse: True - loop: - - "{{ __postgres_tls_key_path | dirname }}" - - "{{ __postgres_tls_cert_path | dirname }}" - become: True - become_user: root - -- block: - - name: Copy certs and private key - copy: - src: "{{ item.src }}" - dest: "{{ item.dest }}" - mode: "{{ item.mode }}" - loop: - - { src: "{{ postgres_tls_key_source }}", dest: '{{ __postgres_tls_key_path }}', mode: '0600' } - - { src: "{{ postgres_tls_cert_source }}", dest: '{{ __postgres_tls_cert_path }}', mode: '0750' } - loop_control: - label: "{{ item.dest }}" - notify: __postgres_reload - become: True - become_user: "{{ postgres_user }}" diff --git a/templates/etc/containers/systemd/postgres.container.j2 b/templates/etc/containers/systemd/postgres.container.j2 new file mode 100644 index 0000000..764a08e --- /dev/null +++ b/templates/etc/containers/systemd/postgres.container.j2 @@ -0,0 +1,38 @@ +#jinja2: lstrip_blocks: True +{{ ansible_managed | comment }} +[Install] +WantedBy=default.target + +[Service] +Restart=on-failure +RestartSec=5s + +EnvironmentFile=/etc/containers/systemd/postgres.sys.env + +[Container] +Image={{ postgres_image }} +Exec=postgres $POSTGRES_ARGS +EnvironmentFile=/etc/containers/systemd/postgres.env +User={{ postgres_uid }} +Group={{ postgres_gid }} +HostName={{ postgres_hostname }} +{% for item in postgres_volumes %} +Volume={{ item.name }}:{{ item.dest }}{{ ":" + item.opts if item.opts is defined else "" }} +{% endfor %} +{% if (postgres_cap_add + postgres_cap_drop) | length > 0 %} + +{% if postgres_cap_add | length > 0 %} +AddCapability={{ postgres_cap_add | join(" ") }} +{% endif %} +{% if postgres_cap_drop | length > 0 %} +DropCapability={{ postgres_cap_drop | join(" ") }} +{% endif %} +{% endif %} + +Network={{ postgres_network }} +{% for item in postgres_exposed_ports %} +PublishPort={{ item }} +{% endfor %} +{% for item in postgres_podman_args %} +PodmanArgs={{ item }} +{% endfor %} diff --git a/templates/etc/containers/systemd/postgres.env.j2 b/templates/etc/containers/systemd/postgres.env.j2 new file mode 100644 index 0000000..2620c8e --- /dev/null +++ b/templates/etc/containers/systemd/postgres.env.j2 @@ -0,0 +1,18 @@ +#jinja2: lstrip_blocks: True +{{ ansible_managed | comment }} +POSTGRES_USER={{ postgres_user }} +POSTGRES_PASSWORD={{ postgres_password }} +POSTGRES_DB={{ postgres_db }} +{% if postgres_app_user is defined and postgres_app_password is defined and postgres_app_db is defined %} + +POSTGRES_APP_USER={{ postgres_app_user }} +POSTGRES_APP_PASSWORD={{ postgres_app_password }} +POSTGRES_APP_DB={{ postgres_app_db }} +{% endif%} + +{% if postgres_host_auth_method is defined %} +POSTGRES_HOST_AUTH_METHOD={{ postgres_host_auth_method }} +{% endif %} +{% if postgres_initdb_args | length > 0 %} +POSTGRES_INITDB_ARGS={{ postgres_initdb_args | join(" ") }} +{% endif %} diff --git a/templates/etc/containers/systemd/postgres.network.j2 b/templates/etc/containers/systemd/postgres.network.j2 new file mode 100644 index 0000000..7c18a79 --- /dev/null +++ b/templates/etc/containers/systemd/postgres.network.j2 @@ -0,0 +1,19 @@ +#jinja2: lstrip_blocks: True +{{ ansible_managed | comment }} +[Network] +{% if postgres_network_ipv4_subnet is defined %} +Subnet={{ postgres_network_ipv4_subnet }} +{% endif %} +{% if postgres_network_ipv4_gateway is defined %} +Gateway={{ postgres_network_ipv4_gateway }} +{% endif %} +IPv6={{ postgres_network_ipv6_enabled | bool | lower }} +{% if postgres_network_ipv6_enabled | bool %} +{% if postgres_network_ipv6_subnet is defined %} +Subnet={{ postgres_network_ipv6_subnet }} +{% endif %} +{% if postgres_network_ipv6_gateway is defined %} +Gateway={{ postgres_network_ipv6_gateway }} +{% endif %} +{% endif %} +Label=app=postgres diff --git a/templates/etc/containers/systemd/postgres.sys.env.j2 b/templates/etc/containers/systemd/postgres.sys.env.j2 new file mode 100644 index 0000000..58c5d4d --- /dev/null +++ b/templates/etc/containers/systemd/postgres.sys.env.j2 @@ -0,0 +1,28 @@ +#jinja2: lstrip_blocks: True +{{ ansible_managed | comment }} +POSTGRES_ARGS=-c timezone='Etc/UTC' -c log_min_messages={{ postgres_log_level }} -c log_timezone='Etc/UTC' \ + {% if postgres_tls_enabled %} + -c ssl=on \ + {% if postgres_tls_ca_file is defined %} + -c ssl_ca_file='{{ postgres_tls_ca_file }}' \ + {% endif %} + {% if postgres_tls_cert_file is defined %} + -c ssl_cert_file='{{ postgres_tls_cert_file }}' \ + {% endif %} + {% if postgres_tls_key_file is defined %} + -c ssl_key_file='{{ postgres_tls_key_file }}' \ + {% endif %} + -c ssl_ciphers='HIGH:MEDIUM:+3DES:!aNULL' \ + -c ssl_prefer_server_ciphers=on \ + -c ssl_ecdh_curve='prime256v1' \ + -c ssl_min_protocol_version='TLSv1.2' \ + {% else %} + -c ssl=off \ + {% endif %} + -c datestyle='iso, mdy' \ + -c lc_messages='en_US.UTF-8' \ + -c lc_monetary='en_US.UTF-8' \ + -c lc_numeric='en_US.UTF-8' \ + -c lc_time='en_US.UTF-8' \ + -c default_text_search_config='pg_catalog.english' \ + -c port=5432 diff --git a/templates/etc/systemd/system/postgresql.service.j2 b/templates/etc/systemd/system/postgresql.service.j2 deleted file mode 100644 index bf7afb4..0000000 --- a/templates/etc/systemd/system/postgresql.service.j2 +++ /dev/null @@ -1,4 +0,0 @@ -{{ ansible_managed | comment }} -.include {{ __postgres_service_file }} -[Service] -Environment=PGDATA={{ __postgres_data_dir }} diff --git a/templates/postgresql/data/pg_hba.conf.j2 b/templates/postgresql/data/pg_hba.conf.j2 deleted file mode 100644 index 2f49f08..0000000 --- a/templates/postgresql/data/pg_hba.conf.j2 +++ /dev/null @@ -1,5 +0,0 @@ -# PostgreSQL Client Authentication Configuration File -# =================================================== -# -# See: https://www.postgresql.org/docs/current/static/auth-pg-hba-conf.html -# TYPE DATABASE USER ADDRESS METHOD diff --git a/templates/postgresql/data/postgresql.conf.j2 b/templates/postgresql/data/postgresql.conf.j2 deleted file mode 100644 index 13ac7fe..0000000 --- a/templates/postgresql/data/postgresql.conf.j2 +++ /dev/null @@ -1,86 +0,0 @@ -{{ ansible_managed | comment }} -# ----------------------------- -# PostgreSQL configuration file -# ----------------------------- - -#------------------------------------------------------------------------------ -# CONNECTIONS AND AUTHENTICATION -#------------------------------------------------------------------------------ - -# - Connection Settings - - -listen_addresses = '{{ postgres_connection_addresses | join(",") }}' -port = {{ postgres_connection_port }} -max_connections = 100 -unix_socket_directories = '{{ postgres_socket_directories | join(",") }}' - -# - Authentication - - -authentication_timeout = 1min -password_encryption = {{ postgres_password_encryption }} - -# - SSL - - -{% if postgres_tls_enabled %} -ssl = on -ssl_cert_file = '{{ __postgres_tls_cert_path }}' -ssl_key_file = '{{ __postgres_tls_key_path }}' -{% else %} -ssl = off -{% endif %} - - -#------------------------------------------------------------------------------ -# RESOURCE USAGE (except WAL) -#------------------------------------------------------------------------------ - -# - Memory - - -shared_buffers = 128MB -dynamic_shared_memory_type = posix - -# - Checkpoints - - -max_wal_size = 1GB -min_wal_size = 80MB - - -#------------------------------------------------------------------------------ -# REPORTING AND LOGGING -#------------------------------------------------------------------------------ - -# - Where to Log - - -log_destination = '{{ postgres_log_destination | join(",") }}' -{% if "stderr" in postgres_log_destination or "csvlog" in postgres_log_destination %} -logging_collector = on -{% else %} -logging_collector = off -{% endif %} -log_directory = '{{ postgres_log_directory }}' -log_filename = '{{ postgres_log_filename }}' -log_rotation_age = {{ postgres_log_rotation_age }} -log_rotation_size = {{ postgres_log_rotation_size }} -log_truncate_on_rotation = on - -# - What to Log - - -log_line_prefix = '%m [%p] ' -log_timezone = 'Etc/UTC' - - -#------------------------------------------------------------------------------ -# CLIENT CONNECTION DEFAULTS -#------------------------------------------------------------------------------ - -# - Locale and Formatting - - -datestyle = 'iso, mdy' -timezone = 'Etc/UTC' - -lc_messages = 'en_US.UTF-8' -lc_monetary = 'en_US.UTF-8' # locale for monetary formatting -lc_numeric = 'en_US.UTF-8' # locale for number formatting -lc_time = 'en_US.UTF-8' # locale for time formatting - -default_text_search_config = 'pg_catalog.english' diff --git a/vars/main.yml b/vars/main.yml deleted file mode 100644 index 92477b9..0000000 --- a/vars/main.yml +++ /dev/null @@ -1,12 +0,0 @@ ---- -__postgres_version: "{{ postgres_version | regex_replace('\\.') }}" -__postgres_packagename: "postgresql{{ __postgres_version }}" -__postgres_daemon: "postgresql-{{ __postgres_version }}" -__postgres_service_file: "/usr/lib/systemd/system/{{ __postgres_daemon }}.service" -__postgres_packages: - - "{{ __postgres_packagename }}" - - "{{ __postgres_packagename }}-server" -__postgres_data_dir: "{{ postgres_base_dir | default('/var/lib/pgsql/' + __postgres_version) }}/data" -__postgres_config_path: "{{ __postgres_data_dir }}" -__postgres_tls_key_path: "{{ __postgres_data_dir }}/tls/key/{{ postgres_tls_key_filename }}" -__postgres_tls_cert_path: "{{ __postgres_data_dir }}/tls/certs/{{ postgres_tls_cert_filename }}"