From 03a3dd33ea36f794680d02963d6a25ac00cf2f49 Mon Sep 17 00:00:00 2001 From: George Spanos Date: Thu, 5 Jun 2025 15:02:45 +0300 Subject: [PATCH 1/2] postgresql_db: fix session_role support for raw connections (#865) (cherry picked from commit de362160f4be9459dd2ad6292565ae700f4c625e) --- .../865-postgresql_db-session_role-fix.yml | 2 + plugins/modules/postgresql_db.py | 20 +- .../targets/postgresql_db/tasks/main.yml | 2 + .../tasks/state_dump_restore_role.yml | 198 ++++++++++++++++++ 4 files changed, 220 insertions(+), 2 deletions(-) create mode 100644 changelogs/fragments/865-postgresql_db-session_role-fix.yml create mode 100644 tests/integration/targets/postgresql_db/tasks/state_dump_restore_role.yml diff --git a/changelogs/fragments/865-postgresql_db-session_role-fix.yml b/changelogs/fragments/865-postgresql_db-session_role-fix.yml new file mode 100644 index 00000000..29845e12 --- /dev/null +++ b/changelogs/fragments/865-postgresql_db-session_role-fix.yml @@ -0,0 +1,2 @@ +bugfixes: + - postgresql_db - fixed ``session_role`` parameter that was being ignored for raw connections (https://github.com/ansible-collections/community.postgresql/pull/865) \ No newline at end of file diff --git a/plugins/modules/postgresql_db.py b/plugins/modules/postgresql_db.py index 6b3eaa7b..5976b80f 100644 --- a/plugins/modules/postgresql_db.py +++ b/plugins/modules/postgresql_db.py @@ -530,6 +530,7 @@ def db_dump(module, target, target_opts="", password=None, host=None, port=None, + session_role=None, **kw): flags = login_flags(db, host, port, user, db_prefix=False) @@ -553,6 +554,9 @@ def db_dump(module, target, target_opts="", elif os.path.splitext(target)[-1] == '.xz': comp_prog_path = module.get_bin_path('xz', True) + if session_role: + flags.append(' --role={0}'.format(shlex_quote(session_role))) + cmd += "".join(flags) if dump_extra_args: @@ -583,11 +587,13 @@ def db_restore(module, target, target_opts="", password=None, host=None, port=None, + session_role=None, **kw): flags = login_flags(db, host, port, user) comp_prog_path = None cmd = module.get_bin_path('psql', True) + pg_restore = False if os.path.splitext(target)[-1] == '.sql': flags.append(' --file={0}'.format(target)) @@ -595,14 +601,17 @@ def db_restore(module, target, target_opts="", elif os.path.splitext(target)[-1] == '.tar': flags.append(' --format=Tar') cmd = module.get_bin_path('pg_restore', True) + pg_restore = True elif os.path.splitext(target)[-1] == '.pgc': flags.append(' --format=Custom') cmd = module.get_bin_path('pg_restore', True) + pg_restore = True elif os.path.splitext(target)[-1] == '.dir': flags.append(' --format=Directory') cmd = module.get_bin_path('pg_restore', True) + pg_restore = True elif os.path.splitext(target)[-1] == '.gz': comp_prog_path = module.get_bin_path('zcat', True) @@ -613,6 +622,9 @@ def db_restore(module, target, target_opts="", elif os.path.splitext(target)[-1] == '.xz': comp_prog_path = module.get_bin_path('xzcat', True) + if pg_restore and session_role: + flags.append(' --role={0}'.format(shlex_quote(session_role))) + cmd += "".join(flags) if target_opts: cmd += " {0} ".format(target_opts) @@ -843,9 +855,13 @@ def main(): method = state == "dump" and db_dump or db_restore if state == 'dump': - rc, stdout, stderr, cmd = method(module, target, target_opts, db, dump_extra_args, **conn_params) + rc, stdout, stderr, cmd = method( + module, target, target_opts, db, dump_extra_args, session_role=session_role, **conn_params + ) else: - rc, stdout, stderr, cmd = method(module, target, target_opts, db, **conn_params) + rc, stdout, stderr, cmd = method( + module, target, target_opts, db, session_role=session_role, **conn_params + ) if rc != 0: module.fail_json(msg=stderr, stdout=stdout, rc=rc, cmd=cmd) diff --git a/tests/integration/targets/postgresql_db/tasks/main.yml b/tests/integration/targets/postgresql_db/tasks/main.yml index 6db4c577..3511d198 100644 --- a/tests/integration/targets/postgresql_db/tasks/main.yml +++ b/tests/integration/targets/postgresql_db/tasks/main.yml @@ -43,6 +43,8 @@ file: dbdata.tar test_fixture: admin +- import_tasks: state_dump_restore_role.yml + # Simple test to create and then drop with force - import_tasks: manage_database.yml diff --git a/tests/integration/targets/postgresql_db/tasks/state_dump_restore_role.yml b/tests/integration/targets/postgresql_db/tasks/state_dump_restore_role.yml new file mode 100644 index 00000000..dfe67a4d --- /dev/null +++ b/tests/integration/targets/postgresql_db/tasks/state_dump_restore_role.yml @@ -0,0 +1,198 @@ +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +# ============================================================ + +- name: create a new role + postgresql_user: + name: "{{ db_session_role1 }}" + state: "present" + encrypted: 'true' + password: "password" + login_user: "{{ pg_user }}" + db: postgres + become: true + become_user: "{{ pg_user }}" + +- name: make db_session_role1 an admin for convenience + postgresql_membership: + group: "{{ pg_user }}" + target_roles: "{{ db_session_role1 }}" + state: present + +# needs to be .pgc to force use of pg_dump/pg_restore binaries +- set_fact: db_file_name="{{tmp_dir}}/dbdata.pgc" + +- name: create test databases with different owners + postgresql_db: + state: present + name: "{{ item.name }}" + owner: "{{ item.owner }}" + login_user: "{{ pg_user }}" + loop: + - name: src_db + owner: "{{ pg_user }}" + - name: dst_db + owner: "{{ db_session_role1 }}" + +- name: create schema1 in src_db owned by pg_user + postgresql_schema: + state: present + name: schema1 + login_db: src_db + login_user: "{{ pg_user }}" + +- name: create employees table in src_db.schema1 + postgresql_table: + name: schema1.employees + columns: + - id bigserial primary key + - name varchar(20) + state: present + login_db: src_db + login_user: "{{ pg_user }}" + +- name: populate src_db.schema1.employees + postgresql_query: + login_db: src_db + login_user: "{{ pg_user }}" + query: >- + INSERT INTO schema1.employees (name) + VALUES ('Guybrush Threepwood'); + +- name: pg_dump src_db.schema1 as pg_user + postgresql_db: + login_user: "{{ pg_user }}" + name: src_db + state: dump + target: "{{ db_file_name }}" + target_opts: "-n schema1" + register: result + become_user: "{{ pg_user }}" + become: true + +- name: assert pg_dump usage with -n parameter + assert: + that: + - result is changed + - result.executed_commands[0] is search("/bin/pg_dump") + - result.executed_commands[0] is search("-n schema1") + +- name: pg_restore archive on dst_db with session_role + postgresql_db: + login_user: "{{ pg_user }}" + name: dst_db + state: restore + target: "{{ db_file_name }}" + session_role: "{{ db_session_role1 }}" + target_opts: "--no-owner" + register: result + become_user: "{{ pg_user }}" + become: true + +- name: assert pg_restore usage with role parameter + assert: + that: + - result is changed + - result.executed_commands[0] is search("/bin/pg_restore") + - result.executed_commands[0] is search("--role=") + +- name: check restored schema1 owner is db_session_role1 + postgresql_query: + login_db: dst_db + login_user: "{{ pg_user }}" + query: >- + SELECT r.rolname AS owner + FROM pg_namespace ns + JOIN pg_roles r ON ns.nspowner = r.oid + WHERE ns.nspname = 'schema1'; + register: result + +- assert: + that: + - result.query_result[0]['owner'] == db_session_role1 + +- name: check restored schema1.employees owner is db_session_role1 + postgresql_query: + login_db: dst_db + login_user: "{{ pg_user }}" + query: >- + SELECT tableowner as owner + FROM pg_tables WHERE tablename = 'employees' and schemaname = 'schema1' + register: result + +- assert: + that: + - result.query_result[0]['owner'] == db_session_role1 + +- name: drop schema1 from dst_db + postgresql_schema: + state: absent + cascade_drop: true + name: schema1 + login_db: dst_db + login_user: "{{ pg_user }}" + +- name: remove db archive + file: + name: "{{ db_file_name }}" + state: absent + +- name: pg_dump src_db.schema1 with session_role + postgresql_db: + login_user: "{{ pg_user }}" + session_role: "{{ db_session_role1 }}" + name: src_db + state: dump + target: "{{ db_file_name }}" + target_opts: "-n schema1" + register: result + become_user: "{{ pg_user }}" + become: true + +- name: assert pg_dump usage with role parameter + assert: + that: + - result is changed + - result.executed_commands[0] is search("/bin/pg_dump") + - result.executed_commands[0] is search("-n schema1") + - result.executed_commands[0] is search("--role=") + +- name: verify archive file was created + ansible.builtin.stat: + path: "{{ db_file_name }}" + +# Clean up +- name: remove test databases + postgresql_db: + name: "{{ item }}" + login_user: "{{ pg_user }}" + state: absent + loop: + - src_db + - dst_db + +- name: remove db archive + file: + name: "{{ db_file_name }}" + state: absent + +- name: remove db_session_role1 user + postgresql_user: + login_user: "{{ pg_user }}" + name: "{{ db_session_role1 }}" + state: absent + become: true + become_user: "{{ pg_user }}" From edbf591e16e5eb2476e27f4f1c78abcb6d0a801c Mon Sep 17 00:00:00 2001 From: George Spanos Date: Fri, 5 Dec 2025 16:49:11 +0200 Subject: [PATCH 2/2] postgresql_db: fix integration test for fedora42 (#906) --- .../targets/postgresql_db/tasks/state_dump_restore_role.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/integration/targets/postgresql_db/tasks/state_dump_restore_role.yml b/tests/integration/targets/postgresql_db/tasks/state_dump_restore_role.yml index dfe67a4d..31180e04 100644 --- a/tests/integration/targets/postgresql_db/tasks/state_dump_restore_role.yml +++ b/tests/integration/targets/postgresql_db/tasks/state_dump_restore_role.yml @@ -87,7 +87,7 @@ assert: that: - result is changed - - result.executed_commands[0] is search("/bin/pg_dump") + - result.executed_commands[0] is search("/s?bin/pg_dump") - result.executed_commands[0] is search("-n schema1") - name: pg_restore archive on dst_db with session_role @@ -106,7 +106,7 @@ assert: that: - result is changed - - result.executed_commands[0] is search("/bin/pg_restore") + - result.executed_commands[0] is search("/s?bin/pg_restore") - result.executed_commands[0] is search("--role=") - name: check restored schema1 owner is db_session_role1 @@ -166,7 +166,7 @@ assert: that: - result is changed - - result.executed_commands[0] is search("/bin/pg_dump") + - result.executed_commands[0] is search("/s?bin/pg_dump") - result.executed_commands[0] is search("-n schema1") - result.executed_commands[0] is search("--role=")