summaryrefslogtreecommitdiffstats
path: root/ansible_collections/community/postgresql/tests
diff options
context:
space:
mode:
Diffstat (limited to 'ansible_collections/community/postgresql/tests')
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_copy/aliases2
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_copy/meta/main.yml2
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_copy/tasks/main.yml8
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_copy/tasks/postgresql_copy_initial.yml278
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_db/aliases3
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_db/defaults/main.yml11
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_db/meta/main.yml2
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_db/tasks/main.yml47
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_db/tasks/manage_database.yml9
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_db/tasks/postgresql_db_general.yml152
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_db/tasks/postgresql_db_initial.yml366
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_db/tasks/postgresql_db_session_role.yml80
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_db/tasks/state_dump_restore.yml235
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_db/tasks/state_rename.yml261
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_ext/aliases3
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_ext/defaults/main.yml2
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_ext/meta/main.yml3
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_ext/tasks/main.yml26
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_ext/tasks/postgresql_ext_initial.yml208
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_ext/tasks/postgresql_ext_session_role.yml114
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_ext/tasks/postgresql_ext_version_opt.yml554
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_idx/aliases2
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_idx/meta/main.yml2
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_idx/tasks/main.yml7
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_idx/tasks/postgresql_idx_initial.yml377
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_info/aliases4
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_info/defaults/main.yml13
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_info/meta/main.yml2
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_info/tasks/main.yml12
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_info/tasks/postgresql_info_initial.yml243
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_info/tasks/setup_publication.yml64
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_lang/aliases2
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_lang/meta/main.yml2
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_lang/tasks/main.yml25
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_lang/tasks/postgresql_lang_add_owner_param.yml199
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_lang/tasks/postgresql_lang_initial.yml231
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_lang/vars/CentOS-7.yml3
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_lang/vars/CentOS-8.yml3
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_lang/vars/default.yml0
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_membership/aliases2
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_membership/defaults/main.yml6
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_membership/meta/main.yml2
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_membership/tasks/main.yml7
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_membership/tasks/postgresql_membership_initial.yml736
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_owner/aliases2
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_owner/defaults/main.yml3
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_owner/meta/main.yml2
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_owner/tasks/main.yml9
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_owner/tasks/postgresql_owner_initial.yml1073
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_pg_hba/aliases2
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_pg_hba/defaults/main.yml29
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_pg_hba/meta/main.yml2
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_pg_hba/tasks/main.yml8
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_pg_hba/tasks/postgresql_pg_hba_bulk_rules.yml136
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_pg_hba/tasks/postgresql_pg_hba_initial.yml264
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_ping/aliases2
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_ping/defaults/main.yml2
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_ping/meta/main.yml2
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_ping/tasks/main.yml9
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_ping/tasks/postgresql_ping_initial.yml187
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_privs/aliases2
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_privs/defaults/main.yml14
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_privs/meta/main.yml2
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_privs/tasks/main.yml19
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_privs/tasks/pg_authid_not_readable.yml50
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_privs/tasks/postgresql_privs_general.yml1767
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_privs/tasks/postgresql_privs_initial.yml407
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_privs/tasks/postgresql_privs_session_role.yml102
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_privs/tasks/test_target_role.yml120
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_publication/aliases3
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_publication/meta/main.yml2
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_publication/tasks/main.yml8
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_publication/tasks/postgresql_publication_initial.yml436
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_query/aliases2
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_query/files/test0.sql6
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_query/files/test1.sql10
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_query/meta/main.yml2
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_query/tasks/main.yml7
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_query/tasks/postgresql_query_initial.yml604
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_schema/aliases2
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_schema/defaults/main.yml7
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_schema/meta/main.yml2
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_schema/tasks/main.yml9
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_schema/tasks/postgresql_schema_initial.yml331
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_schema/tasks/postgresql_schema_session_role.yml78
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/aliases2
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/defaults/main.yml1
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/files/test0.sql4
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/files/test1.sql10
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/files/test10.sql1
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/files/test11.sql1
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/files/test12.sql1
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/files/test2.sql1
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/files/test3.sql4
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/files/test4.sql1
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/files/test5.sql1
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/files/test6.sql1
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/files/test7.sql1
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/files/test8.sql1
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/files/test9.sql1
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/meta/main.yml2
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/tasks/main.yml7
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/tasks/postgresql_script_initial.yml311
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_sequence/aliases2
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_sequence/defaults/main.yml5
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_sequence/meta/main.yml2
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_sequence/tasks/main.yml8
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_sequence/tasks/postgresql_sequence_initial.yml730
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_set/aliases2
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_set/meta/main.yml2
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_set/tasks/main.yml11
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_set/tasks/options_coverage.yml71
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_set/tasks/postgresql_set_initial.yml442
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_slot/aliases2
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_slot/meta/main.yml2
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_slot/tasks/main.yml9
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_slot/tasks/postgresql_slot_initial.yml735
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_subscription/aliases4
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_subscription/defaults/main.yml13
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_subscription/meta/main.yml2
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_subscription/tasks/main.yml12
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_subscription/tasks/postgresql_subscription_initial.yml672
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_subscription/tasks/setup_publication.yml85
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_table/aliases2
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_table/meta/main.yml2
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_table/tasks/main.yml7
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_table/tasks/postgresql_table_initial.yml899
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_tablespace/aliases2
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_tablespace/defaults/main.yml3
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_tablespace/meta/main.yml2
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_tablespace/tasks/main.yml7
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_tablespace/tasks/postgresql_tablespace_initial.yml245
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_user/aliases2
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_user/defaults/main.yml4
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_user/meta/main.yml2
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_user/tasks/main.yml12
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_user/tasks/postgresql_user_general.yml802
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_user/tasks/postgresql_user_initial.yml156
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_user/tasks/test_no_password_change.yml167
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_user/tasks/test_password.yml429
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_user_obj_stat_info/aliases4
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_user_obj_stat_info/defaults/main.yml12
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_user_obj_stat_info/meta/main.yml2
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_user_obj_stat_info/tasks/main.yml8
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/postgresql_user_obj_stat_info/tasks/postgresql_user_obj_stat_info.yml222
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/setup_pkg_mgr/tasks/main.yml17
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/defaults/main.yml21
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/files/dummy--0.sql2
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/files/dummy--1.0.sql2
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/files/dummy--2.0.sql2
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/files/dummy--3-1.0-1.sql2
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/files/dummy--3-1.0.sql2
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/files/dummy--3-1.foo.sql2
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/files/dummy--3.0-1.sql2
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/files/dummy--3.0-foo.sql2
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/files/dummy--3.0.sql2
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/files/dummy--3.beta.sql2
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/files/dummy--4.0.sql2
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/files/dummy--v4.sql2
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/files/dummy.control3
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/files/pg_hba.conf10
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/meta/main.yml2
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/tasks/main.yml279
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/tasks/ssl.yml108
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/vars/Debian-8.yml9
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/vars/RedHat-py3.yml9
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/vars/RedHat.yml8
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/vars/Ubuntu-20-py3.yml13
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/vars/default-py3.yml7
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/vars/default.yml7
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_replication/defaults/main.yml26
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_replication/handlers/main.yml24
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_replication/tasks/main.yml13
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_replication/tasks/setup_postgresql_cluster.yml149
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_replication/templates/pg_hba.conf.j27
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_replication/templates/primary_postgresql.conf.j228
-rw-r--r--ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_replication/templates/replica_postgresql.conf.j228
-rw-r--r--ansible_collections/community/postgresql/tests/requirements.yml3
-rw-r--r--ansible_collections/community/postgresql/tests/sanity/extra/no-unwanted-files.json7
-rwxr-xr-xansible_collections/community/postgresql/tests/sanity/extra/no-unwanted-files.py43
-rw-r--r--ansible_collections/community/postgresql/tests/sanity/ignore-2.12.txt5
-rw-r--r--ansible_collections/community/postgresql/tests/sanity/ignore-2.13.txt5
-rw-r--r--ansible_collections/community/postgresql/tests/sanity/ignore-2.14.txt5
-rw-r--r--ansible_collections/community/postgresql/tests/sanity/ignore-2.15.txt6
-rw-r--r--ansible_collections/community/postgresql/tests/sanity/ignore-2.16.txt6
-rw-r--r--ansible_collections/community/postgresql/tests/unit/plugins/module_utils/__init__.py0
-rw-r--r--ansible_collections/community/postgresql/tests/unit/plugins/module_utils/test_postgres.py338
-rw-r--r--ansible_collections/community/postgresql/tests/unit/plugins/module_utils/test_saslprep.py55
-rw-r--r--ansible_collections/community/postgresql/tests/unit/plugins/modules/__init__.py0
-rw-r--r--ansible_collections/community/postgresql/tests/unit/plugins/modules/test_postgresql_set.py28
-rw-r--r--ansible_collections/community/postgresql/tests/utils/constraints.txt52
-rwxr-xr-xansible_collections/community/postgresql/tests/utils/shippable/aix.sh22
-rwxr-xr-xansible_collections/community/postgresql/tests/utils/shippable/check_matrix.py120
-rwxr-xr-xansible_collections/community/postgresql/tests/utils/shippable/freebsd.sh22
-rwxr-xr-xansible_collections/community/postgresql/tests/utils/shippable/linux.sh18
-rwxr-xr-xansible_collections/community/postgresql/tests/utils/shippable/remote.sh22
-rwxr-xr-xansible_collections/community/postgresql/tests/utils/shippable/rhel.sh22
-rwxr-xr-xansible_collections/community/postgresql/tests/utils/shippable/sanity.sh27
-rwxr-xr-xansible_collections/community/postgresql/tests/utils/shippable/shippable.sh208
-rwxr-xr-xansible_collections/community/postgresql/tests/utils/shippable/timing.py16
-rwxr-xr-xansible_collections/community/postgresql/tests/utils/shippable/timing.sh5
-rwxr-xr-xansible_collections/community/postgresql/tests/utils/shippable/units.sh26
202 files changed, 18040 insertions, 0 deletions
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_copy/aliases b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_copy/aliases
new file mode 100644
index 000000000..a4c92ef85
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_copy/aliases
@@ -0,0 +1,2 @@
+destructive
+shippable/posix/group1
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_copy/meta/main.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_copy/meta/main.yml
new file mode 100644
index 000000000..4ce5a5837
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_copy/meta/main.yml
@@ -0,0 +1,2 @@
+dependencies:
+ - setup_postgresql_db
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_copy/tasks/main.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_copy/tasks/main.yml
new file mode 100644
index 000000000..359c5d3be
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_copy/tasks/main.yml
@@ -0,0 +1,8 @@
+####################################################################
+# WARNING: These are designed specifically for Ansible tests #
+# and should not be used as examples of how to write Ansible roles #
+####################################################################
+
+# Initial CI tests of postgresql_copy module
+- import_tasks: postgresql_copy_initial.yml
+ when: postgres_version_resp.stdout is version('9.4', '>=')
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_copy/tasks/postgresql_copy_initial.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_copy/tasks/postgresql_copy_initial.yml
new file mode 100644
index 000000000..5c51c108e
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_copy/tasks/postgresql_copy_initial.yml
@@ -0,0 +1,278 @@
+# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) <aaklychkov@mail.ru>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+# The file for testing postgresql_copy module.
+
+- vars:
+ test_table: acme
+ data_file_txt: /tmp/data.txt
+ data_file_csv: /tmp/data.csv
+ task_parameters: &task_parameters
+ become_user: '{{ pg_user }}'
+ become: true
+ register: result
+ pg_parameters: &pg_parameters
+ login_user: '{{ pg_user }}'
+ login_db: postgres
+
+ block:
+ # Test preparation:
+ - name: postgresql_copy - create test table
+ <<: *task_parameters
+ postgresql_table:
+ <<: *pg_parameters
+ name: '{{ test_table }}'
+ columns:
+ - id int
+ - name text
+
+ # Insert the data:
+ - name: postgresql_copy - insert rows into test table
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: "INSERT INTO {{ test_table }} (id, name) VALUES (1, 'first')"
+
+ - name: postgresql_copy - ensure that test data files don't exist
+ <<: *task_parameters
+ file:
+ path: '{{ item }}'
+ state: absent
+ with_items:
+ - '{{ data_file_csv }}'
+ - '{{ data_file_txt }}'
+
+ # ##############
+ # Do main tests:
+
+ # check_mode - if it's OK, must always return changed=True:
+ - name: postgresql_copy - check_mode, copy test table content to data_file_txt
+ check_mode: true
+ <<: *task_parameters
+ postgresql_copy:
+ <<: *pg_parameters
+ copy_to: '{{ data_file_txt }}'
+ src: '{{ test_table }}'
+ trust_input: false
+
+ - assert:
+ that:
+ - result is changed
+
+ # check that nothing changed after the previous step:
+ - name: postgresql_copy - check that data_file_txt doesn't exist
+ <<: *task_parameters
+ ignore_errors: true
+ shell: head -n 1 '{{ data_file_txt }}'
+
+ - assert:
+ that:
+ - result.failed == true
+ - result.rc == 1
+
+ # check_mode - if it's OK, must always return changed=True:
+ - name: postgresql_copy - check_mode, copy test table content from data_file_txt
+ check_mode: true
+ <<: *task_parameters
+ postgresql_copy:
+ <<: *pg_parameters
+ copy_from: '{{ data_file_txt }}'
+ dst: '{{ test_table }}'
+ trust_input: false
+
+ - assert:
+ that:
+ - result is changed
+
+ # check that nothing changed after the previous step:
+ - name: postgresql_copy - check that test table continue to have one row
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: 'SELECT * FROM {{ test_table }}'
+
+ - assert:
+ that:
+ - result.rowcount == 1
+
+ # check_mode - test must fail because test table doesn't exist:
+ - name: postgresql_copy - check_mode, copy non existent table to data_file_txt
+ check_mode: true
+ ignore_errors: true
+ <<: *task_parameters
+ postgresql_copy:
+ <<: *pg_parameters
+ copy_to: '{{ data_file_txt }}'
+ src: non_existent_table
+ trust_input: false
+
+ - assert:
+ that:
+ - result.failed == true
+ - result.queries is not defined
+
+ - name: postgresql_copy - check trust_input
+ <<: *task_parameters
+ postgresql_copy:
+ <<: *pg_parameters
+ copy_to: '{{ data_file_txt }}'
+ src: '{{ test_table }}'
+ session_role: 'curious.anonymous"; SELECT * FROM information_schema.tables; --'
+ trust_input: false
+ ignore_errors: true
+
+ - assert:
+ that:
+ - result is failed
+ - result.msg is search('is potentially dangerous')
+
+ - name: postgresql_copy - copy test table data to data_file_txt
+ <<: *task_parameters
+ postgresql_copy:
+ <<: *pg_parameters
+ copy_to: '{{ data_file_txt }}'
+ src: '{{ test_table }}'
+ trust_input: false
+
+ - assert:
+ that:
+ - result is changed
+ - result.queries == ["COPY \"{{ test_table }}\" TO '{{ data_file_txt }}'"]
+ - result.src == '{{ test_table }}'
+ - result.dst == '{{ data_file_txt }}'
+
+ # check the prev test
+ - name: postgresql_copy - check data_file_txt exists and not empty
+ <<: *task_parameters
+ shell: 'head -n 1 {{ data_file_txt }}'
+
+ - assert:
+ that:
+ - result.stdout == '1\tfirst'
+
+ # test different options and columns
+ - name: postgresql_copy - copy test table data to data_file_csv with options and columns
+ <<: *task_parameters
+ postgresql_copy:
+ <<: *pg_parameters
+ copy_to: '{{ data_file_csv }}'
+ src: '{{ test_table }}'
+ columns:
+ - id
+ - name
+ options:
+ format: csv
+ trust_input: false
+
+ - assert:
+ that:
+ - result is changed
+ - result.queries == ["COPY \"{{ test_table }}\" (id,name) TO '{{ data_file_csv }}' (format csv)"]
+ - result.src == '{{ test_table }}'
+ - result.dst == '{{ data_file_csv }}'
+
+ # check the prev test
+ - name: postgresql_copy - check data_file_csv exists and not empty
+ <<: *task_parameters
+ shell: 'head -n 1 {{ data_file_csv }}'
+
+ - assert:
+ that:
+ - result.stdout == '1,first'
+
+ - name: postgresql_copy - copy from data_file_csv to test table
+ <<: *task_parameters
+ postgresql_copy:
+ <<: *pg_parameters
+ copy_from: '{{ data_file_csv }}'
+ dst: '{{ test_table }}'
+ columns:
+ - id
+ - name
+ options:
+ format: csv
+ trust_input: false
+
+ - assert:
+ that:
+ - result is changed
+ - result.queries == ["COPY \"{{ test_table }}\" (id,name) FROM '{{ data_file_csv }}' (format csv)"]
+ - result.dst == '{{ test_table }}'
+ - result.src == '{{ data_file_csv }}'
+
+ - name: postgresql_copy - check that there are two rows in test table after the prev step
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: "SELECT * FROM {{ test_table }} WHERE id = '1' AND name = 'first'"
+
+ - assert:
+ that:
+ - result.rowcount == 2
+
+ - name: postgresql_copy - test program option, copy to program
+ <<: *task_parameters
+ postgresql_copy:
+ <<: *pg_parameters
+ src: '{{ test_table }}'
+ copy_to: '/bin/true'
+ program: true
+ columns: id, name
+ options:
+ delimiter: '|'
+ trust_input: false
+ when: ansible_distribution != 'FreeBSD'
+
+ - assert:
+ that:
+ - result is changed
+ - result.queries == ["COPY \"{{ test_table }}\" (id, name) TO PROGRAM '/bin/true' (delimiter '|')"]
+ - result.src == '{{ test_table }}'
+ - result.dst == '/bin/true'
+ when: ansible_distribution != 'FreeBSD'
+
+ - name: postgresql_copy - test program option, copy from program
+ <<: *task_parameters
+ postgresql_copy:
+ <<: *pg_parameters
+ dst: '{{ test_table }}'
+ copy_from: 'echo 1,first'
+ program: true
+ columns: id, name
+ options:
+ delimiter: ','
+ trust_input: false
+
+ - assert:
+ that:
+ - result is changed
+ - result.queries == ["COPY \"{{ test_table }}\" (id, name) FROM PROGRAM 'echo 1,first' (delimiter ',')"]
+ - result.dst == '{{ test_table }}'
+ - result.src == 'echo 1,first'
+ when: ansible_distribution != 'FreeBSD'
+
+ - name: postgresql_copy - check that there are three rows in test table after the prev step
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: "SELECT * FROM {{ test_table }} WHERE id = '1' AND name = 'first'"
+
+ - assert:
+ that:
+ - result.rowcount == 3
+
+ # clean up
+ - name: postgresql_copy - remove test table
+ <<: *task_parameters
+ postgresql_table:
+ <<: *pg_parameters
+ name: '{{ test_table }}'
+ state: absent
+
+ - name: postgresql_copy - remove test data files
+ <<: *task_parameters
+ file:
+ path: '{{ item }}'
+ state: absent
+ with_items:
+ - '{{ data_file_csv }}'
+ - '{{ data_file_txt }}'
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_db/aliases b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_db/aliases
new file mode 100644
index 000000000..2f88eca08
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_db/aliases
@@ -0,0 +1,3 @@
+destructive
+shippable/posix/group1
+postgresql_db
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_db/defaults/main.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_db/defaults/main.yml
new file mode 100644
index 000000000..766feeecc
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_db/defaults/main.yml
@@ -0,0 +1,11 @@
+db_name: 'ansible_db'
+db_user1: 'ansible.db.user1'
+db_user2: 'ansible.db.user2'
+tmp_dir: '/tmp'
+db_session_role1: 'session_role1'
+db_session_role2: 'session_role2'
+
+# To test trust_input parameter and
+# possibility to create a database with dots in its name
+db_name_with_dot: 'db.name'
+suspicious_db_name: '{{ db_name_with_dot }}"; --'
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_db/meta/main.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_db/meta/main.yml
new file mode 100644
index 000000000..4ce5a5837
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_db/meta/main.yml
@@ -0,0 +1,2 @@
+dependencies:
+ - setup_postgresql_db
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_db/tasks/main.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_db/tasks/main.yml
new file mode 100644
index 000000000..dd55c3f98
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_db/tasks/main.yml
@@ -0,0 +1,47 @@
+####################################################################
+# WARNING: These are designed specifically for Ansible tests #
+# and should not be used as examples of how to write Ansible roles #
+####################################################################
+
+- import_tasks: postgresql_db_session_role.yml
+
+# Initial tests of postgresql_db module:
+- import_tasks: postgresql_db_initial.yml
+
+# General tests:
+- import_tasks: postgresql_db_general.yml
+
+# Tests for rename value of state option
+- import_tasks: state_rename.yml
+
+# Dump/restore tests per format:
+- include_tasks: state_dump_restore.yml
+ vars:
+ test_fixture: user
+ file: '{{ loop_item }}'
+ loop:
+ - dbdata.sql
+ - dbdata.sql.gz
+ - dbdata.sql.bz2
+ - dbdata.sql.xz
+ - dbdata.tar
+ - dbdata.tar.gz
+ - dbdata.tar.bz2
+ - dbdata.tar.xz
+ - dbdata.pgc
+ - dbdata.dir
+ - dbdata.dir.gz
+ - dbdata.dir.bz2
+ - dbdata.dir.xz
+ loop_control:
+ loop_var: loop_item
+ when: postgres_version_resp.stdout is version('9.1', '>=')
+
+# Dump/restore tests per other logins:
+- import_tasks: state_dump_restore.yml
+ vars:
+ file: dbdata.tar
+ test_fixture: admin
+
+# Simple test to create and then drop with force
+- import_tasks: manage_database.yml
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_db/tasks/manage_database.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_db/tasks/manage_database.yml
new file mode 100644
index 000000000..42d0f4ee9
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_db/tasks/manage_database.yml
@@ -0,0 +1,9 @@
+- name: Create a simple database mydb
+ postgresql_db:
+ name: mydb
+
+- name: Drop the database with force
+ postgresql_db:
+ name: mydb
+ state: absent
+ force: true
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_db/tasks/postgresql_db_general.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_db/tasks/postgresql_db_general.yml
new file mode 100644
index 000000000..6a178bea1
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_db/tasks/postgresql_db_general.yml
@@ -0,0 +1,152 @@
+- become_user: '{{ pg_user }}'
+ become: true
+ vars:
+ db_tablespace: bar
+ tblspc_location: /ssd
+ db_name: acme
+ block_parameters:
+ become_user: '{{ pg_user }}'
+ become: true
+ task_parameters:
+ register: result
+ pg_parameters:
+ login_user: '{{ pg_user }}'
+ block:
+ - name: postgresql_db - drop dir for test tablespace
+ become: true
+ become_user: root
+ file:
+ path: '{{ tblspc_location }}'
+ state: absent
+ ignore_errors: true
+ - name: postgresql_db - disable selinux
+ become: true
+ become_user: root
+ shell: setenforce 0
+ ignore_errors: true
+ - name: postgresql_db - create dir for test tablespace
+ become: true
+ become_user: root
+ file:
+ path: '{{ tblspc_location }}'
+ state: directory
+ owner: '{{ pg_user }}'
+ group: '{{ pg_user }}'
+ mode: '0700'
+ - name: postgresql_db_ - create a new tablespace
+ postgresql_tablespace:
+ login_user: '{{ pg_user }}'
+ login_db: postgres
+ name: '{{ db_tablespace }}'
+ location: '{{ tblspc_location }}'
+ - register: result
+ name: postgresql_db_tablespace - Create DB with tablespace option in check mode
+ check_mode: true
+ postgresql_db:
+ login_user: '{{ pg_user }}'
+ maintenance_db: postgres
+ name: '{{ db_name }}'
+ tablespace: '{{ db_tablespace }}'
+ - assert:
+ that:
+ - result is changed
+ - register: result
+ name: postgresql_db_tablespace - Check actual DB tablespace, rowcount must be 0 because actually nothing changed
+ postgresql_query:
+ login_user: '{{ pg_user }}'
+ login_db: postgres
+ query: 'SELECT 1 FROM pg_database AS d JOIN pg_tablespace AS t ON d.dattablespace = t.oid WHERE d.datname = ''{{ db_name }}'' AND t.spcname = ''{{ db_tablespace }}''
+
+ '
+ - assert:
+ that:
+ - result.rowcount == 0
+ - register: result
+ name: postgresql_db_tablespace - Create DB with tablespace option
+ postgresql_db:
+ login_user: '{{ pg_user }}'
+ maintenance_db: postgres
+ name: '{{ db_name }}'
+ tablespace: '{{ db_tablespace }}'
+ - assert:
+ that:
+ - result is changed
+ - result.executed_commands == ['CREATE DATABASE "{{ db_name }}" TABLESPACE "{{ db_tablespace }}"']
+ - register: result
+ name: postgresql_db_tablespace - Check actual DB tablespace, rowcount must be 1
+ postgresql_query:
+ login_user: '{{ pg_user }}'
+ login_db: postgres
+ query: 'SELECT 1 FROM pg_database AS d JOIN pg_tablespace AS t ON d.dattablespace = t.oid WHERE d.datname = ''{{ db_name }}'' AND t.spcname = ''{{ db_tablespace }}''
+
+ '
+ - assert:
+ that:
+ - result.rowcount == 1
+ - register: result
+ name: postgresql_db_tablespace - The same DB with tablespace option again
+ postgresql_db:
+ login_user: '{{ pg_user }}'
+ maintenance_db: postgres
+ name: '{{ db_name }}'
+ tablespace: '{{ db_tablespace }}'
+ - assert:
+ that:
+ - result is not changed
+ - register: result
+ name: postgresql_db_tablespace - Change tablespace in check_mode
+ check_mode: true
+ postgresql_db:
+ login_user: '{{ pg_user }}'
+ maintenance_db: postgres
+ name: '{{ db_name }}'
+ tablespace: pg_default
+ - assert:
+ that:
+ - result is changed
+ - register: result
+ name: postgresql_db_tablespace - Check actual DB tablespace, rowcount must be 1 because actually nothing changed
+ postgresql_query:
+ login_user: '{{ pg_user }}'
+ login_db: postgres
+ query: 'SELECT 1 FROM pg_database AS d JOIN pg_tablespace AS t ON d.dattablespace = t.oid WHERE d.datname = ''{{ db_name }}'' AND t.spcname = ''{{ db_tablespace }}''
+
+ '
+ - assert:
+ that:
+ - result.rowcount == 1
+ - register: result
+ name: postgresql_db_tablespace - Change tablespace in actual mode
+ postgresql_db:
+ login_user: '{{ pg_user }}'
+ maintenance_db: postgres
+ name: '{{ db_name }}'
+ tablespace: pg_default
+ - assert:
+ that:
+ - result is changed
+ - register: result
+ name: postgresql_db_tablespace - Check actual DB tablespace, rowcount must be 1
+ postgresql_query:
+ login_user: '{{ pg_user }}'
+ login_db: postgres
+ query: 'SELECT 1 FROM pg_database AS d JOIN pg_tablespace AS t ON d.dattablespace = t.oid WHERE d.datname = ''{{ db_name }}'' AND t.spcname = ''pg_default''
+
+ '
+ - assert:
+ that:
+ - result.rowcount == 1
+ - register: result
+ name: postgresql_db_tablespace - Drop test DB
+ postgresql_db:
+ login_user: '{{ pg_user }}'
+ maintenance_db: postgres
+ name: '{{ db_name }}'
+ state: absent
+ - register: result
+ name: postgresql_db_tablespace - Remove tablespace
+ postgresql_tablespace:
+ login_user: '{{ pg_user }}'
+ login_db: postgres
+ name: '{{ db_tablespace }}'
+ state: absent
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_db/tasks/postgresql_db_initial.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_db/tasks/postgresql_db_initial.yml
new file mode 100644
index 000000000..472524a23
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_db/tasks/postgresql_db_initial.yml
@@ -0,0 +1,366 @@
+#
+# Create and destroy db
+#
+- name: Create DB
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_db:
+ state: present
+ name: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ register: result
+
+- name: assert that module reports the db was created
+ assert:
+ that:
+ - result is changed
+ - result.db == "{{ db_name }}"
+ - result.executed_commands == ['CREATE DATABASE "{{ db_name }}"']
+
+- name: Check that database created
+ become_user: "{{ pg_user }}"
+ become: true
+ shell: echo "select datname from pg_database where datname = '{{ db_name }}';" | psql -d postgres
+ register: result
+
+- assert:
+ that:
+ - "result.stdout_lines[-1] == '(1 row)'"
+
+- name: Run create on an already created db
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_db:
+ state: present
+ name: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ register: result
+
+- name: assert that module reports the db was unchanged
+ assert:
+ that:
+ - result is not changed
+
+- name: Destroy DB
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_db:
+ state: absent
+ name: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ register: result
+
+- name: assert that module reports the db was changed
+ assert:
+ that:
+ - result is changed
+ - result.executed_commands == ['DROP DATABASE "{{ db_name }}"']
+
+- name: Check that database was destroyed
+ become_user: "{{ pg_user }}"
+ become: true
+ shell: echo "select datname from pg_database where datname = '{{ db_name }}';" | psql -d postgres
+ register: result
+
+- assert:
+ that:
+ - "result.stdout_lines[-1] == '(0 rows)'"
+
+- name: Destroy DB
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_db:
+ state: absent
+ name: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ register: result
+
+- name: assert that removing an already removed db makes no change
+ assert:
+ that:
+ - result is not changed
+
+
+# This corner case works to add but not to drop. This is sufficiently crazy
+# that I'm not going to attempt to fix it unless someone lets me know that they
+# need the functionality
+#
+# - postgresql_db:
+# state: 'present'
+# name: '"silly.""name"'
+# - shell: echo "select datname from pg_database where datname = 'silly.""name';" | psql
+# register: result
+#
+# - assert:
+# that: "result.stdout_lines[-1] == '(1 row)'"
+# - postgresql_db:
+# state: absent
+# name: '"silly.""name"'
+# - shell: echo "select datname from pg_database where datname = 'silly.""name';" | psql
+# register: result
+#
+# - assert:
+# that: "result.stdout_lines[-1] == '(0 rows)'"
+
+#
+# Test conn_limit, encoding, collate, ctype, template options
+#
+- name: Create a DB with conn_limit, encoding, collate, ctype, and template options
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_db:
+ name: '{{ db_name }}'
+ state: 'present'
+ conn_limit: '100'
+ encoding: 'LATIN1'
+ lc_collate: 'pt_BR{{ locale_latin_suffix }}'
+ lc_ctype: 'es_ES{{ locale_latin_suffix }}'
+ template: 'template0'
+ login_user: "{{ pg_user }}"
+ register: result
+
+- assert:
+ that:
+ - result is changed
+ - result.executed_commands == ["CREATE DATABASE \"{{ db_name }}\" TEMPLATE \"template0\" ENCODING 'LATIN1' LC_COLLATE 'pt_BR{{ locale_latin_suffix }}' LC_CTYPE 'es_ES{{ locale_latin_suffix }}' CONNECTION LIMIT 100"] or result.executed_commands == ["CREATE DATABASE \"{{ db_name }}\" TEMPLATE \"template0\" ENCODING E'LATIN1' LC_COLLATE E'pt_BR{{ locale_latin_suffix }}' LC_CTYPE E'es_ES{{ locale_latin_suffix }}' CONNECTION LIMIT 100"]
+
+- name: Check that the DB has all of our options
+ become_user: "{{ pg_user }}"
+ become: true
+ shell: echo "select datname, datconnlimit, pg_encoding_to_char(encoding), datcollate, datctype from pg_database where datname = '{{ db_name }}';" | psql -d postgres
+ register: result
+
+- assert:
+ that:
+ - "result.stdout_lines[-1] == '(1 row)'"
+ - "'LATIN1' in result.stdout_lines[-2]"
+ - "'pt_BR' in result.stdout_lines[-2]"
+ - "'es_ES' in result.stdout_lines[-2]"
+ - "'UTF8' not in result.stdout_lines[-2]"
+ - "'en_US' not in result.stdout_lines[-2]"
+ - "'100' in result.stdout_lines[-2]"
+
+- name: Check that running db creation with options a second time does nothing
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_db:
+ name: '{{ db_name }}'
+ state: 'present'
+ conn_limit: '100'
+ encoding: 'LATIN1'
+ lc_collate: 'pt_BR{{ locale_latin_suffix }}'
+ lc_ctype: 'es_ES{{ locale_latin_suffix }}'
+ template: 'template0'
+ login_user: "{{ pg_user }}"
+ register: result
+
+- assert:
+ that:
+ - result is not changed
+
+
+- name: Check that attempting to change encoding returns an error
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_db:
+ name: '{{ db_name }}'
+ state: 'present'
+ encoding: 'UTF8'
+ lc_collate: 'pt_BR{{ locale_utf8_suffix }}'
+ lc_ctype: 'es_ES{{ locale_utf8_suffix }}'
+ template: 'template0'
+ login_user: "{{ pg_user }}"
+ register: result
+ ignore_errors: true
+
+- assert:
+ that:
+ - result is failed
+
+- name: Check that changing the conn_limit actually works
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_db:
+ name: '{{ db_name }}'
+ state: 'present'
+ conn_limit: '200'
+ encoding: 'LATIN1'
+ lc_collate: 'pt_BR{{ locale_latin_suffix }}'
+ lc_ctype: 'es_ES{{ locale_latin_suffix }}'
+ template: 'template0'
+ login_user: "{{ pg_user }}"
+ register: result
+
+- assert:
+ that:
+ - result is changed
+ - result.executed_commands == ['ALTER DATABASE "{{ db_name }}" CONNECTION LIMIT 200']
+
+- name: Check that conn_limit has actually been set / updated to 200
+ become_user: "{{ pg_user }}"
+ become: true
+ shell: echo "SELECT datconnlimit AS conn_limit FROM pg_database WHERE datname = '{{ db_name }}';" | psql -d postgres
+ register: result
+
+- assert:
+ that:
+ - "result.stdout_lines[-1] == '(1 row)'"
+ - "'200' == '{{ result.stdout_lines[-2] | trim }}'"
+
+- name: Cleanup test DB
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_db:
+ name: '{{ db_name }}'
+ state: 'absent'
+ login_user: "{{ pg_user }}"
+
+- shell: echo "select datname, pg_encoding_to_char(encoding), datcollate, datctype from pg_database where datname = '{{ db_name }}';" | psql -d postgres
+ become_user: "{{ pg_user }}"
+ become: true
+ register: result
+
+- assert:
+ that:
+ - "result.stdout_lines[-1] == '(0 rows)'"
+
+#
+# Test db ownership
+#
+- name: Create an unprivileged user to own a DB
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_user:
+ name: "{{ item }}"
+ encrypted: 'true'
+ password: "md55c8ccfd9d6711fc69a7eae647fc54f51"
+ login_user: "{{ pg_user }}"
+ db: postgres
+ loop:
+ - "{{ db_user1 }}"
+ - "{{ db_user2 }}"
+
+- name: Create db with user ownership
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_db:
+ name: "{{ db_name }}"
+ state: "present"
+ owner: "{{ db_user1 }}"
+ login_user: "{{ pg_user }}"
+ register: result
+
+- assert:
+ that:
+ - result is changed
+ - result.executed_commands == ['CREATE DATABASE "{{ db_name }}" OWNER "{{ db_user1 }}"']
+
+- name: Check that the user owns the newly created DB
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ query: >
+ SELECT 1 FROM pg_catalog.pg_database
+ WHERE datname = '{{ db_name }}'
+ AND pg_catalog.pg_get_userbyid(datdba) = '{{ db_user1 }}'
+ register: result
+
+- assert:
+ that:
+ - result.rowcount == 1
+
+- name: Change the owner on an existing db, username with dots
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_db:
+ name: "{{ db_name }}"
+ state: "present"
+ owner: "{{ db_user2 }}"
+ login_user: "{{ pg_user }}"
+ register: result
+
+- assert:
+ that:
+ - result is changed
+ - result.executed_commands == ['ALTER DATABASE "{{ db_name }}" OWNER TO "{{ db_user2 }}"']
+
+- name: Check the previous step
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ login_user: "{{ pg_user }}"
+ db: postgres
+ query: >
+ SELECT 1 FROM pg_catalog.pg_database
+ WHERE datname = '{{ db_name }}'
+ AND pg_catalog.pg_get_userbyid(datdba) = '{{ db_user2 }}'
+ register: result
+
+- assert:
+ that:
+ - result.rowcount == 1
+
+- name: Change the owner on an existing db
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_db:
+ name: "{{ db_name }}"
+ state: "present"
+ owner: "{{ pg_user }}"
+ login_user: "{{ pg_user }}"
+ register: result
+
+- name: assert that ansible says it changed the db
+ assert:
+ that:
+ - result is changed
+
+- name: Check that the user owns the newly created DB
+ become_user: "{{ pg_user }}"
+ become: true
+ shell: echo "select pg_catalog.pg_get_userbyid(datdba) from pg_catalog.pg_database where datname = '{{ db_name }}';" | psql -d postgres
+ register: result
+
+- assert:
+ that:
+ - "result.stdout_lines[-1] == '(1 row)'"
+ - "'{{ pg_user }}' == '{{ result.stdout_lines[-2] | trim }}'"
+
+- name: Cleanup db
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_db:
+ name: "{{ db_name }}"
+ state: "absent"
+ login_user: "{{ pg_user }}"
+
+- name: Check that database was destroyed
+ become_user: "{{ pg_user }}"
+ become: true
+ shell: echo "select datname from pg_database where datname = '{{ db_name }}';" | psql -d postgres
+ register: result
+
+- assert:
+ that:
+ - "result.stdout_lines[-1] == '(0 rows)'"
+
+- name: Cleanup test user
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_user:
+ name: "{{ db_user1 }}"
+ state: 'absent'
+ login_user: "{{ pg_user }}"
+ db: postgres
+
+- name: Check that they were removed
+ become_user: "{{ pg_user }}"
+ become: true
+ shell: echo "select * from pg_user where usename='{{ db_user1 }}';" | psql -d postgres
+ register: result
+
+- assert:
+ that:
+ - "result.stdout_lines[-1] == '(0 rows)'"
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_db/tasks/postgresql_db_session_role.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_db/tasks/postgresql_db_session_role.yml
new file mode 100644
index 000000000..74f9e3ff3
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_db/tasks/postgresql_db_session_role.yml
@@ -0,0 +1,80 @@
+- name: Check that becoming an non-existing user throws an error
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_db:
+ state: present
+ name: must_fail
+ login_user: "{{ pg_user }}"
+ session_role: "{{ db_session_role1 }}"
+ register: result
+ ignore_errors: true
+
+- assert:
+ that:
+ - result is failed
+
+- name: Create a high privileged user
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_user:
+ name: "{{ db_session_role1 }}"
+ state: "present"
+ password: "password"
+ role_attr_flags: "CREATEDB,LOGIN,CREATEROLE"
+ login_user: "{{ pg_user }}"
+ db: postgres
+
+- name: Create a low privileged user using the newly created user
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_user:
+ name: "{{ db_session_role2 }}"
+ state: "present"
+ password: "password"
+ role_attr_flags: "LOGIN"
+ login_user: "{{ pg_user }}"
+ session_role: "{{ db_session_role1 }}"
+ db: postgres
+
+- name: Create DB as session_role
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_db:
+ state: present
+ name: "{{ db_session_role1 }}"
+ login_user: "{{ pg_user }}"
+ session_role: "{{ db_session_role1 }}"
+ register: result
+
+- name: Check that database created and is owned by correct user
+ become_user: "{{ pg_user }}"
+ become: true
+ shell: echo "select rolname from pg_database join pg_roles on datdba = pg_roles.oid where datname = '{{ db_session_role1 }}';" | psql -AtXq postgres
+ register: result
+
+- assert:
+ that:
+ - "result.stdout_lines[-1] == '{{ db_session_role1 }}'"
+
+- name: Fail when creating database as low privileged user
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_db:
+ state: present
+ name: "{{ db_session_role2 }}"
+ login_user: "{{ pg_user }}"
+ session_role: "{{ db_session_role2 }}"
+ register: result
+ ignore_errors: true
+
+- assert:
+ that:
+ - result is failed
+
+- name: Drop test db
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_db:
+ state: absent
+ name: "{{ db_session_role1 }}"
+ login_user: "{{ pg_user }}"
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_db/tasks/state_dump_restore.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_db/tasks/state_dump_restore.yml
new file mode 100644
index 000000000..dbb3bf7a1
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_db/tasks/state_dump_restore.yml
@@ -0,0 +1,235 @@
+# test code for state dump and restore for postgresql_db module
+# copied from mysql_db/tasks/state_dump_import.yml
+# (c) 2014, Wayne Rosario <wrosario@ansible.com>
+
+# 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 <http://www.gnu.org/licenses/>.
+
+# ============================================================
+
+- name: Create a test user
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_user:
+ name: "{{ db_user1 }}"
+ state: "present"
+ encrypted: 'true'
+ password: "password"
+ role_attr_flags: "CREATEDB,LOGIN,CREATEROLE"
+ login_user: "{{ pg_user }}"
+ db: postgres
+
+- set_fact: db_file_name="{{tmp_dir}}/{{file}}"
+
+- set_fact:
+ admin_str: "psql -U {{ pg_user }}"
+
+- set_fact:
+ user_str: "env PGPASSWORD=password psql -h localhost -U {{ db_user1 }} {{ db_name }}"
+ when: test_fixture == "user"
+ # "-n public" is required to work around pg_restore issues with plpgsql
+
+- set_fact:
+ user_str: "psql -U {{ pg_user }} {{ db_name }}"
+ when: test_fixture == "admin"
+
+
+
+- set_fact:
+ sql_create: "create table employee(id int, name varchar(100));"
+ sql_insert: "insert into employee values (47,'Joe Smith');"
+ sql_select: "select * from employee;"
+
+- name: state dump/restore - create database
+ postgresql_db:
+ state: present
+ name: "{{ db_name }}"
+ owner: "{{ db_user1 }}"
+ login_user: "{{ pg_user }}"
+
+- name: state dump/restore - create table employee
+ command: '{{ user_str }} -c "{{ sql_create }}"'
+
+- name: state dump/restore - insert data into table employee
+ command: '{{ user_str }} -c "{{ sql_insert }}"'
+
+- name: state dump/restore - file name should not exist
+ file: name={{ db_file_name }} state=absent
+
+- name: test state=dump to backup the database (expect changed=true)
+ postgresql_db:
+ name: "{{ db_name }}"
+ target: "{{ db_file_name }}"
+ owner: "{{ db_user1 }}"
+ login_user: '{{(test_fixture == "user")|ternary(db_user1, pg_user)}}'
+ target_opts: '{{(test_fixture == "user")|ternary("-n public", omit)}}'
+ login_host: '{{(test_fixture == "user")|ternary("localhost", omit)}}'
+ login_password: '{{(test_fixture == "user")|ternary("password", omit)}}'
+ state: dump
+ dump_extra_args: --exclude-table=fake
+ register: result
+ become_user: "{{ pg_user }}"
+ become: true
+
+- name: assert output message backup the database
+ assert:
+ that:
+ - result is changed
+ - result.executed_commands[0] is search("--exclude-table=fake")
+
+- name: assert database was backed up successfully
+ command: file {{ db_file_name }}
+ register: result
+
+- name: state dump/restore - remove database for restore
+ postgresql_db:
+ name: "{{ db_name }}"
+ target: "{{ db_file_name }}"
+ owner: "{{ db_user1 }}"
+ login_user: '{{(test_fixture == "user")|ternary(db_user1, pg_user)}}'
+ target_opts: '{{(test_fixture == "user")|ternary("-n public", omit)}}'
+ login_host: '{{(test_fixture == "user")|ternary("localhost", omit)}}'
+ login_password: '{{(test_fixture == "user")|ternary("password", omit)}}'
+ state: absent
+
+- name: state dump/restore - re-create database
+ postgresql_db:
+ state: present
+ name: "{{ db_name }}"
+ owner: "{{ db_user1 }}"
+ login_user: "{{ pg_user }}"
+
+- name: test state=restore to restore the database (expect changed=true)
+ postgresql_db:
+ name: "{{ db_name }}"
+ target: "{{ db_file_name }}"
+ owner: "{{ db_user1 }}"
+ login_user: '{{(test_fixture == "user")|ternary(db_user1, pg_user)}}'
+ target_opts: '{{(test_fixture == "user")|ternary("-n public", omit)}}'
+ login_host: '{{(test_fixture == "user")|ternary("localhost", omit)}}'
+ login_password: '{{(test_fixture == "user")|ternary("password", omit)}}'
+ state: restore
+ register: result
+ become_user: "{{ pg_user }}"
+ become: true
+
+- name: assert output message restore the database
+ assert:
+ that:
+ - result is changed
+
+- name: select data from table employee
+ command: '{{ user_str }} -c "{{ sql_select }}"'
+ register: result
+
+- name: assert data in database is from the restore database
+ assert:
+ that:
+ - "'47' in result.stdout"
+ - "'Joe Smith' in result.stdout"
+
+############################
+# 1. Test trust_input parameter
+# 2. Test db name containing dots
+
+- name: state dump/restore - create database, trust_input no
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_db:
+ state: present
+ name: "{{ suspicious_db_name }}"
+ owner: "{{ db_user1 }}"
+ login_user: "{{ pg_user }}"
+ trust_input: false
+ register: result
+ ignore_errors: true
+
+- assert:
+ that:
+ - result is failed
+ - result.msg == 'Passed input \'{{ suspicious_db_name }}\' is potentially dangerous'
+
+- name: state dump/restore - create database, trust_input true explicitly
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_db:
+ state: present
+ name: "{{ db_name_with_dot }}"
+ owner: "{{ db_user1 }}"
+ login_user: "{{ pg_user }}"
+ trust_input: true
+ register: result
+
+- assert:
+ that:
+ - result is changed
+
+- name: test state=restore to restore the database (expect changed=true)
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_db:
+ name: "{{ db_name_with_dot }}"
+ target: "{{ db_file_name }}"
+ owner: "{{ db_user1 }}"
+ login_user: '{{(test_fixture == "user")|ternary(db_user1, pg_user)}}'
+ target_opts: '{{(test_fixture == "user")|ternary("-n public", omit)}}'
+ login_host: '{{(test_fixture == "user")|ternary("localhost", omit)}}'
+ login_password: '{{(test_fixture == "user")|ternary("password", omit)}}'
+ state: restore
+ register: result
+
+- name: assert output message restore the database
+ assert:
+ that:
+ - result is changed
+
+- name: state dump/restore - remove databases
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_db:
+ state: absent
+ name: "{{ db_name_with_dot }}"
+ owner: "{{ db_user1 }}"
+ login_user: "{{ pg_user }}"
+ trust_input: true
+ register: result
+
+- assert:
+ that:
+ - result is changed
+
+# Clean up
+- name: state dump/restore - remove database name
+ postgresql_db:
+ name: "{{ db_name }}"
+ target: "{{ db_file_name }}"
+ owner: "{{ db_user1 }}"
+ login_user: '{{(test_fixture == "user")|ternary(db_user1, pg_user)}}'
+ target_opts: '{{(test_fixture == "user")|ternary("-n public", omit)}}'
+ login_host: '{{(test_fixture == "user")|ternary("localhost", omit)}}'
+ login_password: '{{(test_fixture == "user")|ternary("password", omit)}}'
+ state: absent
+
+- name: remove file name
+ file: name={{ db_file_name }} state=absent
+
+- name: Remove the test user
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_user:
+ name: "{{ db_user1 }}"
+ state: "absent"
+ login_user: "{{ pg_user }}"
+ db: postgres
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_db/tasks/state_rename.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_db/tasks/state_rename.yml
new file mode 100644
index 000000000..dc87b76fb
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_db/tasks/state_rename.yml
@@ -0,0 +1,261 @@
+# 0. Check necessary options.
+# 1. When both databases do not exists, it must fail.
+# 2. When both databases exist, it must fail.
+# 3. When the source database exists and the target does not, rename it.
+# 4. When the source database doesn't exist and the target does, do nothing.
+# 5. Check mode
+
+- become_user: '{{ pg_user }}'
+ become: true
+ vars:
+ db_source_name: acme
+ db_target_name: acme1
+
+ task_parameters: &task_parameters
+ register: result
+
+ pg_parameters: &pg_parameters
+ login_user: '{{ pg_user }}'
+
+ block:
+ # 0. Check necessary options.
+ - name: Miss target option, must fail
+ <<: *task_parameters
+ postgresql_db:
+ <<: *pg_parameters
+ name: '{{ db_source_name }}'
+ state: rename
+ ignore_errors: true
+
+ - assert:
+ that:
+ - result is failed
+ - result.msg == 'The "target" option must be defined when the "rename" option is used.'
+
+ - name: Target and name options are the same, must fail
+ <<: *task_parameters
+ postgresql_db:
+ <<: *pg_parameters
+ name: '{{ db_source_name }}'
+ state: rename
+ target: '{{ db_source_name }}'
+ ignore_errors: true
+
+ - assert:
+ that:
+ - result is failed
+ - result.msg == 'The "name/db" option and the "target" option cannot be the same.'
+
+ - name: Maintenance_db and name options are the same, must fail
+ <<: *task_parameters
+ postgresql_db:
+ <<: *pg_parameters
+ name: postgres
+ state: rename
+ target: '{{ db_source_name }}'
+ ignore_errors: true
+
+ - assert:
+ that:
+ - result is failed
+ - result.msg == 'The "maintenance_db" option and the "name/db" option cannot be the same.'
+
+ # 1. When both databases do not exists, it must fail.
+ - name: Try to rename when both do not exist, must fail
+ <<: *task_parameters
+ postgresql_db:
+ <<: *pg_parameters
+ name: '{{ db_source_name }}'
+ state: rename
+ target: '{{ db_target_name}}'
+ ignore_errors: true
+
+ - assert:
+ that:
+ - result is failed
+ - result.msg == 'The source and the target databases do not exist.'
+
+ - name: Try to rename when both do not exist, must fail, check_mode
+ <<: *task_parameters
+ postgresql_db:
+ <<: *pg_parameters
+ name: '{{ db_source_name }}'
+ state: rename
+ target: '{{ db_target_name}}'
+ ignore_errors: true
+ check_mode: true
+
+ - assert:
+ that:
+ - result is failed
+ - result.msg == 'The source and the target databases do not exist.'
+
+ # 2. When both databases exist, it must fail.
+ - name: Create test DBs
+ <<: *task_parameters
+ postgresql_db:
+ <<: *pg_parameters
+ name: '{{ item }}'
+ state: present
+ loop:
+ - '{{ db_source_name }}'
+ - '{{ db_target_name }}'
+
+ - name: Try to rename when both exist, must fail
+ <<: *task_parameters
+ postgresql_db:
+ <<: *pg_parameters
+ name: '{{ db_source_name }}'
+ state: rename
+ target: '{{ db_target_name}}'
+ ignore_errors: true
+
+ - assert:
+ that:
+ - result is failed
+ - result.msg == 'Both the source and the target databases exist.'
+
+ - name: Try to rename when both exist, must fail
+ <<: *task_parameters
+ postgresql_db:
+ <<: *pg_parameters
+ name: '{{ db_source_name }}'
+ state: rename
+ target: '{{ db_target_name}}'
+ ignore_errors: true
+
+ - assert:
+ that:
+ - result is failed
+ - result.msg == 'Both the source and the target databases exist.'
+
+ # 3. When the source database exists and the target does not, rename it.
+ # 4. When the source database doesn't exist and the target does, do nothing.
+ - name: Drop the target DB
+ <<: *task_parameters
+ postgresql_db:
+ <<: *pg_parameters
+ name: '{{ db_target_name }}'
+ state: absent
+
+ - name: Rename DB in check mode
+ <<: *task_parameters
+ postgresql_db:
+ <<: *pg_parameters
+ name: '{{ db_source_name }}'
+ state: rename
+ target: '{{ db_target_name }}'
+ check_mode: true
+
+ - assert:
+ that:
+ - result is succeeded
+ - result.executed_commands == []
+
+ - name: Check that nothing really happened
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: "SELECT * FROM pg_database WHERE datname = '{{ db_source_name }}'"
+
+ - assert:
+ that:
+ - result.rowcount == 1
+
+ - name: Check that nothing really happened
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: "SELECT * FROM pg_database WHERE datname = '{{ db_target_name }}'"
+
+ - assert:
+ that:
+ - result.rowcount == 0
+
+ - name: Rename DB in actual mode
+ <<: *task_parameters
+ postgresql_db:
+ <<: *pg_parameters
+ name: '{{ db_source_name }}'
+ state: rename
+ target: '{{ db_target_name}}'
+
+ - assert:
+ that:
+ - result is changed
+ - result.executed_commands == ['ALTER DATABASE "{{ db_source_name }}" RENAME TO "{{ db_target_name}}"']
+
+ - name: Check the changes have been made
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: "SELECT * FROM pg_database WHERE datname = '{{ db_source_name }}'"
+
+ - assert:
+ that:
+ - result.rowcount == 0
+
+ - name: Check the changes have been made
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: "SELECT * FROM pg_database WHERE datname = '{{ db_target_name }}'"
+
+ - assert:
+ that:
+ - result.rowcount == 1
+
+ - name: Try to rename same DBs again in check mode
+ <<: *task_parameters
+ postgresql_db:
+ <<: *pg_parameters
+ name: '{{ db_source_name }}'
+ state: rename
+ target: '{{ db_target_name}}'
+ check_mode: true
+
+ - assert:
+ that:
+ - result is not changed
+ - result.executed_commands == []
+
+ - name: Try to rename same DBs again in actual mode
+ <<: *task_parameters
+ postgresql_db:
+ <<: *pg_parameters
+ name: '{{ db_source_name }}'
+ state: rename
+ target: '{{ db_target_name}}'
+
+ - assert:
+ that:
+ - result is not changed
+ - result.executed_commands == []
+
+ - name: Check the state is the same
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: "SELECT * FROM pg_database WHERE datname = '{{ db_source_name }}'"
+
+ - assert:
+ that:
+ - result.rowcount == 0
+
+ - name: Check the state is the same
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: "SELECT * FROM pg_database WHERE datname = '{{ db_target_name }}'"
+
+ - assert:
+ that:
+ - result.rowcount == 1
+
+ # Clean up
+ - name: Remove test DB
+ <<: *task_parameters
+ postgresql_db:
+ <<: *pg_parameters
+ name: '{{ db_target_name }}'
+ state: absent
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_ext/aliases b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_ext/aliases
new file mode 100644
index 000000000..142e8aa07
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_ext/aliases
@@ -0,0 +1,3 @@
+destructive
+shippable/posix/group1
+skip/freebsd
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_ext/defaults/main.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_ext/defaults/main.yml
new file mode 100644
index 000000000..05bac61d4
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_ext/defaults/main.yml
@@ -0,0 +1,2 @@
+db_session_role1: 'session_role1'
+db_session_role2: 'session_role2'
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_ext/meta/main.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_ext/meta/main.yml
new file mode 100644
index 000000000..0ec7d2fcc
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_ext/meta/main.yml
@@ -0,0 +1,3 @@
+dependencies:
+ - setup_pkg_mgr
+ - setup_postgresql_db
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_ext/tasks/main.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_ext/tasks/main.yml
new file mode 100644
index 000000000..1fa365be3
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_ext/tasks/main.yml
@@ -0,0 +1,26 @@
+####################################################################
+# WARNING: These are designed specifically for Ansible tests #
+# and should not be used as examples of how to write Ansible roles #
+####################################################################
+
+- import_tasks: postgresql_ext_session_role.yml
+
+# Initial CI tests of postgresql_ext module.
+# pg_extension system view is available from PG 9.1.
+# The tests are restricted by Fedora because there will be errors related with
+# attempts to change the environment during postgis installation or
+# missing postgis package in repositories.
+# Anyway, these tests completely depend on Postgres version,
+# not specific distributions.
+- import_tasks: postgresql_ext_initial.yml
+ when:
+ - postgres_version_resp.stdout is version('9.1', '>=')
+ - ansible_distribution == 'Fedora'
+
+# CI tests of "version" option.
+# It uses a mock extension, see test/integration/targets/setup_postgresql_db/.
+# TODO: change postgresql_ext_initial.yml to use the mock extension too.
+- import_tasks: postgresql_ext_version_opt.yml
+ when:
+ - ansible_distribution == 'Ubuntu'
+ - postgres_version_resp.stdout is version('9.1', '>=')
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_ext/tasks/postgresql_ext_initial.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_ext/tasks/postgresql_ext_initial.yml
new file mode 100644
index 000000000..3e3eeda83
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_ext/tasks/postgresql_ext_initial.yml
@@ -0,0 +1,208 @@
+---
+- name: postgresql_ext - install postgis on Linux
+ package: name=postgis state=present
+ when: ansible_os_family != "Windows"
+
+- name: postgresql_ext - create schema schema1
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_schema:
+ database: postgres
+ name: schema1
+ state: present
+
+- name: postgresql_ext - drop extension if exists
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ db: postgres
+ query: DROP EXTENSION IF EXISTS postgis
+ ignore_errors: true
+
+- name: postgresql_ext - create extension postgis in check_mode
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_ext:
+ login_db: postgres
+ login_port: 5432
+ name: postgis
+ check_mode: true
+ ignore_errors: true
+ register: result
+
+- assert:
+ that:
+ - result is changed
+ - result.queries == []
+
+- name: postgresql_ext - check that extension doesn't exist after the previous step
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ db: postgres
+ query: SELECT extname FROM pg_extension WHERE extname='postgis'
+ ignore_errors: true
+ register: result
+
+- assert:
+ that:
+ - result.rowcount == 0
+
+- name: postgresql_ext - create extension postgis
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_ext:
+ login_db: postgres
+ login_port: 5432
+ name: postgis
+ ignore_errors: true
+ register: result
+
+- assert:
+ that:
+ - result is changed
+ - result.queries == ['CREATE EXTENSION "postgis"']
+
+- name: postgresql_ext - check that extension exists after the previous step
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ db: postgres
+ query: SELECT extname FROM pg_extension WHERE extname='postgis'
+ ignore_errors: true
+ register: result
+
+- assert:
+ that:
+ - result.rowcount == 1
+
+- name: postgresql_ext - drop extension postgis
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_ext:
+ db: postgres
+ name: postgis
+ state: absent
+ ignore_errors: true
+ register: result
+
+- assert:
+ that:
+ - result is changed
+ - result.queries == ['DROP EXTENSION "postgis"']
+
+- name: postgresql_ext - check that extension doesn't exist after the previous step
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ db: postgres
+ query: SELECT extname FROM pg_extension WHERE extname='postgis'
+ ignore_errors: true
+ register: result
+
+- assert:
+ that:
+ - result.rowcount == 0
+
+- name: postgresql_ext - create extension postgis
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_ext:
+ db: postgres
+ name: postgis
+ schema: schema1
+ ignore_errors: true
+ register: result
+
+- assert:
+ that:
+ - result is changed
+ - result.queries == ['CREATE EXTENSION "postgis" WITH SCHEMA "schema1"']
+
+- name: postgresql_ext - check that extension exists after the previous step
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ db: postgres
+ query: "SELECT extname FROM pg_extension AS e LEFT JOIN pg_catalog.pg_namespace AS n \nON n.oid = e.extnamespace WHERE e.extname='postgis' AND n.nspname='schema1'\n"
+ ignore_errors: true
+ register: result
+
+- assert:
+ that:
+ - result.rowcount == 1
+
+- name: postgresql_ext - drop extension postgis cascade
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_ext:
+ db: postgres
+ name: postgis
+ state: absent
+ cascade: true
+ ignore_errors: true
+ register: result
+
+- assert:
+ that:
+ - result is changed
+ - result.queries == ['DROP EXTENSION "postgis" CASCADE']
+
+- name: postgresql_ext - check that extension doesn't exist after the previous step
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ db: postgres
+ query: SELECT extname FROM pg_extension WHERE extname='postgis'
+ ignore_errors: true
+ register: result
+
+- assert:
+ that:
+ - result.rowcount == 0
+
+- name: postgresql_ext - create extension postgis cascade
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_ext:
+ db: postgres
+ name: postgis
+ cascade: true
+ ignore_errors: true
+ register: result
+ when: postgres_version_resp.stdout is version('9.6', '<=')
+
+- assert:
+ that:
+ - result is changed
+ - result.queries == ['CREATE EXTENSION "postgis" CASCADE"']
+ when: postgres_version_resp.stdout is version('9.6', '<=')
+
+- name: postgresql_ext - check that extension exists after the previous step
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ db: postgres
+ query: SELECT extname FROM pg_extension WHERE extname='postgis'
+ ignore_errors: true
+ register: result
+ when: postgres_version_resp.stdout is version('9.6', '<=')
+
+- assert:
+ that:
+ - result.rowcount == 1
+ when: postgres_version_resp.stdout is version('9.6', '<=')
+
+- name: postgresql_ext - check that using a dangerous name fails
+ postgresql_ext:
+ db: postgres
+ name: postgis
+ session_role: 'curious.anonymous"; SELECT * FROM information_schema.tables; --'
+ trust_input: false
+ ignore_errors: true
+ register: result
+
+- assert:
+ that:
+ - result is failed
+ - result.msg is search('is potentially dangerous')
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_ext/tasks/postgresql_ext_session_role.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_ext/tasks/postgresql_ext_session_role.yml
new file mode 100644
index 000000000..29173fd0b
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_ext/tasks/postgresql_ext_session_role.yml
@@ -0,0 +1,114 @@
+- name: Create a high privileged user
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_user:
+ name: "{{ db_session_role1 }}"
+ state: "present"
+ password: "password"
+ role_attr_flags: "CREATEDB,LOGIN,CREATEROLE"
+ login_user: "{{ pg_user }}"
+ db: postgres
+
+- name: Create DB as session_role
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_db:
+ state: present
+ name: "{{ db_session_role1 }}"
+ login_user: "{{ pg_user }}"
+ session_role: "{{ db_session_role1 }}"
+ register: result
+
+- name: Check that pg_extension exists (PostgreSQL >= 9.1)
+ become_user: "{{ pg_user }}"
+ become: true
+ shell: echo "select count(*) from pg_class where relname='pg_extension' and relkind='r'" | psql -AtXq postgres
+ register: pg_extension
+
+- name: Remove plpgsql from testdb using postgresql_ext
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_ext:
+ name: plpgsql
+ db: "{{ db_session_role1 }}"
+ login_user: "{{ pg_user }}"
+ state: absent
+ when:
+ "pg_extension.stdout_lines[-1] == '1'"
+
+- name: Fail when trying to create an extension as a mere mortal user
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_ext:
+ name: plpgsql
+ db: "{{ db_session_role1 }}"
+ login_user: "{{ pg_user }}"
+ session_role: "{{ db_session_role2 }}"
+ ignore_errors: true
+ register: result
+ when:
+ "pg_extension.stdout_lines[-1] == '1'"
+
+- assert:
+ that:
+ - result is failed
+ when:
+ "pg_extension.stdout_lines[-1] == '1'"
+
+- name: Install extension as session_role
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_ext:
+ name: plpgsql
+ db: "{{ db_session_role1 }}"
+ login_user: "{{ pg_user }}"
+ session_role: "{{ db_session_role1 }}"
+ trust_input: false
+ when:
+ "pg_extension.stdout_lines[-1] == '1'"
+
+- name: Check that extension is created and is owned by session_role
+ become_user: "{{ pg_user }}"
+ become: true
+ shell: echo "select rolname from pg_extension join pg_roles on extowner=pg_roles.oid where extname='plpgsql';" | psql -AtXq "{{ db_session_role1 }}"
+ register: result
+ when:
+ "pg_extension.stdout_lines[-1] == '1'"
+
+- assert:
+ that:
+ - "result.stdout_lines[-1] == '{{ db_session_role1 }}'"
+ when:
+ "pg_extension.stdout_lines[-1] == '1'"
+
+- name: Remove plpgsql from testdb using postgresql_ext
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_ext:
+ name: plpgsql
+ db: "{{ db_session_role1 }}"
+ login_user: "{{ pg_user }}"
+ state: absent
+ trust_input: false
+ when:
+ "pg_extension.stdout_lines[-1] == '1'"
+
+- name: Drop test db
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_db:
+ state: absent
+ name: "{{ db_session_role1 }}"
+ login_user: "{{ pg_user }}"
+
+- name: Drop test users
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_user:
+ name: "{{ item }}"
+ state: absent
+ login_user: "{{ pg_user }}"
+ db: postgres
+ with_items:
+ - "{{ db_session_role1 }}"
+ - "{{ db_session_role2 }}"
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_ext/tasks/postgresql_ext_version_opt.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_ext/tasks/postgresql_ext_version_opt.yml
new file mode 100644
index 000000000..2443fe785
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_ext/tasks/postgresql_ext_version_opt.yml
@@ -0,0 +1,554 @@
+# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) <aaklychkov@mail.ru>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+# Tests for postgresql_ext version option
+
+- vars:
+ test_ext: dummy
+ test_schema: schema1
+ task_parameters: &task_parameters
+ become_user: '{{ pg_user }}'
+ become: true
+ register: result
+ pg_parameters: &pg_parameters
+ login_user: '{{ pg_user }}'
+ login_db: postgres
+
+ block:
+ # Preparation:
+ - name: postgresql_ext_version - create schema schema1
+ <<: *task_parameters
+ postgresql_schema:
+ <<: *pg_parameters
+ name: "{{ test_schema }}"
+
+ # Do tests:
+ - name: postgresql_ext_version - create extension of specific version, check mode
+ <<: *task_parameters
+ postgresql_ext:
+ <<: *pg_parameters
+ name: "{{ test_ext }}"
+ schema: "{{ test_schema }}"
+ version: '1.0'
+ trust_input: false
+ check_mode: true
+
+ - assert:
+ that:
+ - result is changed
+
+ - name: postgresql_ext_version - check that nothing was actually changed
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: "SELECT 1 FROM pg_extension WHERE extname = '{{ test_ext }}'"
+
+ - assert:
+ that:
+ - result.rowcount == 0
+
+ - name: postgresql_ext_version - create extension of specific version
+ <<: *task_parameters
+ postgresql_ext:
+ <<: *pg_parameters
+ name: "{{ test_ext }}"
+ schema: "{{ test_schema }}"
+ version: '1.0'
+ trust_input: false
+
+ - assert:
+ that:
+ - result is changed
+ - result.queries == ["CREATE EXTENSION \"{{ test_ext }}\" WITH SCHEMA \"{{ test_schema }}\" VERSION '1.0'"]
+
+ - name: postgresql_ext_version - check
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: "SELECT 1 FROM pg_extension WHERE extname = '{{ test_ext }}' AND extversion = '1.0'"
+
+ - assert:
+ that:
+ - result.rowcount == 1
+
+ - name: postgresql_ext_version - try to create extension of the same version again in check_mode
+ <<: *task_parameters
+ postgresql_ext:
+ <<: *pg_parameters
+ name: "{{ test_ext }}"
+ schema: "{{ test_schema }}"
+ version: '1.0'
+ trust_input: false
+ check_mode: true
+
+ - assert:
+ that:
+ - result is not changed
+
+ - name: postgresql_ext_version - check
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: "SELECT 1 FROM pg_extension WHERE extname = '{{ test_ext }}' AND extversion = '1.0'"
+
+ - assert:
+ that:
+ - result.rowcount == 1
+
+ - name: postgresql_ext_version - try to create extension of the same version again in actual mode
+ <<: *task_parameters
+ postgresql_ext:
+ <<: *pg_parameters
+ name: "{{ test_ext }}"
+ schema: "{{ test_schema }}"
+ version: '1.0'
+ trust_input: false
+
+ - assert:
+ that:
+ - result is not changed
+
+ - name: postgresql_ext_version - check
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: "SELECT 1 FROM pg_extension WHERE extname = '{{ test_ext }}' AND extversion = '1.0'"
+
+ - assert:
+ that:
+ - result.rowcount == 1
+
+ - name: postgresql_ext_version - update the extension to the next version in check_mode
+ <<: *task_parameters
+ postgresql_ext:
+ <<: *pg_parameters
+ name: "{{ test_ext }}"
+ schema: "{{ test_schema }}"
+ version: '2.0'
+ trust_input: false
+ check_mode: true
+
+ - assert:
+ that:
+ - result is changed
+
+ - name: postgresql_ext_version - check, the version must be 1.0
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: "SELECT 1 FROM pg_extension WHERE extname = '{{ test_ext }}' AND extversion = '1.0'"
+
+ - assert:
+ that:
+ - result.rowcount == 1
+
+ - name: postgresql_ext_version - update the extension to the next version
+ <<: *task_parameters
+ postgresql_ext:
+ <<: *pg_parameters
+ name: "{{ test_ext }}"
+ schema: "{{ test_schema }}"
+ version: '2.0'
+ trust_input: false
+
+ - assert:
+ that:
+ - result is changed
+ - result.queries == ["ALTER EXTENSION \"{{ test_ext }}\" UPDATE TO '2.0'"]
+
+ - name: postgresql_ext_version - check, the version must be 2.0
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: "SELECT 1 FROM pg_extension WHERE extname = '{{ test_ext }}' AND extversion = '2.0'"
+
+ - assert:
+ that:
+ - result.rowcount == 1
+
+ - name: postgresql_ext_version - check that version won't be changed if version won't be passed
+ <<: *task_parameters
+ postgresql_ext:
+ <<: *pg_parameters
+ name: "{{ test_ext }}"
+ schema: "{{ test_schema }}"
+ trust_input: false
+
+ - assert:
+ that:
+ - result is not changed
+
+ - name: postgresql_ext_version - check, the version must be 2.0
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: "SELECT 1 FROM pg_extension WHERE extname = '{{ test_ext }}' AND extversion = '2.0'"
+
+ - assert:
+ that:
+ - result.rowcount == 1
+
+ - name: postgresql_ext_version - update the extension to the latest version
+ <<: *task_parameters
+ postgresql_ext:
+ <<: *pg_parameters
+ name: "{{ test_ext }}"
+ schema: "{{ test_schema }}"
+ version: latest
+ trust_input: false
+
+ - assert:
+ that:
+ - result is changed
+ - result.queries == ["ALTER EXTENSION \"{{ test_ext }}\" UPDATE"]
+
+ - name: postgresql_ext_version - check
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: "SELECT 1 FROM pg_extension WHERE extname = '{{ test_ext }}' AND extversion = '4.0'"
+
+ - assert:
+ that:
+ - result.rowcount == 1
+
+ - name: postgresql_ext_version - try to update the extension to the latest version again which always runs an update.
+ <<: *task_parameters
+ postgresql_ext:
+ <<: *pg_parameters
+ name: "{{ test_ext }}"
+ schema: "{{ test_schema }}"
+ version: latest
+ trust_input: false
+
+ - assert:
+ that:
+ - result is changed
+
+ - name: postgresql_ext_version - check that version number did not change even though update ran
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: "SELECT 1 FROM pg_extension WHERE extname = '{{ test_ext }}' AND extversion = '4.0'"
+
+ - assert:
+ that:
+ - result.rowcount == 1
+
+ - name: postgresql_ext_version - try to downgrade the extension version, must fail
+ <<: *task_parameters
+ postgresql_ext:
+ <<: *pg_parameters
+ name: "{{ test_ext }}"
+ schema: "{{ test_schema }}"
+ version: '1.0'
+ trust_input: false
+ ignore_errors: true
+
+ - assert:
+ that:
+ - result.failed == true
+
+ - name: postgresql_ext_version - drop the extension in check_mode
+ <<: *task_parameters
+ postgresql_ext:
+ <<: *pg_parameters
+ name: "{{ test_ext }}"
+ state: absent
+ trust_input: false
+ check_mode: true
+
+ - assert:
+ that:
+ - result is changed
+
+ - name: postgresql_ext_version - check that extension exists
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: "SELECT 1 FROM pg_extension WHERE extname = '{{ test_ext }}' AND extversion = '4.0'"
+
+ - assert:
+ that:
+ - result.rowcount == 1
+
+ - name: postgresql_ext_version - drop the extension in actual mode
+ <<: *task_parameters
+ postgresql_ext:
+ <<: *pg_parameters
+ name: "{{ test_ext }}"
+ state: absent
+ trust_input: false
+
+ - assert:
+ that:
+ - result is changed
+
+ - name: postgresql_ext_version - check that extension doesn't exist after the prev step
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: "SELECT 1 FROM pg_extension WHERE extname = '{{ test_ext }}'"
+
+ - assert:
+ that:
+ - result.rowcount == 0
+
+ - name: postgresql_ext_version - try to drop the non-existent extension again
+ <<: *task_parameters
+ postgresql_ext:
+ <<: *pg_parameters
+ name: "{{ test_ext }}"
+ state: absent
+ trust_input: false
+
+ - assert:
+ that:
+ - result is not changed
+
+ - name: postgresql_ext_version - create the extension without passing version
+ <<: *task_parameters
+ postgresql_ext:
+ <<: *pg_parameters
+ name: "{{ test_ext }}"
+ trust_input: false
+
+ - assert:
+ that:
+ - result is changed
+ - result.queries == ["CREATE EXTENSION \"{{ test_ext }}\""]
+
+ - name: postgresql_ext_version - check
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: "SELECT 1 FROM pg_extension WHERE extname = '{{ test_ext }}' AND extversion = '4.0'"
+
+ - assert:
+ that:
+ - result.rowcount == 1
+
+ - name: postgresql_ext_version - try to install non-existent extension
+ <<: *task_parameters
+ postgresql_ext:
+ <<: *pg_parameters
+ name: non_existent
+ trust_input: false
+ ignore_errors: true
+
+ - assert:
+ that:
+ - result.failed == true
+ - result.msg == "Extension non_existent is not available"
+
+ ######################################################################
+ # https://github.com/ansible-collections/community.general/issues/1095
+ - name: Install postgis
+ package:
+ name: '{{ postgis }}'
+
+ - name: Create postgis extension
+ <<: *task_parameters
+ postgresql_ext:
+ <<: *pg_parameters
+ name: postgis
+ version: latest
+
+ - assert:
+ that:
+ - result is changed
+
+ # https://github.com/ansible-collections/community.postgresql/issues/137
+ - name: Drop extension
+ <<: *task_parameters
+ postgresql_ext:
+ <<: *pg_parameters
+ name: '{{ test_ext }}'
+ schema: '{{ test_schema }}'
+ state: absent
+
+ - name: Non standard version
+ <<: *task_parameters
+ postgresql_ext:
+ <<: *pg_parameters
+ name: '{{ test_ext }}'
+ schema: '{{ test_schema }}'
+ version: 0
+
+ - name: Test
+ <<: *task_parameters
+ postgresql_info:
+ <<: *pg_parameters
+
+ - assert:
+ that:
+ - result['databases']['postgres']['extensions']['dummy']['extversion']['major'] == 0
+ - result['databases']['postgres']['extensions']['dummy']['extversion']['minor'] == None
+ - result['databases']['postgres']['extensions']['dummy']['extversion']['raw'] == '0'
+
+ - name: Upgrade extension to a version that have a sub minor version 3.0-1
+ <<: *task_parameters
+ postgresql_ext:
+ <<: *pg_parameters
+ name: '{{ test_ext }}'
+ schema: '{{ test_schema }}'
+ version: '3.0-1'
+
+ - name: Test
+ <<: *task_parameters
+ postgresql_info:
+ <<: *pg_parameters
+
+ - assert:
+ that:
+ - result['databases']['postgres']['extensions']['dummy']['extversion']['major'] == 3
+ - result['databases']['postgres']['extensions']['dummy']['extversion']['minor'] == 0
+ - result['databases']['postgres']['extensions']['dummy']['extversion']['raw'] == '3.0-1'
+
+ - name: Upgrade extension to version 3.0-foo
+ <<: *task_parameters
+ postgresql_ext:
+ <<: *pg_parameters
+ name: '{{ test_ext }}'
+ schema: '{{ test_schema }}'
+ version: '3.0-foo'
+
+ - name: Test
+ <<: *task_parameters
+ postgresql_info:
+ <<: *pg_parameters
+
+ - assert:
+ that:
+ - result['databases']['postgres']['extensions']['dummy']['extversion']['major'] == None
+ - result['databases']['postgres']['extensions']['dummy']['extversion']['minor'] == None
+ - result['databases']['postgres']['extensions']['dummy']['extversion']['raw'] == '3.0-foo'
+
+ - name: Upgrade extension to version 3.beta
+ <<: *task_parameters
+ postgresql_ext:
+ <<: *pg_parameters
+ name: '{{ test_ext }}'
+ schema: '{{ test_schema }}'
+ version: '3.beta'
+
+ - name: Test
+ <<: *task_parameters
+ postgresql_info:
+ <<: *pg_parameters
+
+ - assert:
+ that:
+ - result['databases']['postgres']['extensions']['dummy']['extversion']['major'] == None
+ - result['databases']['postgres']['extensions']['dummy']['extversion']['minor'] == None
+ - result['databases']['postgres']['extensions']['dummy']['extversion']['raw'] == '3.beta'
+
+ - name: Upgrade extension to version 3-1.0
+ <<: *task_parameters
+ postgresql_ext:
+ <<: *pg_parameters
+ name: '{{ test_ext }}'
+ schema: '{{ test_schema }}'
+ version: '3-1.0'
+
+ - name: Test
+ <<: *task_parameters
+ postgresql_info:
+ <<: *pg_parameters
+
+ - assert:
+ that:
+ - result['databases']['postgres']['extensions']['dummy']['extversion']['major'] == 3
+ - result['databases']['postgres']['extensions']['dummy']['extversion']['minor'] == 1
+ - result['databases']['postgres']['extensions']['dummy']['extversion']['raw'] == '3-1.0'
+
+ - name: Upgrade extension to version 3-1.0-1
+ <<: *task_parameters
+ postgresql_ext:
+ <<: *pg_parameters
+ name: '{{ test_ext }}'
+ schema: '{{ test_schema }}'
+ version: '3-1.0-1'
+
+ - name: Test
+ <<: *task_parameters
+ postgresql_info:
+ <<: *pg_parameters
+
+ - assert:
+ that:
+ - result['databases']['postgres']['extensions']['dummy']['extversion']['major'] == 3
+ - result['databases']['postgres']['extensions']['dummy']['extversion']['minor'] == 1
+ - result['databases']['postgres']['extensions']['dummy']['extversion']['raw'] == '3-1.0-1'
+
+ - name: Upgrade extension to version 3-1.foo
+ <<: *task_parameters
+ postgresql_ext:
+ <<: *pg_parameters
+ name: '{{ test_ext }}'
+ schema: '{{ test_schema }}'
+ version: '3-1.foo'
+
+ - name: Test
+ <<: *task_parameters
+ postgresql_info:
+ <<: *pg_parameters
+
+ - assert:
+ that:
+ - result['databases']['postgres']['extensions']['dummy']['extversion']['major'] == None
+ - result['databases']['postgres']['extensions']['dummy']['extversion']['minor'] == None
+ - result['databases']['postgres']['extensions']['dummy']['extversion']['raw'] == '3-1.foo'
+
+ - name: Upgrade extension to version v4
+ <<: *task_parameters
+ postgresql_ext:
+ <<: *pg_parameters
+ name: '{{ test_ext }}'
+ schema: '{{ test_schema }}'
+ version: 'v4'
+
+ - name: Test
+ <<: *task_parameters
+ postgresql_info:
+ <<: *pg_parameters
+
+ - assert:
+ that:
+ - result['databases']['postgres']['extensions']['dummy']['extversion']['major'] == None
+ - result['databases']['postgres']['extensions']['dummy']['extversion']['minor'] == None
+ - result['databases']['postgres']['extensions']['dummy']['extversion']['raw'] == 'v4'
+
+ - name: Upgrade extension to the latest
+ <<: *task_parameters
+ postgresql_ext:
+ <<: *pg_parameters
+ name: '{{ test_ext }}'
+ schema: '{{ test_schema }}'
+ version: latest
+
+ - name: Test
+ <<: *task_parameters
+ postgresql_info:
+ <<: *pg_parameters
+
+ - assert:
+ that:
+ - result['databases']['postgres']['extensions']['dummy']['extversion']['major'] == 4
+ - result['databases']['postgres']['extensions']['dummy']['extversion']['minor'] == 0
+ - result['databases']['postgres']['extensions']['dummy']['extversion']['raw'] == '4.0'
+
+ # Cleanup:
+ - name: postgresql_ext_version - drop the extension
+ <<: *task_parameters
+ postgresql_ext:
+ <<: *pg_parameters
+ name: "{{ test_ext }}"
+ state: absent
+ trust_input: false
+
+ - name: postgresql_ext_version - drop the schema
+ <<: *task_parameters
+ postgresql_schema:
+ <<: *pg_parameters
+ name: "{{ test_schema }}"
+ state: absent
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_idx/aliases b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_idx/aliases
new file mode 100644
index 000000000..a4c92ef85
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_idx/aliases
@@ -0,0 +1,2 @@
+destructive
+shippable/posix/group1
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_idx/meta/main.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_idx/meta/main.yml
new file mode 100644
index 000000000..4ce5a5837
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_idx/meta/main.yml
@@ -0,0 +1,2 @@
+dependencies:
+ - setup_postgresql_db
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_idx/tasks/main.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_idx/tasks/main.yml
new file mode 100644
index 000000000..2f5945616
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_idx/tasks/main.yml
@@ -0,0 +1,7 @@
+####################################################################
+# WARNING: These are designed specifically for Ansible tests #
+# and should not be used as examples of how to write Ansible roles #
+####################################################################
+
+# Initial CI tests of postgresql_idx module
+- import_tasks: postgresql_idx_initial.yml
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_idx/tasks/postgresql_idx_initial.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_idx/tasks/postgresql_idx_initial.yml
new file mode 100644
index 000000000..3c2dc3fb6
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_idx/tasks/postgresql_idx_initial.yml
@@ -0,0 +1,377 @@
+- name: postgresql_idx - create test table called test_table
+ become_user: '{{ pg_user }}'
+ become: true
+ shell: psql postgres -U "{{ pg_user }}" -t -c "CREATE TABLE test_table (id int, story text);"
+ ignore_errors: true
+
+- name: postgresql_idx - drop test tablespace called ssd if exists
+ become_user: '{{ pg_user }}'
+ become: true
+ shell: psql postgres -U "{{ pg_user }}" -t -c "DROP TABLESPACE IF EXISTS ssd;"
+ ignore_errors: true
+
+- name: postgresql_idx - drop dir for test tablespace
+ become: true
+ file:
+ path: /mnt/ssd
+ state: absent
+ ignore_errors: true
+
+- name: postgresql_idx - create dir for test tablespace
+ become: true
+ file:
+ path: /mnt/ssd
+ state: directory
+ owner: '{{ pg_user }}'
+ mode: '0755'
+ ignore_errors: true
+
+- name: postgresql_idx - create test tablespace called ssd
+ become_user: '{{ pg_user }}'
+ become: true
+ shell: psql postgres -U "{{ pg_user }}" -t -c "CREATE TABLESPACE ssd LOCATION '/mnt/ssd';"
+ ignore_errors: true
+ register: tablespace
+
+- name: postgresql_idx - create test schema
+ become_user: '{{ pg_user }}'
+ become: true
+ shell: psql postgres -U "{{ pg_user }}" -t -c "CREATE SCHEMA foo;"
+ ignore_errors: true
+
+- name: postgresql_idx - create table in non-default schema
+ become_user: '{{ pg_user }}'
+ become: true
+ shell: psql postgres -U "{{ pg_user }}" -t -c "CREATE TABLE foo.foo_table (id int, story text);"
+ ignore_errors: true
+
+- name: postgresql_idx - create btree index in check_mode
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_idx:
+ db: postgres
+ login_user: '{{ pg_user }}'
+ table: test_table
+ columns: id, story
+ idxname: Test0_idx
+ check_mode: true
+ register: result
+ ignore_errors: true
+
+- assert:
+ that:
+ - result is changed
+ - result.tblname == ''
+ - result.name == 'Test0_idx'
+ - result.state == 'absent'
+ - result.valid != ''
+ - result.tblspace == ''
+ - result.storage_params == []
+ - result.schema == ''
+ - result.query == ''
+
+- name: postgresql_idx - check nothing changed after the previous step
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ db: postgres
+ login_user: '{{ pg_user }}'
+ query: SELECT 1 FROM pg_indexes WHERE indexname = 'Test0_idx'
+ register: result
+
+- assert:
+ that:
+ - result.rowcount == 0
+
+- name: postgresql_idx - create btree index concurrently
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_idx:
+ db: postgres
+ login_user: '{{ pg_user }}'
+ table: test_table
+ columns: id, story
+ idxname: Test0_idx
+ trust_input: false
+ register: result
+ ignore_errors: true
+
+- assert:
+ that:
+ - result is changed
+ - result.tblname == 'test_table'
+ - result.name == 'Test0_idx'
+ - result.state == 'present'
+ - result.valid != ''
+ - result.tblspace == ''
+ - result.storage_params == []
+ - result.schema == 'public'
+ - result.query == 'CREATE INDEX CONCURRENTLY "Test0_idx" ON "public"."test_table" USING BTREE (id, story)'
+
+- name: postgresql_idx - check the index exists after the previous step
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ db: postgres
+ login_user: '{{ pg_user }}'
+ query: SELECT 1 FROM pg_indexes WHERE indexname = 'Test0_idx'
+ register: result
+
+- assert:
+ that:
+ - result.rowcount == 1
+
+- name: postgresql_idx - try to create existing index again
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_idx:
+ db: postgres
+ login_user: '{{ pg_user }}'
+ table: test_table
+ columns: id, story
+ idxname: Test0_idx
+ register: result
+ ignore_errors: true
+
+- assert:
+ that:
+ - result is not changed
+ - result.tblname == 'test_table'
+ - result.name == 'Test0_idx'
+ - result.state == 'present'
+ - result.valid != ''
+ - result.tblspace == ''
+ - result.storage_params == []
+ - result.schema == 'public'
+ - result.query == ''
+
+- name: postgresql_idx - create btree index - non-default schema, tablespace, storage parameter
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_idx:
+ db: postgres
+ login_user: '{{ pg_user }}'
+ schema: foo
+ table: foo_table
+ columns:
+ - id
+ - story
+ idxname: foo_test_idx
+ tablespace: ssd
+ storage_params: fillfactor=90
+ trust_input: false
+ register: result
+ ignore_errors: true
+ when: tablespace.rc == 0
+
+- assert:
+ that:
+ - result is changed
+ - result.tblname == 'foo_table'
+ - result.name == 'foo_test_idx'
+ - result.state == 'present'
+ - result.valid != ''
+ - result.tblspace == 'ssd'
+ - result.storage_params == [ "fillfactor=90" ]
+ - result.schema == 'foo'
+ - result.query == 'CREATE INDEX CONCURRENTLY "foo_test_idx" ON "foo"."foo_table" USING BTREE (id,story) WITH (fillfactor=90) TABLESPACE "ssd"'
+ when: tablespace.rc == 0
+
+- name: postgresql_idx - create brin index not concurrently
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_idx:
+ db: postgres
+ login_user: '{{ pg_user }}'
+ schema: public
+ table: test_table
+ state: present
+ type: brin
+ columns: id
+ idxname: test_brin_idx
+ concurrent: false
+ trust_input: false
+ register: result
+ ignore_errors: true
+
+- assert:
+ that:
+ - result is changed
+ - result.tblname == 'test_table'
+ - result.name == 'test_brin_idx'
+ - result.state == 'present'
+ - result.valid != ''
+ - result.tblspace == ''
+ - result.storage_params == []
+ - result.schema == 'public'
+ - result.query == 'CREATE INDEX "test_brin_idx" ON "public"."test_table" USING brin (id)'
+ when: postgres_version_resp.stdout is version('9.5', '>=')
+
+- name: postgresql_idx - create index with condition
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_idx:
+ db: postgres
+ login_user: '{{ pg_user }}'
+ table: test_table
+ columns: id
+ idxname: test1_idx
+ cond: id > 1 AND id != 10
+ trust_input: false
+ register: result
+ ignore_errors: true
+
+- assert:
+ that:
+ - result is changed
+ - result.tblname == 'test_table'
+ - result.name == 'test1_idx'
+ - result.state == 'present'
+ - result.valid != ''
+ - result.tblspace == ''
+ - result.storage_params == []
+ - result.schema == 'public'
+ - result.query == 'CREATE INDEX CONCURRENTLY "test1_idx" ON "public"."test_table" USING BTREE (id) WHERE id > 1 AND id != 10'
+
+- name: postgresql_idx - create unique index
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_idx:
+ db: postgres
+ login_user: '{{ pg_user }}'
+ table: test_table
+ columns: story
+ idxname: test_unique0_idx
+ unique: true
+ trust_input: false
+ register: result
+ ignore_errors: true
+
+- assert:
+ that:
+ - result is changed
+ - result.tblname == 'test_table'
+ - result.name == 'test_unique0_idx'
+ - result.state == 'present'
+ - result.valid != ''
+ - result.tblspace == ''
+ - result.storage_params == []
+ - result.schema == 'public'
+ - result.query == 'CREATE UNIQUE INDEX CONCURRENTLY "test_unique0_idx" ON "public"."test_table" USING BTREE (story)'
+
+- name: postgresql_idx - avoid unique index with type different of btree
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_idx:
+ db: postgres
+ login_user: '{{ pg_user }}'
+ table: test_table
+ columns: story
+ idxname: test_unique0_idx
+ unique: true
+ concurrent: false
+ type: brin
+ register: result
+ ignore_errors: true
+
+- assert:
+ that:
+ - result is not changed
+ - result.msg == 'Only btree currently supports unique indexes'
+
+- name: postgresql_idx - drop index from specific schema cascade in check_mode
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_idx:
+ db: postgres
+ login_user: '{{ pg_user }}'
+ schema: foo
+ name: foo_test_idx
+ cascade: true
+ state: absent
+ concurrent: false
+ trust_input: true
+ check_mode: true
+ register: result
+ ignore_errors: true
+ when: tablespace.rc == 0
+
+- assert:
+ that:
+ - result is changed
+ - result.name == 'foo_test_idx'
+ - result.state == 'present'
+ - result.schema == 'foo'
+ - result.query == ''
+ when: tablespace.rc == 0
+
+- name: postgresql_idx - check the index exists after the previous step
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ db: postgres
+ login_user: '{{ pg_user }}'
+ query: SELECT 1 FROM pg_indexes WHERE indexname = 'foo_test_idx' AND schemaname = 'foo'
+ register: result
+ when: tablespace.rc == 0
+
+- assert:
+ that:
+ - result.rowcount == 1
+ when: tablespace.rc == 0
+
+- name: postgresql_idx - drop index from specific schema cascade
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_idx:
+ db: postgres
+ login_user: '{{ pg_user }}'
+ schema: foo
+ name: foo_test_idx
+ cascade: true
+ state: absent
+ concurrent: false
+ register: result
+ ignore_errors: true
+ when: tablespace.rc == 0
+
+- assert:
+ that:
+ - result is changed
+ - result.name == 'foo_test_idx'
+ - result.state == 'absent'
+ - result.schema == 'foo'
+ - result.query == 'DROP INDEX "foo"."foo_test_idx" CASCADE'
+ when: tablespace.rc == 0
+
+- name: postgresql_idx - check the index doesn't exist after the previous step
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ db: postgres
+ login_user: '{{ pg_user }}'
+ query: SELECT 1 FROM pg_indexes WHERE indexname = 'foo_test_idx' and schemaname = 'foo'
+ register: result
+ when: tablespace.rc == 0
+
+- assert:
+ that:
+ - result.rowcount == 0
+ when: tablespace.rc == 0
+
+- name: postgresql_idx - try to drop not existing index
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_idx:
+ db: postgres
+ login_user: '{{ pg_user }}'
+ schema: foo
+ name: foo_test_idx
+ state: absent
+ register: result
+ ignore_errors: true
+
+- assert:
+ that:
+ - result is not changed
+ - result.query == ''
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_info/aliases b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_info/aliases
new file mode 100644
index 000000000..786e05315
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_info/aliases
@@ -0,0 +1,4 @@
+destructive
+shippable/posix/group1
+skip/freebsd
+skip/rhel
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_info/defaults/main.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_info/defaults/main.yml
new file mode 100644
index 000000000..7a8fe2a37
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_info/defaults/main.yml
@@ -0,0 +1,13 @@
+---
+pg_user: postgres
+db_default: postgres
+
+test_table1: acme1
+test_pub: first_publication
+test_pub2: second_publication
+replication_role: logical_replication
+replication_pass: alsdjfKJKDf1#
+test_db: acme_db
+test_subscription: test
+test_subscription2: test2
+conn_timeout: 100
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_info/meta/main.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_info/meta/main.yml
new file mode 100644
index 000000000..d72e4d23c
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_info/meta/main.yml
@@ -0,0 +1,2 @@
+dependencies:
+ - setup_postgresql_replication
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_info/tasks/main.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_info/tasks/main.yml
new file mode 100644
index 000000000..04c7788ad
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_info/tasks/main.yml
@@ -0,0 +1,12 @@
+####################################################################
+# WARNING: These are designed specifically for Ansible tests #
+# and should not be used as examples of how to write Ansible roles #
+####################################################################
+
+# For testing getting publication and subscription info
+- import_tasks: setup_publication.yml
+ when: ansible_distribution == 'Ubuntu' and ansible_distribution_major_version >= '18'
+
+# Initial CI tests of postgresql_info module
+- import_tasks: postgresql_info_initial.yml
+ when: ansible_distribution == 'Ubuntu' and ansible_distribution_major_version >= '18'
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_info/tasks/postgresql_info_initial.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_info/tasks/postgresql_info_initial.yml
new file mode 100644
index 000000000..6dfe50542
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_info/tasks/postgresql_info_initial.yml
@@ -0,0 +1,243 @@
+# Copyright: (c) 2020, Andrew Klychkov (@Andersson007) <aaklychkov@mail.ru>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+- vars:
+ task_parameters: &task_parameters
+ become_user: '{{ pg_user }}'
+ become: true
+ register: result
+ pg_parameters: &pg_parameters
+ login_user: '{{ pg_user }}'
+ login_db: '{{ db_default }}'
+ connect_params:
+ connect_timeout: 30
+
+
+ block:
+
+ - name: Create test subscription
+ <<: *task_parameters
+ postgresql_subscription:
+ <<: *pg_parameters
+ login_port: '{{ replica_port }}'
+ name: '{{ test_subscription }}'
+ login_db: '{{ test_db }}'
+ state: present
+ publications: '{{ test_pub }}'
+ connparams:
+ host: 127.0.0.1
+ port: '{{ primary_port }}'
+ user: '{{ replication_role }}'
+ password: '{{ replication_pass }}'
+ dbname: '{{ test_db }}'
+
+ - name: Create test subscription
+ <<: *task_parameters
+ postgresql_subscription:
+ <<: *pg_parameters
+ login_port: '{{ replica_port }}'
+ name: '{{ test_subscription2 }}'
+ login_db: '{{ test_db }}'
+ state: present
+ publications: '{{ test_pub2 }}'
+ connparams:
+ host: 127.0.0.1
+ port: '{{ primary_port }}'
+ user: '{{ replication_role }}'
+ password: '{{ replication_pass }}'
+ dbname: '{{ test_db }}'
+
+ - name: postgresql_info - create role to check session_role
+ <<: *task_parameters
+ postgresql_user:
+ <<: *pg_parameters
+ login_port: '{{ replica_port }}'
+ login_user: "{{ pg_user }}"
+ name: session_superuser
+ role_attr_flags: SUPERUSER
+
+ - name: postgresql_info - create extra DBs for testing
+ <<: *task_parameters
+ postgresql_db:
+ login_user: '{{ pg_user }}'
+ maintenance_db: postgres
+ login_port: '{{ replica_port }}'
+ name: "{{ item }}"
+ loop:
+ - db1
+ - db2
+
+ - name: postgresql_info - create extra schemas for testing
+ <<: *task_parameters
+ postgresql_schema:
+ login_user: '{{ pg_user }}'
+ login_port: '{{ replica_port }}'
+ db: '{{ item[0] }}'
+ name: "{{ item[1] }}"
+ loop:
+ - [ "db1", "db1_schema1"]
+ - [ "db1", "db1_schema2"]
+ - [ "db2", "db2_schema1"]
+ - [ "db2", "db2_schema2"]
+
+ - name: postgresql_table - create extra tables for testing
+ <<: *task_parameters
+ postgresql_table:
+ login_user: '{{ pg_user }}'
+ login_port: '{{ replica_port }}'
+ db: '{{ item[0] }}'
+ name: "{{ item[1] }}.{{ item[2] }}"
+ columns: waste_id int
+ loop:
+ - [ "db1", "db1_schema1", "db1_schema1_table1"]
+ - [ "db1", "db1_schema1", "db1_schema1_table2"]
+ - [ "db1", "db1_schema2", "db1_schema2_table1"]
+ - [ "db1", "db1_schema2", "db1_schema2_table2"]
+ - [ "db2", "db2_schema1", "db2_schema1_table1"]
+ - [ "db2", "db2_schema1", "db2_schema1_table2"]
+ - [ "db2", "db2_schema2", "db2_schema2_table1"]
+ - [ "db2", "db2_schema2", "db2_schema2_table2"]
+
+ - name: postgresql_info - test return values and session_role param
+ <<: *task_parameters
+ postgresql_info:
+ <<: *pg_parameters
+ login_port: '{{ replica_port }}'
+ session_role: session_superuser
+
+ - assert:
+ that:
+ - result.version != {}
+ - result.version.raw is search('PostgreSQL')
+ - result.in_recovery == false
+ - result.databases.{{ db_default }}.collate
+ - result.databases.{{ db_default }}.languages
+ - result.databases.{{ db_default }}.namespaces
+ - result.databases.{{ db_default }}.extensions
+ - result.databases.{{ test_db }}.subscriptions.{{ test_subscription }}
+ - result.databases.{{ test_db }}.subscriptions.{{ test_subscription2 }}
+
+ - result.databases.db1.namespaces.db1_schema1
+ - result.databases.db1.namespaces.db1_schema2
+ - result.databases.db2.namespaces.db2_schema1
+ - result.databases.db2.namespaces.db2_schema2
+
+ - result.settings
+ - result.tablespaces
+ - result.roles
+
+ - assert:
+ that:
+ - result.version.patch != {}
+ - result.version.full == '{{ result.version.major }}.{{ result.version.minor }}.{{ result.version.patch }}'
+ when: result.version.major == 9
+
+ - assert:
+ that:
+ - result.version.full == '{{ result.version.major }}.{{ result.version.minor }}'
+ when: result.version.major >= 10
+
+ - name: postgresql_info - check filter param passed by list
+ <<: *task_parameters
+ postgresql_info:
+ <<: *pg_parameters
+ login_port: '{{ replica_port }}'
+ filter:
+ - ver*
+ - rol*
+ - in_recov*
+
+ - assert:
+ that:
+ - result.version != {}
+ - result.roles
+ - result.in_recovery == false
+ - result.databases == {}
+ - result.repl_slots == {}
+ - result.replications == {}
+ - result.settings == {}
+ - result.tablespaces == {}
+
+ - name: postgresql_info - check filter param passed by string
+ <<: *task_parameters
+ postgresql_info:
+ <<: *pg_parameters
+ login_port: '{{ replica_port }}'
+ filter: ver*,role*
+
+ - assert:
+ that:
+ - result.version != {}
+ - result.roles
+ - result.databases == {}
+ - result.repl_slots == {}
+ - result.replications == {}
+ - result.settings == {}
+ - result.tablespaces == {}
+
+ - name: postgresql_info - check filter param passed by string
+ <<: *task_parameters
+ postgresql_info:
+ <<: *pg_parameters
+ login_port: '{{ replica_port }}'
+ filter: ver*
+
+ - assert:
+ that:
+ - result.version
+ - result.roles == {}
+
+ - name: postgresql_info - check excluding filter param passed by list
+ <<: *task_parameters
+ postgresql_info:
+ <<: *pg_parameters
+ login_port: '{{ replica_port }}'
+ filter:
+ - "!ver*"
+ - "!rol*"
+ - "!in_rec*"
+
+ - assert:
+ that:
+ - result.version == {}
+ - result.in_recovery == None
+ - result.roles == {}
+ - result.databases
+
+ - name: postgresql_info - test return publication info
+ <<: *task_parameters
+ postgresql_info:
+ <<: *pg_parameters
+ login_db: '{{ test_db }}'
+ login_port: '{{ primary_port }}'
+ trust_input: true
+
+ - assert:
+ that:
+ - result.version != {}
+ - result.in_recovery == false
+ - result.databases.{{ db_default }}.collate
+ - result.databases.{{ db_default }}.languages
+ - result.databases.{{ db_default }}.namespaces
+ - result.databases.{{ db_default }}.extensions
+ - result.databases.{{ test_db }}.publications.{{ test_pub }}.ownername == '{{ pg_user }}'
+ - result.databases.{{ test_db }}.publications.{{ test_pub2 }}.puballtables == true
+ - result.settings
+ - result.tablespaces
+ - result.roles
+
+ - name: postgresql_info - test trust_input parameter
+ <<: *task_parameters
+ postgresql_info:
+ <<: *pg_parameters
+ login_db: '{{ test_db }}'
+ login_port: '{{ primary_port }}'
+ trust_input: false
+ session_role: 'curious.anonymous"; SELECT * FROM information_schema.tables; --'
+ register: result
+ ignore_errors: true
+
+ - assert:
+ that:
+ - result is failed
+ - result.msg is search('is potentially dangerous')
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_info/tasks/setup_publication.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_info/tasks/setup_publication.yml
new file mode 100644
index 000000000..c68edd456
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_info/tasks/setup_publication.yml
@@ -0,0 +1,64 @@
+# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) <aaklychkov@mail.ru>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+# Preparation for further tests of postgresql_subscription module.
+
+- vars:
+ task_parameters: &task_parameters
+ become_user: '{{ pg_user }}'
+ become: true
+ register: result
+ pg_parameters: &pg_parameters
+ login_user: '{{ pg_user }}'
+ login_db: '{{ test_db }}'
+ connect_params:
+ connect_timeout: 30
+
+ block:
+ - name: Create test db
+ <<: *task_parameters
+ postgresql_db:
+ login_user: '{{ pg_user }}'
+ login_port: '{{ item }}'
+ maintenance_db: '{{ db_default }}'
+ name: '{{ test_db }}'
+ loop:
+ - '{{ primary_port }}'
+ - '{{ replica_port }}'
+
+ - name: Create test role
+ <<: *task_parameters
+ postgresql_user:
+ <<: *pg_parameters
+ login_port: '{{ item }}'
+ name: '{{ replication_role }}'
+ password: '{{ replication_pass }}'
+ role_attr_flags: LOGIN,REPLICATION
+ loop:
+ - '{{ primary_port }}'
+ - '{{ replica_port }}'
+
+ - name: Create test table
+ <<: *task_parameters
+ postgresql_table:
+ <<: *pg_parameters
+ login_port: '{{ item }}'
+ name: '{{ test_table1 }}'
+ columns:
+ - id int
+ loop:
+ - '{{ primary_port }}'
+ - '{{ replica_port }}'
+
+ - name: Create publication
+ <<: *task_parameters
+ postgresql_publication:
+ <<: *pg_parameters
+ login_port: '{{ primary_port }}'
+ name: '{{ test_pub }}'
+
+ - name: Create publication
+ <<: *task_parameters
+ postgresql_publication:
+ <<: *pg_parameters
+ login_port: '{{ primary_port }}'
+ name: '{{ test_pub2 }}'
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_lang/aliases b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_lang/aliases
new file mode 100644
index 000000000..a4c92ef85
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_lang/aliases
@@ -0,0 +1,2 @@
+destructive
+shippable/posix/group1
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_lang/meta/main.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_lang/meta/main.yml
new file mode 100644
index 000000000..4ce5a5837
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_lang/meta/main.yml
@@ -0,0 +1,2 @@
+dependencies:
+ - setup_postgresql_db
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_lang/tasks/main.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_lang/tasks/main.yml
new file mode 100644
index 000000000..799501432
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_lang/tasks/main.yml
@@ -0,0 +1,25 @@
+####################################################################
+# WARNING: These are designed specifically for Ansible tests #
+# and should not be used as examples of how to write Ansible roles #
+####################################################################
+
+- name: Include distribution specific variables
+ include_vars: "{{ lookup('first_found', params) }}"
+ vars:
+ params:
+ files:
+ - "{{ ansible_facts.distribution }}-{{ ansible_facts.distribution_major_version }}.yml"
+ - default.yml
+ paths:
+ - vars
+
+# Only run on CentOS 7 because there is a stack trace on CentOS 8 because the module
+# is looking for the incorrect version of plpython.
+# https://gist.github.com/samdoran/8fc1b4ae834d3e66d1895d087419b8d8
+- name: Initial CI tests of postgresql_lang module
+ when:
+ - ansible_facts.distribution == 'CentOS'
+ - ansible_facts.distribution_major_version is version ('7', '==')
+ block:
+ - include_tasks: postgresql_lang_initial.yml
+ - include_tasks: postgresql_lang_add_owner_param.yml
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_lang/tasks/postgresql_lang_add_owner_param.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_lang/tasks/postgresql_lang_add_owner_param.yml
new file mode 100644
index 000000000..a08ff82f2
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_lang/tasks/postgresql_lang_add_owner_param.yml
@@ -0,0 +1,199 @@
+# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) <aaklychkov@mail.ru>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+- vars:
+ test_user1: alice
+ test_user2: bob
+ test_lang: plperl
+ non_existent_role: fake_role
+ task_parameters: &task_parameters
+ become_user: '{{ pg_user }}'
+ become: true
+ register: result
+ pg_parameters: &pg_parameters
+ login_user: '{{ pg_user }}'
+ login_db: postgres
+
+ block:
+ - name: Create roles for tests
+ <<: *task_parameters
+ postgresql_user:
+ <<: *pg_parameters
+ name: '{{ item }}'
+ loop:
+ - '{{ test_user1 }}'
+ - '{{ test_user2 }}'
+
+ - name: Create lang with owner in check_mode
+ <<: *task_parameters
+ postgresql_lang:
+ <<: *pg_parameters
+ name: '{{ test_lang }}'
+ owner: '{{ test_user1 }}'
+ trust_input: false
+ check_mode: true
+
+ - assert:
+ that:
+ - result is changed
+ - result.queries == []
+
+ - name: Check that nothing was actually changed
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: >
+ SELECT r.rolname FROM pg_language l
+ JOIN pg_roles r ON l.lanowner = r.oid
+ WHERE l.lanname = '{{ test_lang }}'
+ AND r.rolname = '{{ test_user1 }}'
+
+ - assert:
+ that:
+ - result.rowcount == 0
+
+ - name: Create lang with owner
+ <<: *task_parameters
+ postgresql_lang:
+ <<: *pg_parameters
+ name: '{{ test_lang }}'
+ owner: '{{ test_user1 }}'
+ trust_input: false
+
+ - assert:
+ that:
+ - result is changed
+ - result.queries == ['CREATE LANGUAGE "{{ test_lang }}"', 'ALTER LANGUAGE "{{ test_lang }}" OWNER TO "{{ test_user1 }}"']
+
+ - name: Check
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: >
+ SELECT r.rolname FROM pg_language l
+ JOIN pg_roles r ON l.lanowner = r.oid
+ WHERE l.lanname = '{{ test_lang }}'
+ AND r.rolname = '{{ test_user1 }}'
+
+ - assert:
+ that:
+ - result.rowcount == 1
+
+ - name: Change lang owner in check_mode
+ <<: *task_parameters
+ postgresql_lang:
+ <<: *pg_parameters
+ name: '{{ test_lang }}'
+ owner: '{{ test_user2 }}'
+ trust_input: true
+ check_mode: true
+
+ - assert:
+ that:
+ - result is changed
+ - result.queries == ['ALTER LANGUAGE "{{ test_lang }}" OWNER TO "{{ test_user2 }}"']
+
+ - name: Check that nothing was actually changed
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: >
+ SELECT r.rolname FROM pg_language l
+ JOIN pg_roles r ON l.lanowner = r.oid
+ WHERE l.lanname = '{{ test_lang }}'
+ AND r.rolname = '{{ test_user2 }}'
+
+ - assert:
+ that:
+ - result.rowcount == 0
+
+ - name: Change lang owner
+ <<: *task_parameters
+ postgresql_lang:
+ <<: *pg_parameters
+ name: '{{ test_lang }}'
+ owner: '{{ test_user2 }}'
+
+ - assert:
+ that:
+ - result is changed
+ # TODO: the first elem of the returned list below
+ # looks like a bug, not related with the option owner, needs to be checked
+ - result.queries == ["UPDATE pg_language SET lanpltrusted = false WHERE lanname = '{{ test_lang }}'", 'ALTER LANGUAGE "{{ test_lang }}" OWNER TO "{{ test_user2 }}"']
+
+ - name: Check
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: >
+ SELECT r.rolname FROM pg_language l
+ JOIN pg_roles r ON l.lanowner = r.oid
+ WHERE l.lanname = '{{ test_lang }}'
+ AND r.rolname = '{{ test_user2 }}'
+
+ - assert:
+ that:
+ - result.rowcount == 1
+
+ - name: Try to change lang owner again to the same role
+ <<: *task_parameters
+ postgresql_lang:
+ <<: *pg_parameters
+ name: '{{ test_lang }}'
+ owner: '{{ test_user2 }}'
+
+ - assert:
+ that:
+ - result is not changed
+ - result.queries == []
+
+ - name: Check
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: >
+ SELECT r.rolname FROM pg_language l
+ JOIN pg_roles r ON l.lanowner = r.oid
+ WHERE l.lanname = '{{ test_lang }}'
+ AND r.rolname = '{{ test_user2 }}'
+
+ - assert:
+ that:
+ - result.rowcount == 1
+
+ - name: Drop test lang with owner, must ignore
+ <<: *task_parameters
+ postgresql_lang:
+ <<: *pg_parameters
+ name: '{{ test_lang }}'
+ state: absent
+ owner: '{{ non_existent_role }}'
+
+ - assert:
+ that:
+ - result is changed
+ - result.queries == ["DROP LANGUAGE \"{{ test_lang }}\""]
+
+ - name: Check
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: >
+ SELECT r.rolname FROM pg_language l
+ JOIN pg_roles r ON l.lanowner = r.oid
+ WHERE l.lanname = '{{ test_lang }}'
+
+ - assert:
+ that:
+ - result.rowcount == 0
+
+ # Clean up
+ - name: Drop test roles
+ <<: *task_parameters
+ postgresql_user:
+ <<: *pg_parameters
+ name: '{{ item }}'
+ state: absent
+ loop:
+ - '{{ test_user1 }}'
+ - '{{ test_user2 }}'
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_lang/tasks/postgresql_lang_initial.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_lang/tasks/postgresql_lang_initial.yml
new file mode 100644
index 000000000..1d24778b4
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_lang/tasks/postgresql_lang_initial.yml
@@ -0,0 +1,231 @@
+# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) <aaklychkov@mail.ru>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+# Preparation for tests:
+- name: Install PostgreSQL support packages
+ become: true
+ action: "{{ ansible_facts.pkg_mgr }}"
+ args:
+ name: "{{ postgresql_lang_packages }}"
+ state: present
+
+###############
+# Do main tests
+#
+
+# Create language in check_mode:
+- name: postgresql_lang - create plperl in check_mode
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_lang:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ name: plperl
+ register: result
+ ignore_errors: true
+ check_mode: true
+
+- assert:
+ that:
+ - result is changed
+ - result.queries == []
+
+- name: postgresql_lang - check that lang doesn't exist after previous step, rowcount must be 0
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ query: "SELECT 1 FROM pg_language WHERE lanname = 'plperl'"
+ register: result
+
+- assert:
+ that:
+ - result.rowcount == 0
+
+# Create language:
+- name: postgresql_lang - create plperl
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_lang:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ name: plperl
+ register: result
+ ignore_errors: true
+
+- assert:
+ that:
+ - result is changed
+ - result.queries == ['CREATE LANGUAGE "plperl"']
+
+- name: postgresql_lang - check that lang exists after previous step
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ query: "SELECT 1 FROM pg_language WHERE lanname = 'plperl'"
+ register: result
+
+- assert:
+ that:
+ - result.rowcount == 1
+
+# Drop language in check_mode:
+- name: postgresql_lang - drop plperl in check_mode
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_lang:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ name: plperl
+ state: absent
+ register: result
+ ignore_errors: true
+ check_mode: true
+
+- assert:
+ that:
+ - result is changed
+ - result.queries == []
+
+- name: postgresql_lang - check that lang exists after previous step, rowcount must be 1
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ query: "SELECT 1 FROM pg_language WHERE lanname = 'plperl'"
+ register: result
+
+- assert:
+ that:
+ - result.rowcount == 1
+
+# Drop language:
+- name: postgresql_lang - drop plperl
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_lang:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ name: plperl
+ state: absent
+ register: result
+ ignore_errors: true
+
+- assert:
+ that:
+ - result is changed
+ - result.queries == ['DROP LANGUAGE "plperl"']
+
+- name: postgresql_lang - check that lang doesn't exist after previous step, rowcount must be 0
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ query: "SELECT 1 FROM pg_language WHERE lanname = 'plperl'"
+ register: result
+
+- assert:
+ that:
+ - result.rowcount == 0
+
+# Check fail_on_drop true
+- name: postgresql_lang - drop c language to check fail_on_drop true
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_lang:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ name: c
+ state: absent
+ fail_on_drop: true
+ register: result
+ ignore_errors: true
+
+- assert:
+ that:
+ - result.failed == true
+
+# Check fail_on_drop no
+- name: postgresql_lang - drop c language to check fail_on_drop no
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_lang:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ name: c
+ state: absent
+ fail_on_drop: false
+ register: result
+ ignore_errors: true
+
+- assert:
+ that:
+ - result.failed == false
+
+# Create trusted language:
+- name: postgresql_lang - create plpythonu
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_lang:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ name: plpythonu
+ trust: true
+ force_trust: true
+ register: result
+ ignore_errors: true
+
+- assert:
+ that:
+ - result is changed
+ - result.queries == ['CREATE TRUSTED LANGUAGE "plpythonu"', "UPDATE pg_language SET lanpltrusted = true WHERE lanname = 'plpythonu'"]
+
+- name: postgresql_lang - check that lang exists and it's trusted after previous step
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ query: "SELECT 1 FROM pg_language WHERE lanname = 'plpythonu' AND lanpltrusted = 't'"
+ register: result
+
+- assert:
+ that:
+ - result.rowcount == 1
+
+# Drop language cascade, tests of aliases:
+- name: postgresql_lang - drop plpythonu cascade
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_lang:
+ login_db: postgres
+ login_user: "{{ pg_user }}"
+ login_port: 5432
+ lang: plpythonu
+ state: absent
+ cascade: true
+ register: result
+ ignore_errors: true
+
+- assert:
+ that:
+ - result is changed
+ - result.queries == ['DROP LANGUAGE "plpythonu" CASCADE']
+
+- name: postgresql_lang - check that lang doesn't exist after previous step, rowcount must be 0
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ query: "SELECT 1 FROM pg_language WHERE lanname = 'plpythonu'"
+ register: result
+
+- assert:
+ that:
+ - result.rowcount == 0
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_lang/vars/CentOS-7.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_lang/vars/CentOS-7.yml
new file mode 100644
index 000000000..8d4bcc7e2
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_lang/vars/CentOS-7.yml
@@ -0,0 +1,3 @@
+postgresql_lang_packages:
+ - postgresql-plperl
+ - postgresql-plpython
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_lang/vars/CentOS-8.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_lang/vars/CentOS-8.yml
new file mode 100644
index 000000000..5da004c8f
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_lang/vars/CentOS-8.yml
@@ -0,0 +1,3 @@
+postgresql_lang_packages:
+ - postgresql-plperl
+ - postgresql-plpython3
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_lang/vars/default.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_lang/vars/default.yml
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_lang/vars/default.yml
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_membership/aliases b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_membership/aliases
new file mode 100644
index 000000000..a4c92ef85
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_membership/aliases
@@ -0,0 +1,2 @@
+destructive
+shippable/posix/group1
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_membership/defaults/main.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_membership/defaults/main.yml
new file mode 100644
index 000000000..7b1d49e44
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_membership/defaults/main.yml
@@ -0,0 +1,6 @@
+test_group1: group1
+test_group2: group2
+test_group3: group.with.dots
+test_user1: user1
+test_user2: user.with.dots
+dangerous_name: 'curious.anonymous"; SELECT * FROM information_schema.tables; --'
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_membership/meta/main.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_membership/meta/main.yml
new file mode 100644
index 000000000..4ce5a5837
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_membership/meta/main.yml
@@ -0,0 +1,2 @@
+dependencies:
+ - setup_postgresql_db
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_membership/tasks/main.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_membership/tasks/main.yml
new file mode 100644
index 000000000..ea058d084
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_membership/tasks/main.yml
@@ -0,0 +1,7 @@
+####################################################################
+# WARNING: These are designed specifically for Ansible tests #
+# and should not be used as examples of how to write Ansible roles #
+####################################################################
+
+# Initial CI tests of postgresql_membership module
+- import_tasks: postgresql_membership_initial.yml
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_membership/tasks/postgresql_membership_initial.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_membership/tasks/postgresql_membership_initial.yml
new file mode 100644
index 000000000..3c8ef17a8
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_membership/tasks/postgresql_membership_initial.yml
@@ -0,0 +1,736 @@
+# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) <aaklychkov@mail.ru>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+####################
+# Prepare for tests:
+
+# Create test roles:
+- name: postgresql_membership - create test roles
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_user:
+ login_user: "{{ pg_user }}"
+ db: postgres
+ name: "{{ item }}"
+ ignore_errors: true
+ with_items:
+ - "{{ test_group1 }}"
+ - "{{ test_group2 }}"
+ - "{{ test_group3 }}"
+ - "{{ test_user1 }}"
+ - "{{ test_user2 }}"
+
+################
+# Do main tests:
+
+### Test check_mode
+# Grant test_group1 to test_user1 in check_mode:
+- name: postgresql_membership - grant test_group1 to test_user1 in check_mode
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_membership:
+ login_user: "{{ pg_user }}"
+ db: postgres
+ group: "{{ test_group1 }}"
+ user: "{{ test_user1 }}"
+ state: present
+ register: result
+ ignore_errors: true
+ check_mode: true
+
+- assert:
+ that:
+ - result is changed
+ - result.groups == ["{{ test_group1 }}"]
+ - result.queries == ["GRANT \"{{ test_group1 }}\" TO \"{{ test_user1 }}\""]
+ - result.granted.{{ test_group1 }} == ["{{ test_user1 }}"]
+ - result.state == "present"
+ - result.target_roles == ["{{ test_user1 }}"]
+
+# Try to revoke test_group1 from test_user1 to check that
+# nothing actually changed in check_mode at the previous step:
+- name: postgresql_membership - try to revoke test_group1 from test_user1 for checking check_mode
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_membership:
+ login_user: "{{ pg_user }}"
+ db: postgres
+ group: "{{ test_group1 }}"
+ user: "{{ test_user1 }}"
+ state: absent
+ register: result
+ ignore_errors: true
+ check_mode: true
+
+- assert:
+ that:
+ - result is not changed
+ - result.groups == ["{{ test_group1 }}"]
+ - result.queries == []
+ - result.revoked.{{ test_group1 }} == []
+ - result.state == "absent"
+ - result.target_roles == ["{{ test_user1 }}"]
+### End of test check_mode
+
+# Grant test_group1 to test_user1:
+- name: postgresql_membership - grant test_group1 to test_user1
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_membership:
+ login_user: "{{ pg_user }}"
+ db: postgres
+ group: "{{ test_group1 }}"
+ user: "{{ test_user1 }}"
+ state: present
+ register: result
+ ignore_errors: true
+
+- assert:
+ that:
+ - result is changed
+ - result.groups == ["{{ test_group1 }}"]
+ - result.queries == ["GRANT \"{{ test_group1 }}\" TO \"{{ test_user1 }}\""]
+ - result.granted.{{ test_group1 }} == ["{{ test_user1 }}"]
+ - result.state == "present"
+ - result.target_roles == ["{{ test_user1 }}"]
+
+# Grant test_group1 to test_user1 again to check that nothing changes:
+- name: postgresql_membership - grant test_group1 to test_user1 again
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_membership:
+ login_user: "{{ pg_user }}"
+ db: postgres
+ group: "{{ test_group1 }}"
+ user: "{{ test_user1 }}"
+ state: present
+ register: result
+ ignore_errors: true
+
+- assert:
+ that:
+ - result is not changed
+ - result.groups == ["{{ test_group1 }}"]
+ - result.queries == []
+ - result.granted.{{ test_group1 }} == []
+ - result.state == "present"
+ - result.target_roles == ["{{ test_user1 }}"]
+
+# Revoke test_group1 from test_user1:
+- name: postgresql_membership - revoke test_group1 from test_user1
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_membership:
+ login_user: "{{ pg_user }}"
+ db: postgres
+ group: "{{ test_group1 }}"
+ user: "{{ test_user1 }}"
+ state: absent
+ register: result
+ ignore_errors: true
+
+- assert:
+ that:
+ - result is changed
+ - result.groups == ["{{ test_group1 }}"]
+ - result.queries == ["REVOKE \"{{ test_group1 }}\" FROM \"{{ test_user1 }}\""]
+ - result.revoked.{{ test_group1 }} == ["{{ test_user1 }}"]
+ - result.state == "absent"
+ - result.target_roles == ["{{ test_user1 }}"]
+
+# Revoke test_group1 from test_user1 again to check that nothing changes:
+- name: postgresql_membership - revoke test_group1 from test_user1 again
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_membership:
+ login_user: "{{ pg_user }}"
+ db: postgres
+ group: "{{ test_group1 }}"
+ user: "{{ test_user1 }}"
+ state: absent
+ register: result
+ ignore_errors: true
+
+- assert:
+ that:
+ - result is not changed
+ - result.groups == ["{{ test_group1 }}"]
+ - result.queries == []
+ - result.revoked.{{ test_group1 }} == []
+ - result.state == "absent"
+ - result.target_roles == ["{{ test_user1 }}"]
+
+# Grant test_group1 and test_group2 to test_user1 and test_user2:
+- name: postgresql_membership - grant two groups to two users
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_membership:
+ login_user: "{{ pg_user }}"
+ db: postgres
+ group:
+ - "{{ test_group1 }}"
+ - "{{ test_group2 }}"
+ user:
+ - "{{ test_user1 }}"
+ - "{{ test_user2 }}"
+ state: present
+ register: result
+ ignore_errors: true
+
+- assert:
+ that:
+ - result is changed
+ - result.groups == ["{{ test_group1 }}", "{{ test_group2 }}"]
+ - result.queries == ["GRANT \"{{ test_group1 }}\" TO \"{{ test_user1 }}\"", "GRANT \"{{ test_group1 }}\" TO \"{{ test_user2 }}\"", "GRANT \"{{ test_group2 }}\" TO \"{{ test_user1 }}\"", "GRANT \"{{ test_group2 }}\" TO \"{{ test_user2 }}\""]
+ - result.granted.{{ test_group1 }} == ["{{ test_user1 }}", "{{ test_user2 }}"]
+ - result.granted.{{ test_group2 }} == ["{{ test_user1 }}", "{{ test_user2 }}"]
+ - result.state == "present"
+ - result.target_roles == ["{{ test_user1 }}", "{{ test_user2 }}"]
+
+# Grant test_group1 and test_group2 to test_user1 and test_user2 again to check that nothing changes:
+- name: postgresql_membership - grant two groups to two users again
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_membership:
+ login_user: "{{ pg_user }}"
+ db: postgres
+ group:
+ - "{{ test_group1 }}"
+ - "{{ test_group2 }}"
+ user:
+ - "{{ test_user1 }}"
+ - "{{ test_user2 }}"
+ state: present
+ register: result
+ ignore_errors: true
+
+- assert:
+ that:
+ - result is not changed
+ - result.groups == ["{{ test_group1 }}", "{{ test_group2 }}"]
+ - result.queries == []
+ - result.granted.{{ test_group1 }} == []
+ - result.granted.{{ test_group2 }} == []
+ - result.state == "present"
+ - result.target_roles == ["{{ test_user1 }}", "{{ test_user2 }}"]
+
+# Revoke only test_group1 from test_user1:
+- name: postgresql_membership - revoke one group from one user
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_membership:
+ login_user: "{{ pg_user }}"
+ db: postgres
+ group: "{{ test_group1 }}"
+ user: "{{ test_user1 }}"
+ state: absent
+ register: result
+ ignore_errors: true
+
+- assert:
+ that:
+ - result is changed
+ - result.groups == ["{{ test_group1 }}"]
+ - result.queries == ["REVOKE \"{{ test_group1 }}\" FROM \"{{ test_user1 }}\""]
+ - result.revoked.{{ test_group1 }} == ["{{ test_user1 }}"]
+ - result.state == "absent"
+ - result.target_roles == ["{{ test_user1 }}"]
+
+# Try to grant test_group1 and test_group2 to test_user1 and test_user2 again
+# to check that nothing changes with test_user2:
+- name: postgresql_membership - grant two groups to two users again
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_membership:
+ login_user: "{{ pg_user }}"
+ db: postgres
+ group:
+ - "{{ test_group1 }}"
+ - "{{ test_group2 }}"
+ user:
+ - "{{ test_user1 }}"
+ - "{{ test_user2 }}"
+ state: present
+ register: result
+ ignore_errors: true
+
+- assert:
+ that:
+ - result is changed
+ - result.groups == ["{{ test_group1 }}", "{{ test_group2 }}"]
+ - result.queries == ["GRANT \"{{ test_group1 }}\" TO \"{{ test_user1 }}\""]
+ - result.granted.{{ test_group1 }} == ["{{ test_user1 }}"]
+ - result.granted.{{ test_group2 }} == []
+ - result.state == "present"
+ - result.target_roles == ["{{ test_user1 }}", "{{ test_user2 }}"]
+
+#####################
+# Check fail_on_role:
+
+# Try to grant non existent group to non existent role with fail_on_role=true:
+- name: postgresql_membership - revoke non existen group from non existen role
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_membership:
+ login_user: "{{ pg_user }}"
+ db: postgres
+ group: fake_group
+ user: fake_user
+ state: present
+ fail_on_role: true
+ register: result
+ ignore_errors: true
+
+- assert:
+ that:
+ - result is not changed
+
+# Try to grant non existent group to non existent role with fail_on_role=no:
+- name: postgresql_membership - revoke non existen group from non existen role
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_membership:
+ login_user: "{{ pg_user }}"
+ db: postgres
+ group: fake_group
+ user: fake_user
+ state: present
+ fail_on_role: false
+ register: result
+ ignore_errors: true
+
+- assert:
+ that:
+ - result is not changed
+ - result.granted == {}
+ - result.groups == []
+ - result.target_roles == []
+ - result.state == 'present'
+
+# Try to revoke non existent group from non existent role with fail_on_role=no:
+- name: postgresql_membership - revoke non existen group from non existen role
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_membership:
+ login_user: "{{ pg_user }}"
+ db: postgres
+ group: fake_group
+ user: fake_user
+ state: absent
+ fail_on_role: false
+ register: result
+ ignore_errors: true
+
+- assert:
+ that:
+ - result is not changed
+ - result.revoked == {}
+ - result.groups == []
+ - result.target_roles == []
+ - result.state == 'absent'
+
+# Grant test_group3 with a name containing dots to test_user1.
+- name: postgresql_membership - grant test_group3 with dots to test_user1
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_membership:
+ login_user: "{{ pg_user }}"
+ db: postgres
+ group: "{{ test_group3 }}"
+ user: "{{ test_user1 }}"
+ state: present
+ register: result
+
+- assert:
+ that:
+ - result is changed
+ - result.queries == ["GRANT \"{{ test_group3 }}\" TO \"{{ test_user1 }}\""]
+
+#############################
+# Check trust_input parameter
+
+- name: postgresql_membership - try to use dangerous input, don't trust
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_membership:
+ login_user: "{{ pg_user }}"
+ db: postgres
+ group:
+ - "{{ test_group3}}"
+ - "{{ dangerous_name }}"
+ user: "{{ test_user1 }}"
+ state: present
+ trust_input: false
+ register: result
+ ignore_errors: true
+
+- assert:
+ that:
+ - result is failed
+ - result.msg == 'Passed input \'{{ dangerous_name }}\' is potentially dangerous'
+
+- name: postgresql_membership - try to use dangerous input, trust explicitly
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_membership:
+ login_user: "{{ pg_user }}"
+ db: postgres
+ group:
+ - "{{ test_group3}}"
+ - "{{ dangerous_name }}"
+ user: "{{ test_user1 }}"
+ state: present
+ trust_input: true
+ register: result
+ ignore_errors: true
+
+- assert:
+ that:
+ - result is failed
+ - result.msg == 'Role {{ dangerous_name }} does not exist'
+
+########################
+# Tests for match method
+- name: Revoke all groups from a role check mode
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_membership:
+ login_user: "{{ pg_user }}"
+ db: postgres
+ groups: []
+ user: "{{ test_user1 }}"
+ state: exact
+ register: result
+ check_mode: true
+
+- assert:
+ that:
+ - result is changed
+ - result.queries == ["REVOKE \"{{ test_group1 }}\" FROM \"{{ test_user1 }}\"", "REVOKE \"{{ test_group2 }}\" FROM \"{{ test_user1 }}\"", "REVOKE \"{{ test_group3 }}\" FROM \"{{ test_user1 }}\""],
+ - result.revoked["group.with.dots"] == ["{{ test_user1 }}"]
+ - result.revoked["group1"] == ["{{ test_user1 }}"]
+ - result.revoked["group2"] == ["{{ test_user1 }}"]
+ - result.granted == {}
+
+
+- name: Check result of prev task
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ login_user: "{{ pg_user }}"
+ db: postgres
+ query: "SELECT ARRAY(SELECT b.rolname FROM pg_catalog.pg_auth_members m JOIN pg_catalog.pg_roles b ON (m.roleid = b.oid) WHERE m.member = r.oid) FROM pg_catalog.pg_roles r WHERE r.rolname = '{{ test_user1 }}'"
+ register: result
+
+- assert:
+ that:
+ - result.query_result.0.array == ['{{ test_group1 }}', '{{ test_group2 }}', '{{ test_group3 }}']
+
+- name: Revoke all groups from a role actual mode
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_membership:
+ login_user: "{{ pg_user }}"
+ db: postgres
+ groups: []
+ user: "{{ test_user1 }}"
+ state: exact
+ register: result
+
+- assert:
+ that:
+ - result is changed
+ - result.queries == ["REVOKE \"{{ test_group1 }}\" FROM \"{{ test_user1 }}\"", "REVOKE \"{{ test_group2 }}\" FROM \"{{ test_user1 }}\"", "REVOKE \"{{ test_group3 }}\" FROM \"{{ test_user1 }}\""],
+ - result.revoked["group.with.dots"] == ["{{ test_user1 }}"]
+ - result.revoked["group1"] == ["{{ test_user1 }}"]
+ - result.revoked["group2"] == ["{{ test_user1 }}"]
+ - result.granted == {}
+
+
+- name: Check result of prev task
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ login_user: "{{ pg_user }}"
+ db: postgres
+ query: "SELECT ARRAY(SELECT b.rolname FROM pg_catalog.pg_auth_members m JOIN pg_catalog.pg_roles b ON (m.roleid = b.oid) WHERE m.member = r.oid) FROM pg_catalog.pg_roles r WHERE r.rolname = '{{ test_user1 }}'"
+ register: result
+
+- assert:
+ that:
+ - result.query_result.0.array == []
+
+- name: Grant all groups to a role check mode
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_membership:
+ login_user: "{{ pg_user }}"
+ db: postgres
+ groups:
+ - "{{ test_group1 }}"
+ - "{{ test_group2 }}"
+ - "{{ test_group3 }}"
+ user: "{{ test_user1 }}"
+ state: exact
+ register: result
+ check_mode: true
+
+- assert:
+ that:
+ - result is changed
+ - result.queries == ["GRANT \"{{ test_group1 }}\" TO \"{{ test_user1 }}\"", "GRANT \"{{ test_group2 }}\" TO \"{{ test_user1 }}\"", "GRANT \"{{ test_group3 }}\" TO \"{{ test_user1 }}\""],
+ - result.granted["group.with.dots"] == ["{{ test_user1 }}"]
+ - result.granted["group1"] == ["{{ test_user1 }}"]
+ - result.granted["group2"] == ["{{ test_user1 }}"]
+ - result.revoked == {}
+
+
+- name: Check result of prev task
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ login_user: "{{ pg_user }}"
+ db: postgres
+ query: "SELECT ARRAY(SELECT b.rolname FROM pg_catalog.pg_auth_members m JOIN pg_catalog.pg_roles b ON (m.roleid = b.oid) WHERE m.member = r.oid) FROM pg_catalog.pg_roles r WHERE r.rolname = '{{ test_user1 }}'"
+ register: result
+
+- assert:
+ that:
+ - result.query_result.0.array == []
+
+- name: Grant all groups to a role real mode
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_membership:
+ login_user: "{{ pg_user }}"
+ db: postgres
+ groups:
+ - "{{ test_group1 }}"
+ - "{{ test_group2 }}"
+ - "{{ test_group3 }}"
+ user: "{{ test_user1 }}"
+ state: exact
+ register: result
+
+- assert:
+ that:
+ - result is changed
+ - result.queries == ["GRANT \"{{ test_group1 }}\" TO \"{{ test_user1 }}\"", "GRANT \"{{ test_group2 }}\" TO \"{{ test_user1 }}\"", "GRANT \"{{ test_group3 }}\" TO \"{{ test_user1 }}\""],
+ - result.granted["group.with.dots"] == ["{{ test_user1 }}"]
+ - result.granted["group1"] == ["{{ test_user1 }}"]
+ - result.granted["group2"] == ["{{ test_user1 }}"]
+ - result.revoked == {}
+
+
+- name: Check result of prev task
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ login_user: "{{ pg_user }}"
+ db: postgres
+ query: "SELECT ARRAY(SELECT b.rolname FROM pg_catalog.pg_auth_members m JOIN pg_catalog.pg_roles b ON (m.roleid = b.oid) WHERE m.member = r.oid) FROM pg_catalog.pg_roles r WHERE r.rolname = '{{ test_user1 }}'"
+ register: result
+
+- assert:
+ that:
+ - result.query_result.0.array == ['{{ test_group1 }}', '{{ test_group2 }}', '{{ test_group3 }}']
+
+- name: Change groups 1
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_membership:
+ login_user: "{{ pg_user }}"
+ db: postgres
+ groups:
+ - "{{ test_group1 }}"
+ user: "{{ test_user1 }}"
+ state: exact
+ register: result
+
+- assert:
+ that:
+ - result is changed
+ - result.queries == ["REVOKE \"{{ test_group2 }}\" FROM \"{{ test_user1 }}\"", "REVOKE \"{{ test_group3 }}\" FROM \"{{ test_user1 }}\""],
+ - result.revoked["group.with.dots"] == ["{{ test_user1 }}"]
+ - result.revoked["group2"] == ["{{ test_user1 }}"]
+ - result.granted == {}
+
+
+- name: Check result of prev task
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ login_user: "{{ pg_user }}"
+ db: postgres
+ query: "SELECT ARRAY(SELECT b.rolname FROM pg_catalog.pg_auth_members m JOIN pg_catalog.pg_roles b ON (m.roleid = b.oid) WHERE m.member = r.oid) FROM pg_catalog.pg_roles r WHERE r.rolname = '{{ test_user1 }}'"
+ register: result
+
+- assert:
+ that:
+ - result.query_result.0.array == ['{{ test_group1 }}']
+
+- name: Change groups 2
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_membership:
+ login_user: "{{ pg_user }}"
+ db: postgres
+ groups:
+ - "{{ test_group2 }}"
+ - "{{ test_group3 }}"
+ user: "{{ test_user1 }}"
+ state: exact
+ register: result
+
+- assert:
+ that:
+ - result is changed
+ - result.queries == ["REVOKE \"{{ test_group1 }}\" FROM \"{{ test_user1 }}\"", "GRANT \"{{ test_group2 }}\" TO \"{{ test_user1 }}\"", "GRANT \"{{ test_group3 }}\" TO \"{{ test_user1 }}\""],
+ - result.granted["group.with.dots"] == ["{{ test_user1 }}"]
+ - result.granted["group2"] == ["{{ test_user1 }}"]
+ - result.revoked["group1"] == ["{{ test_user1 }}"]
+
+- name: Check result of prev task
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ login_user: "{{ pg_user }}"
+ db: postgres
+ query: "SELECT ARRAY(SELECT b.rolname FROM pg_catalog.pg_auth_members m JOIN pg_catalog.pg_roles b ON (m.roleid = b.oid) WHERE m.member = r.oid) FROM pg_catalog.pg_roles r WHERE r.rolname = '{{ test_user1 }}'"
+ register: result
+
+- assert:
+ that:
+ - result.query_result.0.array == ['{{ test_group2 }}', '{{ test_group3 }}']
+
+- name: Change groups 2 again
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_membership:
+ login_user: "{{ pg_user }}"
+ db: postgres
+ groups:
+ - "{{ test_group2 }}"
+ - "{{ test_group3 }}"
+ user: "{{ test_user1 }}"
+ state: exact
+ register: result
+
+- assert:
+ that:
+ - result is not changed
+ - result.queries == [],
+ - result.granted == {}
+ - result.revoked == {}
+
+
+- name: Check result of prev task
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ login_user: "{{ pg_user }}"
+ db: postgres
+ query: "SELECT ARRAY(SELECT b.rolname FROM pg_catalog.pg_auth_members m JOIN pg_catalog.pg_roles b ON (m.roleid = b.oid) WHERE m.member = r.oid) FROM pg_catalog.pg_roles r WHERE r.rolname = '{{ test_user1 }}'"
+ register: result
+
+- assert:
+ that:
+ - result.query_result.0.array == ['{{ test_group2 }}', '{{ test_group3 }}']
+
+- name: Change groups for two users
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_membership:
+ login_user: "{{ pg_user }}"
+ db: postgres
+ groups:
+ - "{{ test_group1 }}"
+ - "{{ test_group2 }}"
+ - "{{ test_group3 }}"
+ users:
+ - "{{ test_user1 }}"
+ - "{{ test_user2 }}"
+ state: exact
+ register: result
+
+- assert:
+ that:
+ - result is changed
+ - result.queries == ["GRANT \"{{ test_group1 }}\" TO \"{{ test_user1 }}\"", "GRANT \"{{ test_group3 }}\" TO \"{{ test_user2 }}\""],
+ - result.granted["group1"] == ["{{ test_user1 }}"]
+ - result.granted["group.with.dots"] == ["{{ test_user2 }}"]
+ - result.revoked == {}
+
+- name: Check result of prev task for {{ test_user1 }}
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ login_user: "{{ pg_user }}"
+ db: postgres
+ query: "SELECT ARRAY(SELECT b.rolname FROM pg_catalog.pg_auth_members m JOIN pg_catalog.pg_roles b ON (m.roleid = b.oid) WHERE m.member = r.oid) FROM pg_catalog.pg_roles r WHERE r.rolname = '{{ test_user1 }}'"
+ register: result
+
+- assert:
+ that:
+ - result.query_result.0.array == ['{{ test_group1 }}', '{{ test_group2 }}', '{{ test_group3 }}']
+
+- name: Check result of prev task for {{ test_user2 }}
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ login_user: "{{ pg_user }}"
+ db: postgres
+ query: "SELECT ARRAY(SELECT b.rolname FROM pg_catalog.pg_auth_members m JOIN pg_catalog.pg_roles b ON (m.roleid = b.oid) WHERE m.member = r.oid) FROM pg_catalog.pg_roles r WHERE r.rolname = '{{ test_user2 }}'"
+ register: result
+
+- assert:
+ that:
+ - result.query_result.0.array == ['{{ test_group1 }}', '{{ test_group2 }}', '{{ test_group3 }}']
+
+- name: Change groups for two users 2
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_membership:
+ login_user: "{{ pg_user }}"
+ db: postgres
+ groups:
+ - "{{ test_group2 }}"
+ - "{{ test_group3 }}"
+ users:
+ - "{{ test_user1 }}"
+ - "{{ test_user2 }}"
+ state: exact
+ register: result
+
+- assert:
+ that:
+ - result is changed
+ - result.queries == ["REVOKE \"{{ test_group1 }}\" FROM \"{{ test_user1 }}\"", "REVOKE \"{{ test_group1 }}\" FROM \"{{ test_user2 }}\""],
+ - result.revoked["group1"] == ["{{ test_user1 }}", "{{ test_user2 }}"]
+ - result.granted == {}
+
+- name: Check result of prev task for {{ test_user1 }}
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ login_user: "{{ pg_user }}"
+ db: postgres
+ query: "SELECT ARRAY(SELECT b.rolname FROM pg_catalog.pg_auth_members m JOIN pg_catalog.pg_roles b ON (m.roleid = b.oid) WHERE m.member = r.oid) FROM pg_catalog.pg_roles r WHERE r.rolname = '{{ test_user1 }}'"
+ register: result
+
+- assert:
+ that:
+ - result.query_result.0.array == ['{{ test_group2 }}', '{{ test_group3 }}']
+
+- name: Check result of prev task for {{ test_user2 }}
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ login_user: "{{ pg_user }}"
+ db: postgres
+ query: "SELECT ARRAY(SELECT b.rolname FROM pg_catalog.pg_auth_members m JOIN pg_catalog.pg_roles b ON (m.roleid = b.oid) WHERE m.member = r.oid) FROM pg_catalog.pg_roles r WHERE r.rolname = '{{ test_user2 }}'"
+ register: result
+
+- assert:
+ that:
+ - result.query_result.0.array == ['{{ test_group2 }}', '{{ test_group3 }}']
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_owner/aliases b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_owner/aliases
new file mode 100644
index 000000000..a4c92ef85
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_owner/aliases
@@ -0,0 +1,2 @@
+destructive
+shippable/posix/group1
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_owner/defaults/main.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_owner/defaults/main.yml
new file mode 100644
index 000000000..e43723c47
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_owner/defaults/main.yml
@@ -0,0 +1,3 @@
+test_tablespace_path: "/ssd"
+
+dangerous_name: 'curious.anonymous"; SELECT * FROM information_schema.tables; --'
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_owner/meta/main.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_owner/meta/main.yml
new file mode 100644
index 000000000..4ce5a5837
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_owner/meta/main.yml
@@ -0,0 +1,2 @@
+dependencies:
+ - setup_postgresql_db
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_owner/tasks/main.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_owner/tasks/main.yml
new file mode 100644
index 000000000..4b2f57510
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_owner/tasks/main.yml
@@ -0,0 +1,9 @@
+####################################################################
+# WARNING: These are designed specifically for Ansible tests #
+# and should not be used as examples of how to write Ansible roles #
+####################################################################
+
+# Initial CI tests of postgresql_owner module
+- import_tasks: postgresql_owner_initial.yml
+ when:
+ - postgres_version_resp.stdout is version('9.4', '>=')
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_owner/tasks/postgresql_owner_initial.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_owner/tasks/postgresql_owner_initial.yml
new file mode 100644
index 000000000..a21160282
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_owner/tasks/postgresql_owner_initial.yml
@@ -0,0 +1,1073 @@
+# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) <aaklychkov@mail.ru>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+####################
+# Prepare for tests:
+
+# Create test roles:
+- name: postgresql_owner - create test roles
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_user:
+ login_user: '{{ pg_user }}'
+ db: postgres
+ name: '{{ item }}'
+ ignore_errors: true
+ with_items:
+ - alice
+ - bob
+
+- name: postgresql_owner - create test database
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_db:
+ login_user: '{{ pg_user }}'
+ db: acme
+
+- name: postgresql_owner - create test table
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ login_user: '{{ pg_user }}'
+ db: acme
+ query: CREATE TABLE my_table (id int)
+
+- name: postgresql_owner - set owner
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_owner:
+ login_user: '{{ pg_user }}'
+ db: acme
+ new_owner: bob
+ obj_name: my_table
+ obj_type: table
+
+- name: postgresql_owner - create test sequence
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ login_user: '{{ pg_user }}'
+ db: acme
+ query: CREATE SEQUENCE test_seq
+
+- name: postgresql_owner - create test function
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ login_user: '{{ pg_user }}'
+ db: acme
+ query: >
+ CREATE FUNCTION increment(integer) RETURNS integer AS 'select $1 + 1;'
+ LANGUAGE SQL IMMUTABLE RETURNS NULL ON NULL INPUT;
+
+- name: postgresql_owner - create test schema
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ login_user: '{{ pg_user }}'
+ db: acme
+ query: CREATE SCHEMA test_schema
+
+- name: postgresql_owner - create test view
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ login_user: '{{ pg_user }}'
+ db: acme
+ query: CREATE VIEW test_view AS SELECT * FROM my_table
+
+- name: postgresql_owner - create test materialized view
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ login_user: '{{ pg_user }}'
+ db: acme
+ query: CREATE MATERIALIZED VIEW test_mat_view AS SELECT * FROM my_table
+ when: postgres_version_resp.stdout is version('9.4', '>=')
+
+- name: postgresql_owner - drop dir for test tablespace
+ become: true
+ file:
+ path: '{{ test_tablespace_path }}'
+ state: absent
+ ignore_errors: true
+
+- name: postgresql_owner - disable selinux
+ become: true
+ shell: setenforce 0
+ ignore_errors: true
+
+- name: postgresql_owner - create dir for test tablespace
+ become: true
+ file:
+ path: '{{ test_tablespace_path }}'
+ state: directory
+ owner: '{{ pg_user }}'
+ group: '{{ pg_user }}'
+ mode: '0700'
+ ignore_errors: true
+
+- name: >
+ postgresql_owner - create a new tablespace called acme and
+ set bob as an its owner
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_tablespace:
+ db: acme
+ login_user: '{{ pg_user }}'
+ name: acme
+ owner: alice
+ location: '{{ test_tablespace_path }}'
+
+################
+# Do main tests:
+
+#
+# check reassign_owned_by param
+#
+# try to reassign ownership to non existent user:
+- name: postgresql_owner - reassign_owned_by to non existent user
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_owner:
+ login_user: '{{ pg_user }}'
+ db: acme
+ new_owner: non_existent
+ reassign_owned_by: bob
+ register: result
+ ignore_errors: true
+
+- assert:
+ that:
+ - result.failed == true
+
+- name: postgresql_owner - reassign_owned_by, check fail_on_role
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_owner:
+ login_user: '{{ pg_user }}'
+ db: acme
+ new_owner: alice
+ reassign_owned_by: non_existent
+ fail_on_role: false
+ register: result
+
+- assert:
+ that:
+ - result.failed == false
+
+- name: postgresql_owner - reassign_owned_by in check_mode
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_owner:
+ login_user: '{{ pg_user }}'
+ db: acme
+ new_owner: alice
+ reassign_owned_by: bob
+ check_mode: true
+ register: result
+
+- assert:
+ that:
+ - result is changed
+ - result.queries == ['REASSIGN OWNED BY "bob" TO "alice"']
+
+- name: postgresql_owner - check that nothing changed after the previous step
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ db: acme
+ login_user: '{{ pg_user }}'
+ query: >
+ SELECT 1 FROM pg_tables WHERE tablename = 'my_table'
+ AND tableowner = 'alice'
+ ignore_errors: true
+ register: result
+
+- assert:
+ that:
+ - result.rowcount == 0
+
+- name: postgresql_owner - reassign_owned_by
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_owner:
+ login_user: '{{ pg_user }}'
+ db: acme
+ new_owner: alice
+ reassign_owned_by: bob
+ trust_input: true
+ register: result
+
+- assert:
+ that:
+ - result is changed
+ - result.queries == ['REASSIGN OWNED BY "bob" TO "alice"']
+
+- name: postgresql_owner - check that ownership has been changed after the previous step
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ db: acme
+ login_user: '{{ pg_user }}'
+ query: SELECT 1 FROM pg_tables WHERE tablename = 'my_table' AND tableowner = 'alice'
+ ignore_errors: true
+ register: result
+
+- assert:
+ that:
+ - result.rowcount == 1
+
+###########################
+# Test trust_inpt parameter
+
+- name: postgresql_owner - reassign_owned_by, trust_input false
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_owner:
+ login_user: '{{ pg_user }}'
+ db: acme
+ new_owner: '{{ dangerous_name }}'
+ reassign_owned_by: alice
+ trust_input: false
+ register: result
+ ignore_errors: true
+
+- assert:
+ that:
+ - result is failed
+ - result.msg == 'Passed input \'{{ dangerous_name }}\' is potentially dangerous'
+
+- name: postgresql_owner - reassign_owned_by, trust_input true by default
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_owner:
+ login_user: '{{ pg_user }}'
+ db: acme
+ new_owner: '{{ dangerous_name }}'
+ reassign_owned_by: alice
+ register: result
+ ignore_errors: true
+
+- assert:
+ that:
+ - result is not changed
+ - result.msg is search('does not exist')
+# End of testing trust_input
+
+#
+# Check obj_type for each type
+#
+
+# #############################
+# check_mode obj_type: database
+- name: postgresql_owner - set db owner in check_mode
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_owner:
+ login_user: '{{ pg_user }}'
+ db: acme
+ new_owner: bob
+ obj_name: acme
+ obj_type: database
+ check_mode: true
+ register: result
+
+- assert:
+ that:
+ - result is changed
+ - result.queries == ['ALTER DATABASE "acme" OWNER TO "bob"']
+
+- name: postgresql_owner - check that nothing changed after the previous step
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ db: acme
+ login_user: '{{ pg_user }}'
+ query: >
+ SELECT 1 FROM pg_database AS d JOIN pg_roles AS r
+ ON d.datdba = r.oid WHERE d.datname = 'acme' AND r.rolname = 'bob'
+ ignore_errors: true
+ register: result
+
+- assert:
+ that:
+ - result.rowcount == 0
+
+- name: postgresql_owner - set db owner
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_owner:
+ login_user: '{{ pg_user }}'
+ db: acme
+ new_owner: bob
+ obj_name: acme
+ obj_type: database
+ register: result
+
+- assert:
+ that:
+ - result is changed
+ - result.queries == ['ALTER DATABASE "acme" OWNER TO "bob"']
+
+- name: postgresql_owner - check that db owner has been changed after the previous step
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ db: acme
+ login_user: '{{ pg_user }}'
+ query: >
+ SELECT 1 FROM pg_database AS d JOIN pg_roles AS r
+ ON d.datdba = r.oid WHERE d.datname = 'acme' AND r.rolname = 'bob'
+ ignore_errors: true
+ register: result
+
+- assert:
+ that:
+ - result.rowcount == 1
+
+- name: postgresql_owner - set db owner again
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_owner:
+ login_user: '{{ pg_user }}'
+ db: acme
+ new_owner: bob
+ obj_name: acme
+ obj_type: database
+ register: result
+
+- assert:
+ that:
+ - result is not changed
+ - result.queries == []
+
+- name: postgresql_owner - check that db owner is bob
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ db: acme
+ login_user: '{{ pg_user }}'
+ query: >
+ SELECT 1 FROM pg_database AS d JOIN pg_roles AS r
+ ON d.datdba = r.oid WHERE d.datname = 'acme' AND r.rolname = 'bob'
+ ignore_errors: true
+ register: result
+
+- assert:
+ that:
+ - result.rowcount == 1
+
+- name: postgresql_owner - set table owner in check_mode
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_owner:
+ login_user: '{{ pg_user }}'
+ db: acme
+ new_owner: bob
+ obj_name: my_table
+ obj_type: table
+ check_mode: true
+ register: result
+
+- assert:
+ that:
+ - result is changed
+ - result.queries == ['ALTER TABLE "my_table" OWNER TO "bob"']
+
+- name: postgresql_owner - check that nothing changed after the previous step
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ db: acme
+ login_user: '{{ pg_user }}'
+ query: >
+ SELECT 1 FROM pg_tables WHERE tablename = 'my_table'
+ AND tableowner = 'bob'
+ ignore_errors: true
+ register: result
+
+- assert:
+ that:
+ - result.rowcount == 0
+
+- name: postgresql_owner - set db owner
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_owner:
+ login_user: '{{ pg_user }}'
+ db: acme
+ new_owner: bob
+ obj_name: my_table
+ obj_type: table
+ register: result
+
+- assert:
+ that:
+ - result is changed
+ - result.queries == ['ALTER TABLE "my_table" OWNER TO "bob"']
+
+- name: postgresql_owner - check that table owner has been changed after the previous step
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ db: acme
+ login_user: '{{ pg_user }}'
+ query: >
+ SELECT 1 FROM pg_tables WHERE tablename = 'my_table'
+ AND tableowner = 'bob'
+ ignore_errors: true
+ register: result
+
+- assert:
+ that:
+ - result.rowcount == 1
+
+- name: postgresql_owner - set db owner again
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_owner:
+ login_user: '{{ pg_user }}'
+ db: acme
+ new_owner: bob
+ obj_name: my_table
+ obj_type: table
+ register: result
+
+- assert:
+ that:
+ - result is not changed
+ - result.queries == []
+
+- name: postgresql_owner - check that table owner is bob
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ db: acme
+ login_user: '{{ pg_user }}'
+ query: >
+ SELECT 1 FROM pg_tables WHERE tablename = 'my_table'
+ AND tableowner = 'bob'
+ ignore_errors: true
+ register: result
+
+- assert:
+ that:
+ - result.rowcount == 1
+
+- name: postgresql_owner - set sequence owner in check_mode
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_owner:
+ login_user: '{{ pg_user }}'
+ db: acme
+ new_owner: bob
+ obj_name: test_seq
+ obj_type: sequence
+ check_mode: true
+ register: result
+
+- assert:
+ that:
+ - result is changed
+ - result.queries == ['ALTER SEQUENCE "test_seq" OWNER TO "bob"']
+
+- name: postgresql_owner - check that nothing changed after the previous step
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ db: acme
+ login_user: '{{ pg_user }}'
+ query: >
+ SELECT 1 FROM pg_class AS c JOIN pg_roles AS r
+ ON c.relowner = r.oid WHERE c.relkind = 'S'
+ AND c.relname = 'test_seq' AND r.rolname = 'bob'
+ ignore_errors: true
+ register: result
+
+- assert:
+ that:
+ - result.rowcount == 0
+
+- name: postgresql_owner - set db owner
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_owner:
+ login_user: '{{ pg_user }}'
+ db: acme
+ new_owner: bob
+ obj_name: test_seq
+ obj_type: sequence
+ register: result
+
+- assert:
+ that:
+ - result is changed
+ - result.queries == ['ALTER SEQUENCE "test_seq" OWNER TO "bob"']
+
+- name: postgresql_owner - check that table owner has been changed after the previous step
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ db: acme
+ login_user: '{{ pg_user }}'
+ query: >
+ SELECT 1 FROM pg_class AS c JOIN pg_roles AS r
+ ON c.relowner = r.oid WHERE c.relkind = 'S'
+ AND c.relname = 'test_seq' AND r.rolname = 'bob'
+ ignore_errors: true
+ register: result
+
+- assert:
+ that:
+ - result.rowcount == 1
+
+- name: postgresql_owner - set db owner again
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_owner:
+ login_user: '{{ pg_user }}'
+ db: acme
+ new_owner: bob
+ obj_name: test_seq
+ obj_type: sequence
+ register: result
+
+- assert:
+ that:
+ - result is not changed
+ - result.queries == []
+
+- name: postgresql_owner - check that sequence owner is bob
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ db: acme
+ login_user: '{{ pg_user }}'
+ query: >
+ SELECT 1 FROM pg_class AS c JOIN pg_roles AS r
+ ON c.relowner = r.oid WHERE c.relkind = 'S'
+ AND c.relname = 'test_seq' AND r.rolname = 'bob'
+ ignore_errors: true
+ register: result
+
+- assert:
+ that:
+ - result.rowcount == 1
+
+- name: postgresql_owner - set function owner in check_mode
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_owner:
+ login_user: '{{ pg_user }}'
+ db: acme
+ new_owner: bob
+ obj_name: increment
+ obj_type: function
+ check_mode: true
+ register: result
+ when: postgres_version_resp.stdout is version('10', '>=')
+
+- assert:
+ that:
+ - result is changed
+ - result.queries == ['ALTER FUNCTION increment OWNER TO "bob"']
+ when: postgres_version_resp.stdout is version('10', '>=')
+
+- name: postgresql_owner - check that nothing changed after the previous step
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ db: acme
+ login_user: '{{ pg_user }}'
+ query: >
+ SELECT 1 FROM pg_proc AS f JOIN pg_roles AS r
+ ON f.proowner = r.oid WHERE f.proname = 'increment' AND r.rolname = 'bob'
+ ignore_errors: true
+ register: result
+ when: postgres_version_resp.stdout is version('10', '>=')
+
+- assert:
+ that:
+ - result.rowcount == 0
+ when: postgres_version_resp.stdout is version('10', '>=')
+
+- name: postgresql_owner - set func owner
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_owner:
+ login_user: '{{ pg_user }}'
+ db: acme
+ new_owner: bob
+ obj_name: increment
+ obj_type: function
+ register: result
+ when: postgres_version_resp.stdout is version('10', '>=')
+
+- assert:
+ that:
+ - result is changed
+ - result.queries == ['ALTER FUNCTION increment OWNER TO "bob"']
+ when: postgres_version_resp.stdout is version('10', '>=')
+
+- name: postgresql_owner - check that func owner has been changed after the previous step
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ db: acme
+ login_user: '{{ pg_user }}'
+ query: >
+ SELECT 1 FROM pg_proc AS f JOIN pg_roles AS r
+ ON f.proowner = r.oid WHERE f.proname = 'increment' AND r.rolname = 'bob'
+ ignore_errors: true
+ register: result
+ when: postgres_version_resp.stdout is version('10', '>=')
+
+- assert:
+ that:
+ - result.rowcount == 1
+ when: postgres_version_resp.stdout is version('10', '>=')
+
+- name: postgresql_owner - set func owner again
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_owner:
+ login_user: '{{ pg_user }}'
+ db: acme
+ new_owner: bob
+ obj_name: increment
+ obj_type: function
+ register: result
+ when: postgres_version_resp.stdout is version('10', '>=')
+
+- assert:
+ that:
+ - result is not changed
+ - result.queries == []
+ when: postgres_version_resp.stdout is version('10', '>=')
+
+- name: postgresql_owner - check that function owner is bob
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ db: acme
+ login_user: '{{ pg_user }}'
+ query: >
+ SELECT 1 FROM pg_proc AS f JOIN pg_roles AS r
+ ON f.proowner = r.oid WHERE f.proname = 'increment' AND r.rolname = 'bob'
+ ignore_errors: true
+ register: result
+ when: postgres_version_resp.stdout is version('10', '>=')
+
+- assert:
+ that:
+ - result.rowcount == 1
+ when: postgres_version_resp.stdout is version('10', '>=')
+
+- name: postgresql_owner - set schema owner in check_mode
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_owner:
+ login_user: '{{ pg_user }}'
+ db: acme
+ new_owner: bob
+ obj_name: test_schema
+ obj_type: schema
+ check_mode: true
+ register: result
+
+- assert:
+ that:
+ - result is changed
+ - result.queries == ['ALTER SCHEMA "test_schema" OWNER TO "bob"']
+
+- name: postgresql_owner - check that nothing changed after the previous step
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ db: acme
+ login_user: '{{ pg_user }}'
+ query: >
+ SELECT 1 FROM information_schema.schemata
+ WHERE schema_name = 'test_schema' AND schema_owner = 'bob'
+ ignore_errors: true
+ register: result
+
+- assert:
+ that:
+ - result.rowcount == 0
+
+- name: postgresql_owner - set schema owner
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_owner:
+ login_user: '{{ pg_user }}'
+ db: acme
+ new_owner: bob
+ obj_name: test_schema
+ obj_type: schema
+ register: result
+
+- assert:
+ that:
+ - result is changed
+ - result.queries == ['ALTER SCHEMA "test_schema" OWNER TO "bob"']
+
+- name: postgresql_owner - check that schema owner has been changed after the previous step
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ db: acme
+ login_user: '{{ pg_user }}'
+ query: >
+ SELECT 1 FROM information_schema.schemata
+ WHERE schema_name = 'test_schema' AND schema_owner = 'bob'
+ ignore_errors: true
+ register: result
+
+- assert:
+ that:
+ - result.rowcount == 1
+
+- name: postgresql_owner - set schema owner again
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_owner:
+ login_user: '{{ pg_user }}'
+ db: acme
+ new_owner: bob
+ obj_name: test_seq
+ obj_type: sequence
+ register: result
+
+- assert:
+ that:
+ - result is not changed
+ - result.queries == []
+
+- name: postgresql_owner - check that schema owner is bob
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ db: acme
+ login_user: '{{ pg_user }}'
+ query: >
+ SELECT 1 FROM information_schema.schemata
+ WHERE schema_name = 'test_schema' AND schema_owner = 'bob'
+ ignore_errors: true
+ register: result
+
+- assert:
+ that:
+ - result.rowcount == 1
+
+- name: postgresql_owner - set view owner in check_mode
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_owner:
+ login_user: '{{ pg_user }}'
+ db: acme
+ new_owner: bob
+ obj_name: test_view
+ obj_type: view
+ check_mode: true
+ register: result
+
+- assert:
+ that:
+ - result is changed
+ - result.queries == ['ALTER VIEW "test_view" OWNER TO "bob"']
+
+- name: postgresql_owner - check that nothing changed after the previous step
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ db: acme
+ login_user: '{{ pg_user }}'
+ query: SELECT 1 FROM pg_views WHERE viewname = 'test_view' AND viewowner = 'bob'
+ ignore_errors: true
+ register: result
+
+- assert:
+ that:
+ - result.rowcount == 0
+
+- name: postgresql_owner - set view owner
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_owner:
+ login_user: '{{ pg_user }}'
+ db: acme
+ new_owner: bob
+ obj_name: test_view
+ obj_type: view
+ register: result
+
+- assert:
+ that:
+ - result is changed
+ - result.queries == ['ALTER VIEW "test_view" OWNER TO "bob"']
+
+- name: postgresql_owner - check that view owner has been changed after the previous step
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ db: acme
+ login_user: '{{ pg_user }}'
+ query: SELECT 1 FROM pg_views WHERE viewname = 'test_view' AND viewowner = 'bob'
+ ignore_errors: true
+ register: result
+
+- assert:
+ that:
+ - result.rowcount == 1
+
+- name: postgresql_owner - set view owner again
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_owner:
+ login_user: '{{ pg_user }}'
+ db: acme
+ new_owner: bob
+ obj_name: test_view
+ obj_type: view
+ register: result
+
+- assert:
+ that:
+ - result is not changed
+ - result.queries == []
+
+- name: postgresql_owner - check that view owner is bob
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ db: acme
+ login_user: '{{ pg_user }}'
+ query: SELECT 1 FROM pg_views WHERE viewname = 'test_view' AND viewowner = 'bob'
+ ignore_errors: true
+ register: result
+
+- assert:
+ that:
+ - result.rowcount == 1
+
+- name: postgresql_owner - set matview owner in check_mode
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_owner:
+ login_user: '{{ pg_user }}'
+ db: acme
+ new_owner: bob
+ obj_name: test_mat_view
+ obj_type: matview
+ check_mode: true
+ register: result
+ when: postgres_version_resp.stdout is version('9.4', '>=')
+
+- assert:
+ that:
+ - result is changed
+ - result.queries == ['ALTER MATERIALIZED VIEW "test_mat_view" OWNER TO "bob"']
+ when: postgres_version_resp.stdout is version('9.4', '>=')
+
+- name: postgresql_owner - check that nothing changed after the previous step
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ db: acme
+ login_user: '{{ pg_user }}'
+ query: SELECT 1 FROM pg_matviews WHERE matviewname = 'test_view' AND matviewowner = 'bob'
+ ignore_errors: true
+ register: result
+ when: postgres_version_resp.stdout is version('9.4', '>=')
+
+- assert:
+ that:
+ - result.rowcount == 0
+ when: postgres_version_resp.stdout is version('9.4', '>=')
+
+- name: postgresql_owner - set matview owner
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_owner:
+ login_user: '{{ pg_user }}'
+ db: acme
+ new_owner: bob
+ obj_name: test_mat_view
+ obj_type: matview
+ register: result
+ when: postgres_version_resp.stdout is version('9.4', '>=')
+
+- assert:
+ that:
+ - result is changed
+ - result.queries == ['ALTER MATERIALIZED VIEW "test_mat_view" OWNER TO "bob"']
+ when: postgres_version_resp.stdout is version('9.4', '>=')
+
+- name: postgresql_owner - check that matview owner has been changed after the previous step
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ db: acme
+ login_user: '{{ pg_user }}'
+ query: SELECT 1 FROM pg_matviews WHERE matviewname = 'test_mat_view' AND matviewowner = 'bob'
+ ignore_errors: true
+ register: result
+ when: postgres_version_resp.stdout is version('9.4', '>=')
+
+- assert:
+ that:
+ - result.rowcount == 1
+ when: postgres_version_resp.stdout is version('9.4', '>=')
+
+- name: postgresql_owner - set matview owner again
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_owner:
+ login_user: '{{ pg_user }}'
+ db: acme
+ new_owner: bob
+ obj_name: test_mat_view
+ obj_type: matview
+ register: result
+ when: postgres_version_resp.stdout is version('9.4', '>=')
+
+- assert:
+ that:
+ - result is not changed
+ - result.queries == []
+ when: postgres_version_resp.stdout is version('9.4', '>=')
+
+- name: postgresql_owner - check that matview owner is bob
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ db: acme
+ login_user: '{{ pg_user }}'
+ query: SELECT 1 FROM pg_matviews WHERE matviewname = 'test_mat_view' AND matviewowner = 'bob'
+ ignore_errors: true
+ register: result
+ when: postgres_version_resp.stdout is version('9.4', '>=')
+
+- assert:
+ that:
+ - result.rowcount == 1
+ when: postgres_version_resp.stdout is version('9.4', '>=')
+
+- name: postgresql_owner - set tablespace owner in check_mode
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_owner:
+ login_user: '{{ pg_user }}'
+ db: acme
+ new_owner: bob
+ obj_name: acme
+ obj_type: tablespace
+ check_mode: true
+ register: result
+
+- assert:
+ that:
+ - result is changed
+ - result.queries == ['ALTER TABLESPACE "acme" OWNER TO "bob"']
+
+- name: postgresql_owner - check that nothing changed after the previous step
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ db: acme
+ login_user: '{{ pg_user }}'
+ query: >
+ SELECT 1 FROM pg_tablespace AS t JOIN pg_roles AS r
+ ON t.spcowner = r.oid WHERE t.spcname = 'acme' AND r.rolname = 'bob'
+ ignore_errors: true
+ register: result
+
+- assert:
+ that:
+ - result.rowcount == 0
+
+- name: postgresql_owner - set tablespace owner
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_owner:
+ login_user: '{{ pg_user }}'
+ db: acme
+ new_owner: bob
+ obj_name: acme
+ obj_type: tablespace
+ register: result
+
+- assert:
+ that:
+ - result is changed
+ - result.queries == ['ALTER TABLESPACE "acme" OWNER TO "bob"']
+
+- name: postgresql_owner - check that tablespace owner has been changed after the previous step
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ db: acme
+ login_user: '{{ pg_user }}'
+ query: >
+ SELECT 1 FROM pg_tablespace AS t JOIN pg_roles AS r
+ ON t.spcowner = r.oid WHERE t.spcname = 'acme' AND r.rolname = 'bob'
+ ignore_errors: true
+ register: result
+
+- assert:
+ that:
+ - result.rowcount == 1
+
+- name: postgresql_owner - set tablespace owner again
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_owner:
+ login_user: '{{ pg_user }}'
+ db: acme
+ new_owner: bob
+ obj_name: acme
+ obj_type: tablespace
+ register: result
+
+- assert:
+ that:
+ - result is not changed
+ - result.queries == []
+
+- name: postgresql_owner - check that tablespace owner is bob
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ db: acme
+ login_user: '{{ pg_user }}'
+ query: >
+ SELECT 1 FROM pg_tablespace AS t JOIN pg_roles AS r
+ ON t.spcowner = r.oid WHERE t.spcname = 'acme' AND r.rolname = 'bob'
+ ignore_errors: true
+ register: result
+
+- assert:
+ that:
+ - result.rowcount == 1
+
+#
+# Crean up
+#
+- name: postgresql_owner - drop test database
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_db:
+ login_user: '{{ pg_user }}'
+ db: acme
+ state: absent
+
+- name: postgresql_owner - drop test tablespace
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_tablespace:
+ db: postgres
+ login_user: '{{ pg_user }}'
+ name: acme
+ state: absent
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_pg_hba/aliases b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_pg_hba/aliases
new file mode 100644
index 000000000..a4c92ef85
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_pg_hba/aliases
@@ -0,0 +1,2 @@
+destructive
+shippable/posix/group1
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_pg_hba/defaults/main.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_pg_hba/defaults/main.yml
new file mode 100644
index 000000000..94df93f9e
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_pg_hba/defaults/main.yml
@@ -0,0 +1,29 @@
+---
+pg_hba_test_ips:
+- contype: local
+ users: 'all,postgres,test'
+- source: '0000:ffff::'
+ netmask: 'ffff:fff0::'
+- source: '192.168.0.0/24'
+ netmask: ''
+ databases: 'all,replication'
+- source: '192.168.1.0/24'
+ netmask: ''
+ databases: 'all'
+ method: reject
+- source: '127.0.0.1/32'
+ netmask: ''
+- source: '::1/128'
+ netmask: ''
+- source: '0000:ff00::'
+ netmask: 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ff00'
+ method: scram-sha-256
+- source: '172.16.0.0'
+ netmask: '255.255.0.0'
+ method: trust
+- contype: hostgssenc
+ users: postgres
+ source: '2001:db8::1/128'
+- contype: hostnogssenc
+ users: all
+ source: '2001:db8::1/128'
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_pg_hba/meta/main.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_pg_hba/meta/main.yml
new file mode 100644
index 000000000..4ce5a5837
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_pg_hba/meta/main.yml
@@ -0,0 +1,2 @@
+dependencies:
+ - setup_postgresql_db
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_pg_hba/tasks/main.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_pg_hba/tasks/main.yml
new file mode 100644
index 000000000..af51cbca9
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_pg_hba/tasks/main.yml
@@ -0,0 +1,8 @@
+####################################################################
+# WARNING: These are designed specifically for Ansible tests #
+# and should not be used as examples of how to write Ansible roles #
+####################################################################
+
+# Initial CI tests of postgresql_pg_hba module
+- import_tasks: postgresql_pg_hba_initial.yml
+- import_tasks: postgresql_pg_hba_bulk_rules.yml
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_pg_hba/tasks/postgresql_pg_hba_bulk_rules.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_pg_hba/tasks/postgresql_pg_hba_bulk_rules.yml
new file mode 100644
index 000000000..4363f6dc9
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_pg_hba/tasks/postgresql_pg_hba_bulk_rules.yml
@@ -0,0 +1,136 @@
+- name: set test variables
+ set_fact:
+ pghba_defaults: &pghba_defaults
+ create: true
+ dest: "/tmp/pg_hba_bulk_test.conf"
+ test_rule0: &test_rule0
+ contype: host
+ databases: "db0"
+ users: "user0"
+ address: "2001:db8::0/128"
+ method: pam
+ test_rule1: &test_rule1
+ contype: host
+ databases: "db1"
+ users: "user1"
+ address: "2001:db8::1/128"
+ method: pam
+ test_rule2: &test_rule2
+ contype: host
+ databases: "db2"
+ users: "user2"
+ address: "2001:db8::2/128"
+ method: pam
+
+- name: create one rule to clear
+ community.postgresql.postgresql_pg_hba:
+ <<: *pghba_defaults
+ state: "present"
+ <<: *test_rule0
+- name: overwrite with one normal rule
+ community.postgresql.postgresql_pg_hba:
+ <<: *pghba_defaults
+ overwrite: true
+ <<: *test_rule1
+ register: result
+- assert:
+ that:
+ - "result.pg_hba|length == 1"
+ - "result.pg_hba[0].db == test_rule1.databases"
+ - "result.pg_hba[0].src == test_rule1.address"
+ - "result.pg_hba[0].usr == test_rule1.users"
+ - "result.pg_hba[0].type == test_rule1.contype"
+- name: 'test the same again (overwrite: true, one normal rule) to ensure nothing changed'
+ community.postgresql.postgresql_pg_hba:
+ <<: *pghba_defaults
+ overwrite: true
+ <<: *test_rule1
+ register: result
+- assert:
+ that:
+ - "result.changed == false"
+
+- name: overwrite with one bulk rule
+ community.postgresql.postgresql_pg_hba:
+ <<: *pghba_defaults
+ overwrite: true
+ rules:
+ - "{{ test_rule2 }}"
+ register: result
+- assert:
+ that:
+ - "result.pg_hba|length == 1"
+ - "result.pg_hba[0].db == test_rule2.databases"
+ - "result.pg_hba[0].src == test_rule2.address"
+ - "result.pg_hba[0].usr == test_rule2.users"
+ - "result.pg_hba[0].type == test_rule2.contype"
+- name: 'test the same again (overwrite: true, one bulk rule) to ensure nothing changes'
+ community.postgresql.postgresql_pg_hba:
+ <<: *pghba_defaults
+ overwrite: true
+ rules:
+ - "{{ test_rule2 }}"
+ register: result
+- assert:
+ that:
+ - "result.changed == false"
+
+- name: test rules_behavior conflict
+ community.postgresql.postgresql_pg_hba: "{{ pghba_defaults|combine(item)|combine({'rules': [test_rule2]}) }}"
+ loop:
+ - address: 2001:db8::a/128
+ - comment: 'testcomment'
+ - contype: hostssl
+ - databases: db_a
+ - method: cert
+ - netmask: 255.255.255.0
+ # address: 192.0.2.0
+ - options: "clientcert=verify-full"
+ - state: absent
+ - users: testuser
+ register: result
+ ignore_errors: true
+- name: get jinja2 version
+ shell: '/usr/bin/pip --disable-pip-version-check --no-cache-dir show Jinja2 2>/dev/null | grep -oPm 1 "(?<=^Version: )\d+\.\d+"'
+ register: jinja2_version
+ ignore_errors: true
+- assert:
+ that:
+ - result.failed
+ - not result.changed
+ - "result.results|selectattr('changed')|length == 0"
+ - "result.results|rejectattr('failed')|length == 0"
+ # the 'in' test was added in jinja 2.10
+ - "jinja2_version.rc == 0 and jinja2_version.stdout|trim is version('2.10', '<') or result.results|selectattr('msg', 'in', 'conflict')|length == 0"
+
+- name: test rules with module defaults
+ community.postgresql.postgresql_pg_hba:
+ <<: *pghba_defaults
+ rules:
+ - contype: hostssl
+ register: result
+- assert:
+ that:
+ - result.changed
+ # assert that module defaults are used
+ - "{'db': 'all', 'method': 'md5', 'src': 'samehost', 'type': 'hostssl', 'usr': 'all'} in result.pg_hba"
+
+- name: test rules with custom defaults
+ community.postgresql.postgresql_pg_hba:
+ <<: *pghba_defaults
+ rules_behavior: combine
+ <<: *test_rule1
+ rules:
+ - {} # complete fallback to custom defaults
+ - databases: other_db # partial fallback to custom defaults
+ # no fallback
+ - <<: *test_rule2
+ state: absent
+ register: result
+- assert:
+ that:
+ - result.changed
+ - "{'db': 'all', 'method': 'md5', 'src': 'samehost', 'type': 'hostssl', 'usr': 'all'} in result.pg_hba" # unchanged preexisting from previous task
+ - "{'db': test_rule1.databases, 'method': test_rule1.method, 'src': test_rule1.address, 'type': test_rule1.contype, 'usr': test_rule1.users} in result.pg_hba"
+ - "{'db': test_rule2.databases, 'method': test_rule2.method, 'src': test_rule2.address, 'type': test_rule2.contype, 'usr': test_rule2.users} not in result.pg_hba"
+ - "{'db': 'other_db', 'method': test_rule1.method, 'src': test_rule1.address, 'type': test_rule1.contype, 'usr': test_rule1.users} in result.pg_hba"
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_pg_hba/tasks/postgresql_pg_hba_initial.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_pg_hba/tasks/postgresql_pg_hba_initial.yml
new file mode 100644
index 000000000..2a9505a5b
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_pg_hba/tasks/postgresql_pg_hba_initial.yml
@@ -0,0 +1,264 @@
+- name: Make sure file does not exist
+ file:
+ dest: /tmp/pg_hba.conf
+ state: absent
+
+- name: check_mode run
+ postgresql_pg_hba:
+ dest: /tmp/pg_hba.conf
+ contype: host
+ source: '0000:ffff::'
+ netmask: 'ffff:fff0::'
+ method: md5
+ backup: 'True'
+ order: sud
+ state: "{{item}}"
+ check_mode: true
+ with_items:
+ - present
+ - absent
+
+- name: check_mode check
+ stat:
+ path: /tmp/pg_hba.conf
+ register: pg_hba_checkmode_check
+
+- name: Remove several ip addresses for idempotency check
+ postgresql_pg_hba:
+ contype: "{{item.contype|default('host')}}"
+ databases: "{{item.databases|default('all')}}"
+ dest: /tmp/pg_hba.conf
+ method: "{{item.method|default('md5')}}"
+ netmask: "{{item.netmask|default('')}}"
+ order: sud
+ source: "{{item.source|default('')}}"
+ state: absent
+ users: "{{item.users|default('all')}}"
+ with_items: "{{pg_hba_test_ips}}"
+ register: pg_hba_idempotency_check1
+
+- name: idempotency not creating file check
+ stat:
+ path: /tmp/pg_hba.conf
+ register: pg_hba_idempotency_file_check
+
+- name: Add several ip addresses
+ postgresql_pg_hba:
+ backup: 'True'
+ contype: "{{item.contype|default('host')}}"
+ create: 'True'
+ databases: "{{item.databases|default('all')}}"
+ dest: /tmp/pg_hba.conf
+ method: "{{item.method|default('md5')}}"
+ netmask: "{{item.netmask|default('')}}"
+ order: sud
+ source: "{{item.source|default('')}}"
+ state: present
+ users: "{{item.users|default('all')}}"
+ register: pg_hba_change
+ with_items: "{{pg_hba_test_ips}}"
+
+- name: Able to add options on rule without
+ postgresql_pg_hba:
+ dest: "/tmp/pg_hba.conf"
+ users: "+some"
+ order: "sud"
+ state: "present"
+ contype: "local"
+ method: "cert"
+ options: "{{ item }}"
+ address: ""
+ with_items:
+ - ""
+ - "clientcert=1"
+
+- name: Retain options even if they contain spaces
+ postgresql_pg_hba:
+ dest: "/tmp/pg_hba.conf"
+ users: "+some"
+ order: "sud"
+ state: "present"
+ contype: "{{ item.contype }}"
+ method: "{{ item.method }}"
+ options: "{{ item.options }}"
+ address: "{{ item.address }}"
+ with_items:
+ - { address: "", contype: "local", method: "ldap", options: "ldapserver=example.com ldapport=389 ldapprefix=\"cn=\"" }
+ - { address: "red", contype: "hostssl", method: "cert", options: "clientcert=1 map=mymap" }
+ - { address: "blue", contype: "hostssl", method: "cert", options: "clientcert=1 map=mymap" }
+ register: pg_hba_options
+
+- name: read pg_hba rules
+ postgresql_pg_hba:
+ dest: /tmp/pg_hba.conf
+ register: pg_hba
+
+- name: Add several ip addresses again for idempotency check
+ postgresql_pg_hba:
+ contype: "{{item.contype|default('host')}}"
+ databases: "{{item.databases|default('all')}}"
+ dest: /tmp/pg_hba.conf
+ method: "{{item.method|default('md5')}}"
+ netmask: "{{item.netmask|default('')}}"
+ order: sud
+ source: "{{item.source|default('')}}"
+ state: present
+ users: "{{item.users|default('all')}}"
+ with_items: "{{pg_hba_test_ips}}"
+ register: pg_hba_idempotency_check2
+
+- name: pre-backup stat
+ stat:
+ path: /tmp/pg_hba.conf
+ register: prebackupstat
+
+- name: Add new ip address for backup check and netmask_sameas_prefix check
+ postgresql_pg_hba:
+ backup: 'True'
+ contype: host
+ dest: /tmp/pg_hba.conf
+ method: md5
+ netmask: 255.255.255.0
+ order: sud
+ source: '172.21.0.0'
+ state: present
+ register: pg_hba_backup_check2
+
+- name: Add new ip address for netmask_sameas_prefix check
+ postgresql_pg_hba:
+ backup: 'True'
+ contype: host
+ dest: /tmp/pg_hba.conf
+ method: md5
+ order: sud
+ source: '172.21.0.0/24'
+ state: present
+ register: netmask_sameas_prefix_check
+
+- name: post-backup stat
+ stat:
+ path: "{{pg_hba_backup_check2.backup_file}}"
+ register: postbackupstat
+
+- name: Dont allow netmask for src in [all, samehost, samenet]
+ postgresql_pg_hba:
+ contype: host
+ dest: /tmp/pg_hba.conf
+ method: md5
+ netmask: '255.255.255.255'
+ order: sud
+ source: all
+ state: present
+ register: pg_hba_fail_src_all_with_netmask
+ ignore_errors: true
+
+- debug:
+ var: pg_hba.pg_hba
+- assert:
+ that:
+ - 'pg_hba.pg_hba == [
+ { "db": "all", "method": "ldap", "type": "local", "usr": "+some", "options": "ldapserver=example.com ldapport=389 ldapprefix=\"cn=\"" },
+ { "db": "all", "method": "md5", "type": "local", "usr": "postgres" },
+ { "db": "all", "method": "md5", "type": "local", "usr": "test" },
+ { "db": "all", "method": "md5", "type": "local", "usr": "all" },
+ { "db": "all", "method": "md5", "src": "2001:db8::1/128", "type": "hostgssenc", "usr": "postgres" },
+ { "db": "all", "method": "cert", "src": "blue", "type": "hostssl", "usr": "+some", "options": "clientcert=1 map=mymap" },
+ { "db": "all", "method": "cert", "src": "red", "type": "hostssl", "usr": "+some", "options": "clientcert=1 map=mymap" },
+ { "db": "all", "method": "md5", "src": "127.0.0.1/32", "type": "host", "usr": "all" },
+ { "db": "all", "method": "md5", "src": "2001:db8::1/128", "type": "hostnogssenc", "usr": "all" },
+ { "db": "all", "method": "md5", "src": "::1/128", "type": "host", "usr": "all" },
+ { "db": "all", "method": "scram-sha-256", "src": "0:ff00::/120", "type": "host", "usr": "all" },
+ { "db": "replication", "method": "md5", "src": "192.168.0.0/24", "type": "host", "usr": "all" },
+ { "db": "all", "method": "md5", "src": "192.168.0.0/24", "type": "host", "usr": "all" },
+ { "db": "all", "method": "reject", "src": "192.168.1.0/24", "type": "host", "usr": "all" },
+ { "db": "all", "method": "trust", "src": "172.16.0.0/16", "type": "host", "usr": "all" },
+ { "db": "all", "method": "md5", "src": "0:fff0::/28", "type": "host", "usr": "all" }
+ ]'
+ - 'pg_hba_change is changed'
+ - 'pg_hba_checkmode_check.stat.exists == false'
+ - 'not pg_hba_idempotency_check1 is changed'
+ - 'not pg_hba_idempotency_check2 is changed'
+ - 'pg_hba_idempotency_file_check.stat.exists == false'
+ - 'prebackupstat.stat.checksum == postbackupstat.stat.checksum'
+ - 'pg_hba_fail_src_all_with_netmask is failed'
+ - 'not netmask_sameas_prefix_check is changed'
+ - 'pg_hba_options is changed'
+
+- name: ensure test file is empty
+ copy:
+ content: ''
+ dest: /tmp/pg_hba2.conf
+
+- name: Create a rule with the comment 'comment1'
+ postgresql_pg_hba:
+ contype: host
+ dest: /tmp/pg_hba2.conf
+ create: true
+ method: md5
+ address: "2001:db8::1/128"
+ order: sud
+ state: present
+ comment: "comment1"
+
+- name: Fetch the file
+ fetch:
+ src: /tmp/pg_hba2.conf
+ dest: /tmp/pg_hba2.conf
+ flat: true
+- name: Read pg_hba2.conf
+ set_fact:
+ content: "{{ lookup('file', '/tmp/pg_hba2.conf') }}"
+- debug:
+ var: content
+- assert:
+ that:
+ - '"\nhost\tall\tall\t2001:db8::1/128\tmd5\t#comment1" == content'
+
+- name: Create a rule with the comment 'comment2'
+ postgresql_pg_hba:
+ contype: host
+ dest: /tmp/pg_hba2.conf
+ method: md5
+ address: "2001:db8::2/128"
+ order: sud
+ state: present
+ comment: "comment2"
+
+- name: Fetch the file
+ fetch:
+ src: /tmp/pg_hba2.conf
+ dest: /tmp/pg_hba2.conf
+ flat: true
+- name: Read pg_hba2.conf
+ set_fact:
+ content: "{{ lookup('file', '/tmp/pg_hba2.conf') }}"
+- debug:
+ var: content
+- assert:
+ that:
+ - '"#comment1\nhost\tall\tall\t2001:db8::1/128\tmd5\nhost\tall\tall\t2001:db8::2/128\tmd5\t#comment2" == content'
+
+- name: Create a rule with the comment 'comment3' and keep_comments_at_rules
+ postgresql_pg_hba:
+ contype: host
+ dest: /tmp/pg_hba2.conf
+ method: md5
+ address: "2001:db8::3/128"
+ order: sud
+ state: present
+ comment: "comment3"
+ keep_comments_at_rules: true
+
+- name: Fetch the file
+ fetch:
+ src: /tmp/pg_hba2.conf
+ dest: /tmp/pg_hba2.conf
+ flat: true
+- name: Read pg_hba2.conf
+ set_fact:
+ content: "{{ lookup('file', '/tmp/pg_hba2.conf') }}"
+- debug:
+ var: content
+- assert:
+ that:
+ - '"#comment1\nhost\tall\tall\t2001:db8::1/128\tmd5\nhost\tall\tall\t2001:db8::2/128\tmd5\t#comment2\nhost\tall\tall\t2001:db8::3/128\tmd5\t#comment3" == content'
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_ping/aliases b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_ping/aliases
new file mode 100644
index 000000000..a4c92ef85
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_ping/aliases
@@ -0,0 +1,2 @@
+destructive
+shippable/posix/group1
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_ping/defaults/main.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_ping/defaults/main.yml
new file mode 100644
index 000000000..73eb55ae2
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_ping/defaults/main.yml
@@ -0,0 +1,2 @@
+---
+db_default: postgres
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_ping/meta/main.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_ping/meta/main.yml
new file mode 100644
index 000000000..4ce5a5837
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_ping/meta/main.yml
@@ -0,0 +1,2 @@
+dependencies:
+ - setup_postgresql_db
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_ping/tasks/main.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_ping/tasks/main.yml
new file mode 100644
index 000000000..bcb18d2fe
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_ping/tasks/main.yml
@@ -0,0 +1,9 @@
+####################################################################
+# WARNING: These are designed specifically for Ansible tests #
+# and should not be used as examples of how to write Ansible roles #
+####################################################################
+
+# Initial CI tests of postgresql_ping module
+- import_tasks: postgresql_ping_initial.yml
+ vars:
+ db_name_nonexist: fake_db
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_ping/tasks/postgresql_ping_initial.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_ping/tasks/postgresql_ping_initial.yml
new file mode 100644
index 000000000..218ae9fd7
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_ping/tasks/postgresql_ping_initial.yml
@@ -0,0 +1,187 @@
+# Test code for the postgresql_ping module
+# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) <aaklychkov@mail.ru>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+- name: postgresql_ping - test return values
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_ping:
+ db: "{{ db_default }}"
+ login_user: "{{ pg_user }}"
+ register: result
+ ignore_errors: true
+
+- assert:
+ that:
+ - result.is_available == true
+ - result.server_version != {}
+ - result.server_version.raw is search('PostgreSQL')
+ - result.server_version.major != ''
+ - result.server_version.minor != ''
+ - result is not changed
+
+- assert:
+ that:
+ - result.server_version.patch != {}
+ - result.server_version.full == '{{ result.server_version.major }}.{{ result.server_version.minor }}.{{ result.server_version.patch }}'
+ when: result.server_version.major == 9
+
+- assert:
+ that:
+ - result.server_version.full == '{{ result.server_version.major }}.{{ result.server_version.minor }}'
+ when: result.server_version.major >= 10
+
+- name: postgresql_ping - check ping of non-existing database doesn't return anything
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_ping:
+ db: "{{ db_name_nonexist }}"
+ login_user: "{{ pg_user }}"
+ register: result
+ ignore_errors: true
+
+- assert:
+ that:
+ - result.is_available == false
+ - result.server_version == {}
+ - result is not changed
+
+- name: postgresql_ping - check ping of the database on non-existent port does not return anything
+ become_user: "{{ pg_user }}"
+ become: true
+ environment:
+ PGPORT: 5435
+ ignore_errors: true
+ postgresql_ping:
+ db: "{{ db_default }}"
+ login_user: "{{ pg_user }}"
+ register: result
+
+- assert:
+ that:
+ - result.is_available == false
+ - result.server_version == {}
+ - result is not changed
+
+- name: postgresql_ping - check ping of the database by a non-existent user does not return anything
+ become_user: "{{ pg_user }}"
+ become: true
+ environment:
+ PGUSER: 'test_user'
+ ignore_errors: true
+ postgresql_ping:
+ db: "{{ db_default }}"
+ register: result
+
+- assert:
+ that:
+ - result.is_available == false
+ - result.server_version == {}
+ - result is not changed
+
+- name: Creating a "test_user" in postresql
+ shell:
+ cmd: psql -U "{{ pg_user }}" -c "CREATE ROLE test_user WITH LOGIN PASSWORD 'TEST_PASSWORD';"
+
+- name: postgresql_ping - check ping of the database by a existent user
+ become_user: "{{ pg_user }}"
+ become: true
+ environment:
+ PGUSER: 'test_user'
+ ignore_errors: true
+ postgresql_ping:
+ db: "{{ db_default }}"
+ login_password: "TEST_PASSWORD"
+ register: result
+
+- assert:
+ that:
+ - result.is_available == true
+ - result.server_version != {}
+ - result.server_version.raw is search('PostgreSQL')
+ - result.server_version.major != ''
+ - result.server_version.minor != ''
+ - result is not changed
+
+- name: postgresql_ping - ping DB with SSL 1
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_ping:
+ db: "{{ ssl_db }}"
+ login_user: "{{ ssl_user }}"
+ login_password: "{{ ssl_pass }}"
+ login_host: 127.0.0.1
+ login_port: 5432
+ ssl_mode: require
+ ca_cert: '{{ ssl_rootcert }}'
+ trust_input: true
+ register: result
+ when:
+ - ansible_os_family == 'Debian'
+ - postgres_version_resp.stdout is version('9.4', '>=')
+
+- assert:
+ that:
+ - result.is_available == true
+ - result.conn_err_msg == ''
+ when:
+ - ansible_os_family == 'Debian'
+ - postgres_version_resp.stdout is version('9.4', '>=')
+
+- name: postgresql_ping - ping DB with SSL 2
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_ping:
+ db: "{{ ssl_db }}"
+ login_user: "{{ ssl_user }}"
+ login_password: "{{ ssl_pass }}"
+ login_host: 127.0.0.1
+ login_port: 5432
+ ssl_mode: verify-full
+ ca_cert: '{{ ssl_rootcert }}'
+ ssl_cert: '{{ ssl_cert }}'
+ ssl_key: '{{ ssl_key }}'
+ trust_input: true
+ register: result
+ when:
+ - ansible_os_family == 'Debian'
+ - postgres_version_resp.stdout is version('9.4', '>=')
+
+- assert:
+ that:
+ - result.is_available == true
+ - result.conn_err_msg == ''
+ when:
+ - ansible_os_family == 'Debian'
+ - postgres_version_resp.stdout is version('9.4', '>=')
+
+- name: postgresql_ping - check trust_input
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_ping:
+ db: "{{ db_default }}"
+ login_user: "{{ pg_user }}"
+ trust_input: false
+ session_role: 'curious.anonymous"; SELECT * FROM information_schema.tables; --'
+ register: result
+ ignore_errors: true
+
+- assert:
+ that:
+ - result is failed
+ - result.msg is search('is potentially dangerous')
+
+# Check conn_err_msg return value
+- name: Try to connect to non-existent DB
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_ping:
+ db: blahblah
+ login_user: "{{ pg_user }}"
+ register: result
+
+- name: Check conn_err_msg return value
+ assert:
+ that:
+ - result is succeeded
+ - result.conn_err_msg is search("database \"blahblah\" does not exist")
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_privs/aliases b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_privs/aliases
new file mode 100644
index 000000000..a4c92ef85
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_privs/aliases
@@ -0,0 +1,2 @@
+destructive
+shippable/posix/group1
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_privs/defaults/main.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_privs/defaults/main.yml
new file mode 100644
index 000000000..0a2766702
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_privs/defaults/main.yml
@@ -0,0 +1,14 @@
+db_name: ansible_db
+db_user1: ansible_db_user1
+db_user2: ansible_db_user2
+db_user3: ansible_db_user3
+db_user_with_dots1: role.with.dots1
+db_user_with_dots2: role.with.dots2
+db_name_with_hyphens: ansible-db
+db_user_with_hyphens: ansible-db-user
+db_schema_with_hyphens: ansible-db-schema
+db_schema_with_dot: test.schema
+db_schema_with_quote: 'TEST_schema"'
+db_session_role1: session_role1
+db_session_role2: session_role2
+dangerous_name: 'curious.anonymous"; SELECT * FROM information_schema.tables; --'
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_privs/meta/main.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_privs/meta/main.yml
new file mode 100644
index 000000000..4ce5a5837
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_privs/meta/main.yml
@@ -0,0 +1,2 @@
+dependencies:
+ - setup_postgresql_db
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_privs/tasks/main.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_privs/tasks/main.yml
new file mode 100644
index 000000000..cf7b63524
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_privs/tasks/main.yml
@@ -0,0 +1,19 @@
+####################################################################
+# WARNING: These are designed specifically for Ansible tests #
+# and should not be used as examples of how to write Ansible roles #
+####################################################################
+
+- include_tasks: postgresql_privs_session_role.yml
+ when: postgres_version_resp.stdout is version('9.4', '>=')
+
+# Initial CI tests of postgresql_privs module:
+- include_tasks: postgresql_privs_initial.yml
+ when: postgres_version_resp.stdout is version('9.4', '>=')
+
+# General tests:
+- include_tasks: postgresql_privs_general.yml
+ when: postgres_version_resp.stdout is version('9.4', '>=')
+
+# Tests default_privs with target_role:
+- include_tasks: test_target_role.yml
+ when: postgres_version_resp.stdout is version('9.4', '>=')
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_privs/tasks/pg_authid_not_readable.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_privs/tasks/pg_authid_not_readable.yml
new file mode 100644
index 000000000..3f810d473
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_privs/tasks/pg_authid_not_readable.yml
@@ -0,0 +1,50 @@
+- name: "Admin user is allowed to access pg_authid relation: password comparison will succeed, password won't be updated"
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_user:
+ name: "{{ db_user1 }}"
+ encrypted: 'true'
+ password: "md5{{ (db_password ~ db_user1) | hash('md5')}}"
+ db: "{{ db_name }}"
+ priv: 'test_table1:INSERT,SELECT,UPDATE,DELETE,TRUNCATE,REFERENCES,TRIGGER/test_table2:INSERT/CREATE,CONNECT,TEMP'
+ login_user: "{{ pg_user }}"
+ register: redo_as_admin
+
+- name: "Check that task succeeded without any change"
+ assert:
+ that:
+ - 'redo_as_admin is not failed'
+ - 'redo_as_admin is not changed'
+ - 'redo_as_admin is successful'
+
+- name: "Check that normal user isn't allowed to access pg_authid"
+ shell: 'psql -c "select * from pg_authid;" {{ db_name }} {{ db_user1 }}'
+ environment:
+ PGPASSWORD: '{{ db_password }}'
+ ignore_errors: true
+ register: pg_authid
+
+- assert:
+ that:
+ - 'pg_authid is failed'
+ - pg_authid.stderr is search('permission denied for (relation|table) pg_authid')
+
+- name: "Normal user isn't allowed to access pg_authid relation: password comparison will fail, password will be updated"
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_user:
+ name: "{{ db_user1 }}"
+ encrypted: 'true'
+ password: "md5{{ (db_password ~ db_user1) | hash('md5')}}"
+ db: "{{ db_name }}"
+ priv: 'test_table1:INSERT,SELECT,UPDATE,DELETE,TRUNCATE,REFERENCES,TRIGGER/test_table2:INSERT/CREATE,CONNECT,TEMP'
+ login_user: "{{ db_user1 }}"
+ login_password: "{{ db_password }}"
+ register: redo_as_normal_user
+
+- name: "Check that task succeeded and that result is changed"
+ assert:
+ that:
+ - 'redo_as_normal_user is not failed'
+ - 'redo_as_normal_user is changed'
+ - 'redo_as_normal_user is successful'
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_privs/tasks/postgresql_privs_general.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_privs/tasks/postgresql_privs_general.yml
new file mode 100644
index 000000000..4b4621010
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_privs/tasks/postgresql_privs_general.yml
@@ -0,0 +1,1767 @@
+# Setup
+- name: Create DB
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_db:
+ state: present
+ name: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+
+- name: Create a user to be owner of objects
+ postgresql_user:
+ name: "{{ db_user3 }}"
+ state: present
+ encrypted: true
+ password: password
+ role_attr_flags: CREATEDB,LOGIN
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+
+- name: Create a user to be given permissions and other tests
+ postgresql_user:
+ name: "{{ db_user2 }}"
+ state: present
+ encrypted: true
+ password: password
+ role_attr_flags: LOGIN
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+
+#############################
+# Test of solving bug 656 #
+#############################
+- name: Create DB with hyphen in the name
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_db:
+ state: present
+ name: "{{ db_name_with_hyphens }}"
+ login_user: "{{ pg_user }}"
+ register: result
+
+- assert:
+ that:
+ - result is changed
+
+- name: Create a user with hyphen in the name
+ postgresql_user:
+ name: "{{ db_user_with_hyphens }}"
+ state: present
+ encrypted: true
+ password: password
+ role_attr_flags: CREATEDB,LOGIN
+ db: "{{ db_name_with_hyphens }}"
+ login_user: "{{ pg_user }}"
+ register: result
+
+- assert:
+ that:
+ - result is changed
+
+- name: Create schema with hyphen in the name
+ postgresql_schema:
+ login_user: "{{ pg_user }}"
+ login_password: password
+ db: "{{ db_name_with_hyphens }}"
+ name: "{{ db_schema_with_hyphens }}"
+ state: present
+ register: result
+
+- assert:
+ that:
+ - result is changed
+
+# Grant rights to the public schema, since in PostgreSQL 15
+# the rights to this schema are taken away from all users except the owner
+- name: GRANT ALL PRIVILEGES ON SCHEMA public TO ansible_db_user1,2,3
+ community.postgresql.postgresql_privs:
+ db: "{{ db_name }}"
+ privs: ALL
+ type: schema
+ objs: public
+ role: "{{ item }}"
+ loop:
+ - "{{ db_user2 }}"
+ - "{{ db_user3 }}"
+
+# Also covers https://github.com/ansible-collections/community.general/issues/884
+- name: Set table default privs on the schema with hyphen in the name
+ postgresql_privs:
+ login_user: "{{ pg_user }}"
+ login_password: password
+ db: "{{ db_name_with_hyphens }}"
+ schema: "{{ db_schema_with_hyphens }}"
+ role: "{{ db_user_with_hyphens }}"
+ type: default_privs
+ obj: TABLES
+ privs: all
+ state: present
+ usage_on_types: true
+ register: result
+ check_mode: true
+
+- assert:
+ that:
+ - result is changed
+ - result.queries is search('ON TYPES')
+
+# Also covers https://github.com/ansible-collections/community.general/issues/884
+- name: Set table default privs on the schema with hyphen in the name
+ postgresql_privs:
+ login_user: "{{ pg_user }}"
+ login_password: password
+ db: "{{ db_name_with_hyphens }}"
+ schema: "{{ db_schema_with_hyphens }}"
+ role: "{{ db_user_with_hyphens }}"
+ type: default_privs
+ obj: TABLES
+ privs: all
+ state: present
+ usage_on_types: false
+ register: result
+
+- assert:
+ that:
+ - result is changed
+ - result.queries is not search('ON TYPES')
+
+- name: Delete table default privs on the schema with hyphen in the name
+ postgresql_privs:
+ login_user: "{{ pg_user }}"
+ login_password: password
+ db: "{{ db_name_with_hyphens }}"
+ schema: "{{ db_schema_with_hyphens }}"
+ role: "{{ db_user_with_hyphens }}"
+ type: default_privs
+ obj: TABLES
+ privs: all
+ state: absent
+ register: result
+
+- assert:
+ that:
+ - result is changed
+
+- name: Delete schema with hyphen in the name
+ postgresql_schema:
+ login_user: "{{ pg_user }}"
+ login_password: password
+ db: "{{ db_name_with_hyphens }}"
+ name: "{{ db_schema_with_hyphens }}"
+ state: absent
+ register: result
+
+- assert:
+ that:
+ - result is changed
+
+- name: Delete a user with hyphen in the name
+ postgresql_user:
+ name: "{{ db_user_with_hyphens }}"
+ state: absent
+ encrypted: true
+ login_password: password
+ role_attr_flags: CREATEDB,LOGIN
+ db: "{{ db_name_with_hyphens }}"
+ login_user: "{{ pg_user }}"
+ register: result
+
+- assert:
+ that:
+ - result is changed
+
+- name: Delete DB with hyphen in the name
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_db:
+ state: absent
+ name: "{{ db_name_with_hyphens }}"
+ login_user: "{{ pg_user }}"
+ register: result
+
+- assert:
+ that:
+ - result is changed
+
+#############################
+# Test of solving bug 27327 #
+#############################
+
+# Create the test table and view:
+- name: Create table
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_table:
+ login_user: "{{ pg_user }}"
+ db: postgres
+ name: test_table1
+ columns:
+ - id int
+
+- name: Create view
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_query:
+ login_user: "{{ pg_user }}"
+ db: postgres
+ query: "CREATE VIEW test_view AS SELECT id FROM test_table1"
+
+# Test check_mode:
+- name: Grant SELECT on test_view, check_mode
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_privs:
+ login_user: "{{ pg_user }}"
+ db: postgres
+ state: present
+ privs: SELECT
+ type: table
+ objs: test_view
+ roles: "{{ db_user2 }}"
+ trust_input: false
+ check_mode: true
+ register: result
+
+- assert:
+ that:
+ - result is changed
+
+# Check:
+- name: Check that nothing was changed after the prev step
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_query:
+ login_user: "{{ pg_user }}"
+ db: postgres
+ query: "SELECT grantee FROM information_schema.role_table_grants WHERE table_name='test_view' AND grantee = '{{ db_user2 }}'"
+ register: result
+
+- assert:
+ that:
+ - result.rowcount == 0
+
+# Test true mode:
+- name: Grant SELECT on test_view
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_privs:
+ login_user: "{{ pg_user }}"
+ db: postgres
+ state: present
+ privs: SELECT
+ type: table
+ objs: test_view
+ roles: "{{ db_user2 }}"
+ trust_input: false
+ register: result
+
+- assert:
+ that:
+ - result is changed
+
+# Check:
+- name: Check that nothing was changed after the prev step
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_query:
+ login_user: "{{ pg_user }}"
+ db: postgres
+ query: "SELECT grantee FROM information_schema.role_table_grants WHERE table_name='test_view' AND grantee = '{{ db_user2 }}'"
+ register: result
+
+- assert:
+ that:
+ - result.rowcount == 1
+
+# Test true mode:
+- name: Try to grant SELECT again
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_privs:
+ login_user: "{{ pg_user }}"
+ db: postgres
+ state: present
+ privs: SELECT
+ type: table
+ objs: test_view
+ roles: "{{ db_user2 }}"
+ trust_input: false
+ register: result
+
+- assert:
+ that:
+ - result is not changed
+
+# Cleanup:
+- name: Drop test view
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_query:
+ login_user: "{{ pg_user }}"
+ db: postgres
+ query: "DROP VIEW test_view"
+
+- name: Drop test table
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_table:
+ login_user: "{{ pg_user }}"
+ db: postgres
+ name: test_table1
+ state: absent
+
+######################################################
+# Test foreign data wrapper and foreign server privs #
+######################################################
+
+# Foreign data wrapper setup
+- name: Create foreign data wrapper extension
+ become: true
+ become_user: "{{ pg_user }}"
+ shell: echo "CREATE EXTENSION postgres_fdw" | psql -d "{{ db_name }}"
+
+- name: Create dummy foreign data wrapper
+ become: true
+ become_user: "{{ pg_user }}"
+ shell: echo "CREATE FOREIGN DATA WRAPPER dummy" | psql -d "{{ db_name }}"
+
+- name: Create foreign server
+ become: true
+ become_user: "{{ pg_user }}"
+ shell: echo "CREATE SERVER dummy_server FOREIGN DATA WRAPPER dummy" | psql -d "{{ db_name }}"
+
+# Test
+- name: Grant foreign data wrapper privileges
+ postgresql_privs:
+ state: present
+ type: foreign_data_wrapper
+ roles: "{{ db_user2 }}"
+ privs: ALL
+ objs: dummy
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ trust_input: false
+ register: result
+ ignore_errors: true
+
+# Checks
+- assert:
+ that:
+ - result is changed
+
+- name: Get foreign data wrapper privileges
+ become: true
+ become_user: "{{ pg_user }}"
+ shell: echo "{{ fdw_query }}" | psql -d "{{ db_name }}"
+ vars:
+ fdw_query: >
+ SELECT fdwacl FROM pg_catalog.pg_foreign_data_wrapper
+ WHERE fdwname = ANY (ARRAY['dummy']) ORDER BY fdwname
+ register: fdw_result
+
+- assert:
+ that:
+ - "fdw_result.stdout_lines[-1] == '(1 row)'"
+ - "'{{ db_user2 }}' in fdw_result.stdout_lines[-2]"
+
+# Test
+- name: Grant foreign data wrapper privileges second time
+ postgresql_privs:
+ state: present
+ type: foreign_data_wrapper
+ roles: "{{ db_user2 }}"
+ privs: ALL
+ objs: dummy
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ trust_input: false
+ register: result
+ ignore_errors: true
+
+# Checks
+- assert:
+ that:
+ - result is not changed
+
+# Test
+- name: Revoke foreign data wrapper privileges
+ postgresql_privs:
+ state: absent
+ type: foreign_data_wrapper
+ roles: "{{ db_user2 }}"
+ privs: ALL
+ objs: dummy
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ trust_input: false
+ register: result
+ ignore_errors: true
+
+# Checks
+- assert:
+ that:
+ - result is changed
+
+- name: Get foreign data wrapper privileges
+ become: true
+ become_user: "{{ pg_user }}"
+ shell: echo "{{ fdw_query }}" | psql -d "{{ db_name }}"
+ vars:
+ fdw_query: >
+ SELECT fdwacl FROM pg_catalog.pg_foreign_data_wrapper
+ WHERE fdwname = ANY (ARRAY['dummy']) ORDER BY fdwname
+ register: fdw_result
+
+- assert:
+ that:
+ - "fdw_result.stdout_lines[-1] == '(1 row)'"
+ - "'{{ db_user2 }}' not in fdw_result.stdout_lines[-2]"
+
+# Test
+- name: Revoke foreign data wrapper privileges for second time
+ postgresql_privs:
+ state: absent
+ type: foreign_data_wrapper
+ roles: "{{ db_user2 }}"
+ privs: ALL
+ objs: dummy
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ trust_input: false
+ register: result
+ ignore_errors: true
+
+# Checks
+- assert:
+ that:
+ - result is not changed
+
+# Test
+- name: Grant foreign server privileges
+ postgresql_privs:
+ state: present
+ type: foreign_server
+ roles: "{{ db_user2 }}"
+ privs: ALL
+ objs: dummy_server
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ trust_input: false
+ register: result
+ ignore_errors: true
+
+# Checks
+- assert:
+ that:
+ - result is changed
+
+- name: Get foreign server privileges
+ become: true
+ become_user: "{{ pg_user }}"
+ shell: echo "{{ fdw_query }}" | psql -d "{{ db_name }}"
+ vars:
+ fdw_query: >
+ SELECT srvacl FROM pg_catalog.pg_foreign_server
+ WHERE srvname = ANY (ARRAY['dummy_server']) ORDER BY srvname
+ register: fs_result
+
+- assert:
+ that:
+ - "fs_result.stdout_lines[-1] == '(1 row)'"
+ - "'{{ db_user2 }}' in fs_result.stdout_lines[-2]"
+
+# Test
+- name: Grant foreign server privileges for second time
+ postgresql_privs:
+ state: present
+ type: foreign_server
+ roles: "{{ db_user2 }}"
+ privs: ALL
+ objs: dummy_server
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ trust_input: false
+ register: result
+ ignore_errors: true
+
+# Checks
+- assert:
+ that:
+ - result is not changed
+
+# Test
+- name: Revoke foreign server privileges
+ postgresql_privs:
+ state: absent
+ type: foreign_server
+ roles: "{{ db_user2 }}"
+ privs: ALL
+ objs: dummy_server
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ trust_input: false
+ register: result
+ ignore_errors: true
+
+# Checks
+- assert:
+ that:
+ - result is changed
+
+- name: Get foreign server privileges
+ become: true
+ become_user: "{{ pg_user }}"
+ shell: echo "{{ fdw_query }}" | psql -d "{{ db_name }}"
+ vars:
+ fdw_query: >
+ SELECT srvacl FROM pg_catalog.pg_foreign_server
+ WHERE srvname = ANY (ARRAY['dummy_server']) ORDER BY srvname
+ register: fs_result
+
+- assert:
+ that:
+ - "fs_result.stdout_lines[-1] == '(1 row)'"
+ - "'{{ db_user2 }}' not in fs_result.stdout_lines[-2]"
+
+# Test
+- name: Revoke foreign server privileges for second time
+ postgresql_privs:
+ state: absent
+ type: foreign_server
+ roles: "{{ db_user2 }}"
+ privs: ALL
+ objs: dummy_server
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ trust_input: false
+ register: result
+ ignore_errors: true
+
+# Checks
+- assert:
+ that:
+ - result is not changed
+
+# Foreign data wrapper cleanup
+- name: Drop foreign server
+ become: true
+ become_user: "{{ pg_user }}"
+ shell: echo "DROP SERVER dummy_server" | psql -d "{{ db_name }}"
+
+- name: Drop dummy foreign data wrapper
+ become: true
+ become_user: "{{ pg_user }}"
+ shell: echo "DROP FOREIGN DATA WRAPPER dummy" | psql -d "{{ db_name }}"
+
+- name: Drop foreign data wrapper extension
+ become: true
+ become_user: "{{ pg_user }}"
+ shell: echo "DROP EXTENSION postgres_fdw" | psql -d "{{ db_name }}"
+
+##########################################
+# Test ALL_IN_SCHEMA for 'function' type #
+##########################################
+
+# Function ALL_IN_SCHEMA Setup
+- name: Create function for test
+ postgresql_query:
+ query: CREATE FUNCTION public.a() RETURNS integer LANGUAGE SQL AS 'SELECT 2';
+ db: "{{ db_name }}"
+ login_user: "{{ db_user3 }}"
+ login_password: password
+
+# Test
+- name: Grant execute to all functions
+ postgresql_privs:
+ type: function
+ state: present
+ privs: EXECUTE
+ roles: "{{ db_user2 }}"
+ objs: ALL_IN_SCHEMA
+ schema: public
+ db: "{{ db_name }}"
+ login_user: "{{ db_user3 }}"
+ login_password: password
+ trust_input: false
+ register: result
+ ignore_errors: true
+
+# Checks
+- assert:
+ that: result is changed
+
+- name: Check that all functions have execute privileges
+ become: true
+ become_user: "{{ pg_user }}"
+ shell: psql {{ db_name }} -c "SELECT proacl FROM pg_proc WHERE proname = 'a'" -t
+ register: result
+
+- assert:
+ that: "'{{ db_user2 }}=X/{{ db_user3 }}' in '{{ result.stdout_lines[0] }}'"
+
+# Test
+- name: Grant execute to all functions again
+ postgresql_privs:
+ type: function
+ state: present
+ privs: EXECUTE
+ roles: "{{ db_user2 }}"
+ objs: ALL_IN_SCHEMA
+ schema: public
+ db: "{{ db_name }}"
+ login_user: "{{ db_user3 }}"
+ login_password: password
+ trust_input: false
+ register: result
+ ignore_errors: true
+
+# Checks
+- assert:
+ that: result is not changed
+
+# Test
+- name: Revoke execute to all functions
+ postgresql_privs:
+ type: function
+ state: absent
+ privs: EXECUTE
+ roles: "{{ db_user2 }}"
+ objs: ALL_IN_SCHEMA
+ schema: public
+ db: "{{ db_name }}"
+ login_user: "{{ db_user3 }}"
+ login_password: password
+ trust_input: false
+ register: result
+ ignore_errors: true
+
+# Checks
+- assert:
+ that: result is changed
+
+# Test
+- name: Revoke execute to all functions again
+ postgresql_privs:
+ type: function
+ state: absent
+ privs: EXECUTE
+ roles: "{{ db_user2 }}"
+ objs: ALL_IN_SCHEMA
+ schema: public
+ db: "{{ db_name }}"
+ login_user: "{{ db_user3 }}"
+ login_password: password
+ trust_input: false
+ register: result
+ ignore_errors: true
+
+- assert:
+ that: result is not changed
+
+# Function ALL_IN_SCHEMA cleanup
+- name: Remove function for test
+ postgresql_query:
+ query: DROP FUNCTION public.a();
+ db: "{{ db_name }}"
+ login_user: "{{ db_user3 }}"
+ login_password: password
+
+# Issue https://github.com/ansible-collections/community.general/issues/994
+- name: Create a procedure for tests
+ postgresql_query:
+ query: "CREATE PROCEDURE mock_procedure() LANGUAGE SQL AS $$ SELECT 1; $$;"
+ db: "{{ db_name }}"
+ login_user: "{{ db_user3 }}"
+ login_password: password
+ when: postgres_version_resp.stdout is version('11', '>=')
+
+# Issue https://github.com/ansible-collections/community.general/issues/994
+- name: Try to run module against a procedure, not function
+ postgresql_privs:
+ type: function
+ state: present
+ privs: ALL
+ roles: "{{ db_user2 }}"
+ objs: ALL_IN_SCHEMA
+ schema: public
+ db: "{{ db_name }}"
+ login_user: "{{ db_user3 }}"
+ login_password: password
+ register: result
+ when: postgres_version_resp.stdout is version('11', '>=')
+
+- assert:
+ that:
+ - result is not changed
+ when: postgres_version_resp.stdout is version('11', '>=')
+
+###########################
+# Test for procedure type #
+###########################
+- name: Create another procedure for tests
+ postgresql_query:
+ query: "CREATE PROCEDURE mock_procedure1(int, int) LANGUAGE SQL AS $$ SELECT 1; $$;"
+ db: "{{ db_name }}"
+ login_user: "{{ db_user3 }}"
+ login_password: password
+ when: postgres_version_resp.stdout is version('11', '>=')
+
+- name: Grant privs on procedure
+ postgresql_privs:
+ type: procedure
+ state: present
+ privs: EXECUTE
+ roles: "{{ db_user2 }}"
+ objs: 'mock_procedure1(int:int)'
+ schema: public
+ db: "{{ db_name }}"
+ login_user: "{{ db_user3 }}"
+ login_password: password
+ register: result
+ when: postgres_version_resp.stdout is version('11', '>=')
+
+- assert:
+ that:
+ - result is changed
+ when: postgres_version_resp.stdout is version('11', '>=')
+
+- name: Grant privs on procedure again
+ postgresql_privs:
+ type: procedure
+ state: present
+ privs: EXECUTE
+ roles: "{{ db_user2 }}"
+ objs: 'mock_procedure1(int:int)'
+ schema: public
+ db: "{{ db_name }}"
+ login_user: "{{ db_user3 }}"
+ login_password: password
+ register: result
+ when: postgres_version_resp.stdout is version('11', '>=')
+
+- assert:
+ that:
+ - result is not changed
+ when: postgres_version_resp.stdout is version('11', '>=')
+
+- name: Revoke procedure privs
+ postgresql_privs:
+ type: procedure
+ state: absent
+ privs: EXECUTE
+ roles: "{{ db_user2 }}"
+ objs: 'mock_procedure1(int:int)'
+ schema: public
+ db: "{{ db_name }}"
+ login_user: "{{ db_user3 }}"
+ login_password: password
+ register: result
+ when: postgres_version_resp.stdout is version('11', '>=')
+
+- assert:
+ that:
+ - result is changed
+ when: postgres_version_resp.stdout is version('11', '>=')
+
+- name: Revoke procedure privs again
+ postgresql_privs:
+ type: procedure
+ state: absent
+ privs: EXECUTE
+ roles: "{{ db_user2 }}"
+ objs: 'mock_procedure1(int:int)'
+ schema: public
+ db: "{{ db_name }}"
+ login_user: "{{ db_user3 }}"
+ login_password: password
+ register: result
+ when: postgres_version_resp.stdout is version('11', '>=')
+
+- assert:
+ that:
+ - result is not changed
+ when: postgres_version_resp.stdout is version('11', '>=')
+
+- name: Grant procedure privs for all object in schema
+ postgresql_privs:
+ type: procedure
+ state: present
+ privs: ALL
+ roles: "{{ db_user2 }}"
+ objs: ALL_IN_SCHEMA
+ schema: public
+ db: "{{ db_name }}"
+ login_user: "{{ db_user3 }}"
+ login_password: password
+ register: result
+ when: postgres_version_resp.stdout is version('11', '>=')
+
+- assert:
+ that:
+ - result is changed
+ when: postgres_version_resp.stdout is version('11', '>=')
+
+- name: Grant procedure privs for all object in schema again
+ postgresql_privs:
+ type: procedure
+ state: present
+ privs: ALL
+ roles: "{{ db_user2 }}"
+ objs: ALL_IN_SCHEMA
+ schema: public
+ db: "{{ db_name }}"
+ login_user: "{{ db_user3 }}"
+ login_password: password
+ register: result
+ when: postgres_version_resp.stdout is version('11', '>=')
+
+- assert:
+ that:
+ - result is not changed
+ when: postgres_version_resp.stdout is version('11', '>=')
+
+- name: Revoke procedure privs for all object in schema
+ postgresql_privs:
+ type: procedure
+ state: absent
+ privs: ALL
+ roles: "{{ db_user2 }}"
+ objs: ALL_IN_SCHEMA
+ schema: public
+ db: "{{ db_name }}"
+ login_user: "{{ db_user3 }}"
+ login_password: password
+ register: result
+ when: postgres_version_resp.stdout is version('11', '>=')
+
+- assert:
+ that:
+ - result is changed
+ when: postgres_version_resp.stdout is version('11', '>=')
+
+#################################################
+# Test ALL_IN_SCHEMA for 'partioned tables type #
+#################################################
+
+# Partitioning tables is a feature introduced in Postgresql 10.
+# (see https://www.postgresql.org/docs/10/ddl-partitioning.html )
+# The test below check for this version
+
+# Function ALL_IN_SCHEMA Setup
+- name: Create partioned table for test purpose
+ postgresql_query:
+ query: CREATE TABLE public.testpt (id int not null, logdate date not null) PARTITION BY RANGE (logdate);
+ db: "{{ db_name }}"
+ login_user: "{{ db_user3 }}"
+ login_password: password
+ when: postgres_version_resp.stdout is version('10', '>=')
+
+# Test
+- name: Grant execute to all tables in check mode
+ postgresql_privs:
+ type: table
+ state: present
+ privs: SELECT
+ roles: "{{ db_user2 }}"
+ objs: ALL_IN_SCHEMA
+ schema: public
+ db: "{{ db_name }}"
+ login_user: "{{ db_user3 }}"
+ login_password: password
+ trust_input: false
+ register: result
+ ignore_errors: true
+ when: postgres_version_resp.stdout is version('10', '>=')
+ check_mode: true
+
+# Checks
+- name: Check that all partitioned tables don't have select privileges after the check mode task
+ postgresql_query:
+ query: SELECT grantee, privilege_type FROM information_schema.role_table_grants WHERE table_name='testpt' and privilege_type='SELECT' and grantee = %(grantuser)s
+ db: "{{ db_name }}"
+ login_user: '{{ db_user2 }}'
+ login_password: password
+ named_args:
+ grantuser: '{{ db_user2 }}'
+ become: true
+ become_user: "{{ pg_user }}"
+ register: result
+ when: postgres_version_resp.stdout is version('10', '>=')
+
+- assert:
+ that:
+ - result.rowcount == 0
+ when: postgres_version_resp.stdout is version('10', '>=')
+
+# Test
+- name: Grant execute with grant option on pg_create_restore_point function
+ postgresql_privs:
+ privs: EXECUTE
+ type: function
+ schema: pg_catalog
+ obj: pg_create_restore_point(text)
+ db: "{{ db_name }}"
+ roles: "{{ db_user2 }}"
+ login_user: "{{ pg_user }}"
+ grant_option: true
+ state: present
+ become: true
+ become_user: "{{ pg_user }}"
+ register: result
+
+# Checks
+- assert:
+ that: result is changed
+
+- name: Check that user has GRANT privilege on the function
+ postgresql_query:
+ query: SELECT proacl FROM pg_proc WHERE proname='pg_create_restore_point'
+ db: "{{ db_name }}"
+ login_user: "{{ db_user2 }}"
+ login_password: password
+ become: true
+ become_user: "{{ pg_user }}"
+ register: result
+
+- assert:
+ that: "'{{ db_user2 }}=X*/{{ pg_user }}' in result.query_result[0].proacl"
+
+# Test
+- name: Grant execute without specifying grant_option to check idempotence
+ postgresql_privs:
+ privs: EXECUTE
+ type: function
+ schema: pg_catalog
+ obj: pg_create_restore_point(text)
+ db: "{{ db_name }}"
+ roles: "{{ db_user2 }}"
+ login_user: "{{ pg_user }}"
+ state: present
+ become: true
+ become_user: "{{ pg_user }}"
+ register: result
+
+# Checks
+- assert:
+ that: result is not changed
+
+- name: Check that user has GRANT privilege on the function
+ postgresql_query:
+ query: SELECT proacl FROM pg_proc WHERE proname='pg_create_restore_point'
+ db: "{{ db_name }}"
+ login_user: "{{ db_user2 }}"
+ login_password: password
+ become: true
+ become_user: "{{ pg_user }}"
+ register: result
+
+- assert:
+ that: "'{{ db_user2 }}=X*/{{ pg_user }}' in result.query_result[0].proacl"
+
+# Test
+- name: Revoke grant option on pg_create_restore_point function
+ postgresql_privs:
+ privs: EXECUTE
+ type: function
+ schema: pg_catalog
+ obj: pg_create_restore_point(text)
+ db: "{{ db_name }}"
+ roles: "{{ db_user2 }}"
+ login_user: "{{ pg_user }}"
+ grant_option: false
+ state: present
+ become: true
+ become_user: "{{ pg_user }}"
+ register: result
+
+# Checks
+- assert:
+ that: result is changed
+
+- name: Check that user does not have GRANT privilege on the function
+ postgresql_query:
+ query: SELECT proacl FROM pg_proc WHERE proname='pg_create_restore_point'
+ db: "{{ db_name }}"
+ login_user: "{{ db_user2 }}"
+ login_password: password
+ become: true
+ become_user: "{{ pg_user }}"
+ register: result
+
+- assert:
+ that: "'{{ db_user2 }}=X/{{ pg_user }}' in result.query_result[0].proacl"
+
+# Test
+- name: Revoke execute on pg_create_restore_point function
+ postgresql_privs:
+ privs: EXECUTE
+ type: function
+ schema: pg_catalog
+ obj: pg_create_restore_point(text)
+ db: "{{ db_name }}"
+ roles: "{{ db_user2 }}"
+ login_user: "{{ pg_user }}"
+ state: absent
+ become: true
+ become_user: "{{ pg_user }}"
+ register: result
+
+# Checks
+- assert:
+ that: result is changed
+
+- name: Check that user does not have EXECUTE privilege on the function
+ postgresql_query:
+ query: SELECT proacl FROM pg_proc WHERE proname='pg_create_restore_point'
+ db: "{{ db_name }}"
+ login_user: "{{ db_user2 }}"
+ login_password: password
+ become: true
+ become_user: "{{ pg_user }}"
+ register: result
+
+- assert:
+ that: "'{{ db_user2 }}' not in result.query_result[0].proacl"
+
+# Test
+- name: Grant execute to all tables
+ postgresql_privs:
+ type: table
+ state: present
+ privs: SELECT
+ roles: "{{ db_user2 }}"
+ objs: ALL_IN_SCHEMA
+ schema: public
+ db: "{{ db_name }}"
+ login_user: "{{ db_user3 }}"
+ login_password: password
+ trust_input: false
+ register: result
+ ignore_errors: true
+ when: postgres_version_resp.stdout is version('10', '>=')
+
+# Checks
+- assert:
+ that: result is changed
+ when: postgres_version_resp.stdout is version('10', '>=')
+
+- name: Check that all partitioned tables have select privileges
+ postgresql_query:
+ query: SELECT grantee, privilege_type FROM information_schema.role_table_grants WHERE table_name='testpt' and privilege_type='SELECT' and grantee = %(grantuser)s
+ db: "{{ db_name }}"
+ login_user: '{{ db_user2 }}'
+ login_password: password
+ named_args:
+ grantuser: '{{ db_user2 }}'
+ become: true
+ become_user: "{{ pg_user }}"
+ register: result
+ when: postgres_version_resp.stdout is version('10', '>=')
+
+- assert:
+ that:
+ - result.rowcount == 1
+ when: postgres_version_resp.stdout is version('10', '>=')
+
+# Test
+- name: Grant execute to all tables again to see no changes are reported
+ postgresql_privs:
+ type: table
+ state: present
+ privs: SELECT
+ roles: "{{ db_user2 }}"
+ objs: ALL_IN_SCHEMA
+ schema: public
+ db: "{{ db_name }}"
+ login_user: "{{ db_user3 }}"
+ login_password: password
+ trust_input: false
+ register: result
+ ignore_errors: true
+ when: postgres_version_resp.stdout is version('10', '>=')
+
+# Checks
+- assert:
+ that: result is not changed
+ when: postgres_version_resp.stdout is version('10', '>=')
+
+# Test
+- name: Revoke SELECT to all tables
+ postgresql_privs:
+ type: table
+ state: absent
+ privs: SELECT
+ roles: "{{ db_user2 }}"
+ objs: ALL_IN_SCHEMA
+ schema: public
+ db: "{{ db_name }}"
+ login_user: "{{ db_user3 }}"
+ login_password: password
+ trust_input: false
+ register: result
+ ignore_errors: true
+ when: postgres_version_resp.stdout is version('10', '>=')
+
+# Checks
+- assert:
+ that: result is changed
+ when: postgres_version_resp.stdout is version('10', '>=')
+
+- name: Check that all partitioned tables don't have select privileges
+ postgresql_query:
+ query: SELECT grantee, privilege_type FROM information_schema.role_table_grants WHERE table_name='testpt' and privilege_type='SELECT' and grantee = %(grantuser)s
+ db: "{{ db_name }}"
+ login_user: '{{ db_user2 }}'
+ login_password: password
+ named_args:
+ grantuser: '{{ db_user2 }}'
+ become: true
+ become_user: "{{ pg_user }}"
+ register: result
+ when: postgres_version_resp.stdout is version('10', '>=')
+
+- assert:
+ that:
+ - result.rowcount == 0
+ when: postgres_version_resp.stdout is version('10', '>=')
+
+# Test
+- name: Revoke SELECT to all tables and no changes are reported
+ postgresql_privs:
+ type: table
+ state: absent
+ privs: SELECT
+ roles: "{{ db_user2 }}"
+ objs: ALL_IN_SCHEMA
+ schema: public
+ db: "{{ db_name }}"
+ login_user: "{{ db_user3 }}"
+ login_password: password
+ trust_input: false
+ register: result
+ ignore_errors: true
+ when: postgres_version_resp.stdout is version('10', '>=')
+
+- assert:
+ that: result is not changed
+ when: postgres_version_resp.stdout is version('10', '>=')
+
+# Table ALL_IN_SCHEMA cleanup
+- name: Remove table for test
+ postgresql_query:
+ query: DROP TABLE public.testpt;
+ db: "{{ db_name }}"
+ login_user: "{{ db_user3 }}"
+ login_password: password
+ trust_input: false
+ ignore_errors: true
+ when: postgres_version_resp.stdout is version('10', '>=')
+
+###########################################
+# Test for 'type' value of type parameter #
+###########################################
+
+# Test
+- name: Grant type privileges
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_privs:
+ state: present
+ type: type
+ roles: "{{ db_user2 }}"
+ privs: ALL
+ objs: numeric
+ schema: pg_catalog
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ trust_input: false
+ register: result
+ when: postgres_version_resp.stdout is version('10', '>=')
+
+# Checks
+- assert:
+ that:
+ - result is changed
+ when: postgres_version_resp.stdout is version('10', '>=')
+
+- name: Get type privileges
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_query:
+ login_user: "{{ pg_user }}"
+ login_db: "{{ db_name }}"
+ query: SELECT typacl FROM pg_catalog.pg_type WHERE typname = 'numeric';
+ register: typ_result
+ when: postgres_version_resp.stdout is version('10', '>=')
+
+- assert:
+ that:
+ - "'{{ db_user2 }}' in typ_result.query_result[0].typacl"
+ when: postgres_version_resp.stdout is version('10', '>=')
+
+- name: Grant type privileges again using check_mode
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_privs:
+ state: present
+ type: type
+ roles: "{{ db_user2 }}"
+ privs: ALL
+ objs: numeric
+ schema: pg_catalog
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ trust_input: false
+ register: result
+ check_mode: true
+ when: postgres_version_resp.stdout is version('10', '>=')
+
+# Checks
+- assert:
+ that:
+ - result is not changed
+ when: postgres_version_resp.stdout is version('10', '>=')
+
+- name: Get type privileges
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_query:
+ login_user: "{{ pg_user }}"
+ login_db: "{{ db_name }}"
+ query: SELECT typacl FROM pg_catalog.pg_type WHERE typname = 'numeric';
+ register: typ_result
+ when: postgres_version_resp.stdout is version('10', '>=')
+
+- assert:
+ that:
+ - "'{{ db_user2 }}' in typ_result.query_result[0].typacl"
+ when: postgres_version_resp.stdout is version('10', '>=')
+
+- name: Grant type privileges again
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_privs:
+ state: present
+ type: type
+ roles: "{{ db_user2 }}"
+ privs: ALL
+ objs: numeric
+ schema: pg_catalog
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ trust_input: false
+ register: result
+ when: postgres_version_resp.stdout is version('10', '>=')
+
+# Checks
+- assert:
+ that:
+ - result is not changed
+ when: postgres_version_resp.stdout is version('10', '>=')
+
+- name: Get type privileges
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_query:
+ login_user: "{{ pg_user }}"
+ login_db: "{{ db_name }}"
+ query: SELECT typacl FROM pg_catalog.pg_type WHERE typname = 'numeric';
+ register: typ_result
+ when: postgres_version_resp.stdout is version('10', '>=')
+
+- assert:
+ that:
+ - "'{{ db_user2 }}' in typ_result.query_result[0].typacl"
+ when: postgres_version_resp.stdout is version('10', '>=')
+
+- name: Revoke type privileges in check_mode
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_privs:
+ state: absent
+ type: type
+ roles: "{{ db_user2 }}"
+ privs: ALL
+ objs: numeric
+ schema: pg_catalog
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ trust_input: false
+ register: result
+ check_mode: true
+ when: postgres_version_resp.stdout is version('10', '>=')
+
+# Checks
+- assert:
+ that:
+ - result is changed
+ when: postgres_version_resp.stdout is version('10', '>=')
+
+- name: Get type privileges
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_query:
+ login_user: "{{ pg_user }}"
+ login_db: "{{ db_name }}"
+ query: SELECT typacl FROM pg_catalog.pg_type WHERE typname = 'numeric';
+ register: typ_result
+ when: postgres_version_resp.stdout is version('10', '>=')
+
+- assert:
+ that:
+ - "'{{ db_user2 }}' in typ_result.query_result[0].typacl"
+ when: postgres_version_resp.stdout is version('10', '>=')
+
+- name: Revoke type privileges
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_privs:
+ state: absent
+ type: type
+ roles: "{{ db_user2 }}"
+ privs: ALL
+ objs: numeric
+ schema: pg_catalog
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ trust_input: false
+ register: result
+ when: postgres_version_resp.stdout is version('10', '>=')
+
+# Checks
+- assert:
+ that:
+ - result is changed
+ when: postgres_version_resp.stdout is version('10', '>=')
+
+- name: Get type privileges
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_query:
+ login_user: "{{ pg_user }}"
+ login_db: "{{ db_name }}"
+ query: SELECT typacl FROM pg_catalog.pg_type WHERE typname = 'numeric';
+ register: typ_result
+ when: postgres_version_resp.stdout is version('10', '>=')
+
+- assert:
+ that:
+ - "'{{ db_user2 }}' not in typ_result.query_result[0].typacl"
+ when: postgres_version_resp.stdout is version('10', '>=')
+
+# type with default schema (public):
+- name: Create custom type in schema public
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_query:
+ login_user: "{{ pg_user }}"
+ login_db: "{{ db_name }}"
+ query: "CREATE TYPE compfoo AS (f1 int, f2 text)"
+ when: postgres_version_resp.stdout is version('10', '>=')
+
+# Test
+- name: Grant type privileges with default schema
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_privs:
+ state: present
+ type: type
+ roles: "{{ db_user2 }}"
+ privs: ALL
+ objs: compfoo
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ trust_input: false
+ register: result
+ when: postgres_version_resp.stdout is version('10', '>=')
+
+# Checks
+- assert:
+ that:
+ - result is changed
+ when: postgres_version_resp.stdout is version('10', '>=')
+
+- name: Get type privileges
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_query:
+ login_user: "{{ pg_user }}"
+ login_db: "{{ db_name }}"
+ query: >
+ SELECT t.typacl FROM pg_catalog.pg_type t JOIN pg_catalog.pg_namespace n
+ ON n.oid = t.typnamespace WHERE t.typname = 'compfoo' AND n.nspname = 'public';
+ register: typ_result
+ when: postgres_version_resp.stdout is version('10', '>=')
+
+- assert:
+ that:
+ - "'{{ db_user2 }}' in typ_result.query_result[0].typacl"
+ when: postgres_version_resp.stdout is version('10', '>=')
+
+######################################################################
+# https://github.com/ansible-collections/community.general/issues/1058
+- name: Create user for test
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_user:
+ login_user: "{{ pg_user }}"
+ login_db: "{{ db_name }}"
+ name: "test-role"
+ role_attr_flags: "NOLOGIN,NOSUPERUSER,INHERIT,NOCREATEDB,NOCREATEROLE,NOREPLICATION"
+
+- name: Test community.general/issue/1058 GRANT with hyphen
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_privs:
+ login_user: "{{ pg_user }}"
+ login_db: "{{ db_name }}"
+ roles: "test-role"
+ objs: "{{ pg_user }}"
+ type: "group"
+ register: result
+
+- assert:
+ that:
+ - result is changed
+ - result.queries == ["GRANT \"{{ pg_user }}\" TO \"test-role\";"]
+
+- name: Test community.general/issue/1058 REVOKE
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_privs:
+ login_user: "{{ pg_user }}"
+ login_db: "{{ db_name }}"
+ roles: "test-role"
+ objs: "{{ pg_user }}"
+ type: "group"
+ state: absent
+ register: result
+
+- assert:
+ that:
+ - result is changed
+ - result.queries == ["REVOKE \"{{ pg_user }}\" FROM \"test-role\";"]
+
+- name: Test community.general/issue/1058 GRANT without hyphen
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_privs:
+ login_user: "{{ pg_user }}"
+ login_db: "{{ db_name }}"
+ roles: "{{ db_user3 }}"
+ objs: "{{ pg_user }}"
+ type: "group"
+ register: result
+
+- assert:
+ that:
+ - result is changed
+ - result.queries == ["GRANT \"{{ pg_user }}\" TO \"{{ db_user3 }}\";"]
+
+- name: Test community.general/issue/1058 GRANT with hyphen as an object
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_privs:
+ login_user: "{{ pg_user }}"
+ login_db: "{{ db_name }}"
+ roles: "{{ db_user3 }}"
+ objs: "test-role,{{ db_user2 }}"
+ type: "group"
+ register: result
+
+- assert:
+ that:
+ - result is changed
+ - result.queries == ["GRANT \"test-role\",\"{{ db_user2 }}\" TO \"{{ db_user3 }}\";"]
+
+- name: Test community.general/issue/1058 GRANT with hyphen as an object
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_privs:
+ login_user: "{{ pg_user }}"
+ login_db: "{{ db_name }}"
+ roles: "{{ db_user3 }}"
+ objs: "test-role"
+ type: "group"
+ register: result
+
+- assert:
+ that:
+ - result is not changed
+
+##############
+# Issue https://github.com/ansible-collections/community.postgresql/issues/381
+- name: create schemas with special names
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_schema:
+ login_user: "{{ pg_user }}"
+ login_password: password
+ db: "{{ db_name }}"
+ name: '"{{ item }}"'
+ state: present
+ loop:
+ - "{{ db_schema_with_dot|replace('\"', '\"\"') }}"
+ - "{{ db_schema_with_quote|replace('\"', '\"\"') }}"
+ register: result
+- assert:
+ that:
+ - result is changed
+- name: create tables in schemas with special names
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_table:
+ login_user: "{{ pg_user }}"
+ login_password: password
+ db: "{{ db_name }}"
+ name: '"{{ item }}"."test.table.name"'
+ columns: []
+ loop:
+ - "{{ db_schema_with_dot|replace('\"', '\"\"') }}"
+ - "{{ db_schema_with_quote|replace('\"', '\"\"') }}"
+ register: result
+- assert:
+ that:
+ - result is changed
+- name: grant privileges on all tables in schemas with special names
+ become: yes
+ become_user: "{{ pg_user }}"
+ postgresql_privs:
+ login_user: "{{ pg_user }}"
+ login_db: "{{ db_name }}"
+ roles: PUBLIC
+ objs: ALL_IN_SCHEMA
+ type: table
+ privs: SELECT
+ schema: "{{ item }}"
+ loop:
+ - "{{ db_schema_with_dot }}"
+ - "{{ db_schema_with_quote }}"
+ register: result
+- assert:
+ that:
+ - result is changed
+- name: grant privileges on some table in schemas with special names
+ become: yes
+ become_user: "{{ pg_user }}"
+ postgresql_privs:
+ login_user: "{{ pg_user }}"
+ login_db: "{{ db_name }}"
+ roles: PUBLIC
+ objs: 'test.table.name'
+ type: table
+ privs: SELECT
+ schema: "{{ item }}"
+ loop:
+ - "{{ db_schema_with_dot }}"
+ - "{{ db_schema_with_quote }}"
+ register: result
+- assert:
+ that:
+ - result is changed
+- name: check permissions on tables in schemas with special names
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_query:
+ login_user: "{{ pg_user }}"
+ db: "{{ db_name }}"
+ query: |
+ select true as granted from information_schema.role_table_grants
+ where table_schema=%s and table_name='test.table.name' and privilege_type='SELECT' and grantee='PUBLIC'
+ positional_args:
+ - "{{ item }}"
+ loop:
+ - "{{ db_schema_with_dot }}"
+ - "{{ db_schema_with_quote }}"
+ register: result
+- assert:
+ that:
+ - 'result.results|length == 2'
+ - 'result.results[0].rowcount == 1'
+ - 'not result.results[0].failed'
+ - 'result.results[1].rowcount == 1'
+ - 'not result.results[1].failed'
+- name: cleanup test schemas with special names
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_schema:
+ login_user: "{{ pg_user }}"
+ login_password: password
+ db: "{{ db_name }}"
+ name: '"{{ item }}"'
+ state: absent
+ cascade_drop: true
+ loop:
+ - "{{ db_schema_with_dot|replace('\"', '\"\"') }}"
+ - "{{ db_schema_with_quote|replace('\"', '\"\"') }}"
+ register: result
+
+
+##############
+# Issue https://github.com/ansible-collections/community.postgresql/issues/332
+- name: Test community.postgresql issue 332 grant usage
+ become: yes
+ become_user: "{{ pg_user }}"
+ postgresql_privs:
+ login_user: "{{ pg_user }}"
+ login_db: "{{ db_name }}"
+ roles: "{{ db_user3 }}"
+ objs: schemas
+ type: default_privs
+ privs: usage
+ register: result
+
+- assert:
+ that:
+ - result is changed
+
+- name: Test community.postgresql issue 332 grant usage, run again
+ become: yes
+ become_user: "{{ pg_user }}"
+ postgresql_privs:
+ login_user: "{{ pg_user }}"
+ login_db: "{{ db_name }}"
+ roles: "{{ db_user3 }}"
+ objs: schemas
+ type: default_privs
+ privs: usage
+ register: result
+
+- assert:
+ that:
+ - result is not changed
+
+- name: Test community.postgresql issue 333 grant usage
+ become: yes
+ become_user: "{{ pg_user }}"
+ postgresql_privs:
+ login_user: "{{ pg_user }}"
+ login_db: "{{ db_name }}"
+ roles: "{{ db_user3 }}"
+ objs: tables
+ type: default_privs
+ schema: not-specified
+ privs: select
+ register: result
+
+- assert:
+ that:
+ - result is changed
+
+- name: Test community.postgresql issue 333 grant usage, run again
+ become: yes
+ become_user: "{{ pg_user }}"
+ postgresql_privs:
+ login_user: "{{ pg_user }}"
+ login_db: "{{ db_name }}"
+ roles: "{{ db_user3 }}"
+ objs: tables
+ type: default_privs
+ schema: not-specified
+ privs: select
+ register: result
+
+- assert:
+ that:
+ - result is not changed
+
+- name: Test community.postgresql issue 373
+ become: yes
+ become_user: "{{ pg_user }}"
+ postgresql_privs:
+ login_user: "{{ pg_user }}"
+ login_db: "{{ db_name }}"
+ roles: "{{ db_user3 }}"
+ objs: ALL_DEFAULT
+ type: default_privs
+ privs: ALL
+ register: result
+
+- assert:
+ that:
+ - result is changed
+
+- name: Test community.postgresql issue 379
+ become: yes
+ become_user: "{{ pg_user }}"
+ postgresql_privs:
+ login_user: "{{ pg_user }}"
+ login_db: "{{ db_name }}"
+ roles: "{{ db_user3 }}"
+ objs: ALL_IN_SCHEMA
+ type: default_privs
+ privs: SELECT,INSERT,UPDATE,DELETE,EXECUTE
+ schema: public
+ register: result
+ ignore_errors: yes
+
+- assert:
+ that:
+ - result is failed
+ - result.msg is search('ALL_IN_SCHEMA can be used only for type')
+
+# Cleanup
+- name: Remove privs
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_privs:
+ state: absent
+ type: type
+ roles: "{{ db_user2 }}"
+ privs: ALL
+ objs: compfoo
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ trust_input: false
+ when: postgres_version_resp.stdout is version('10', '>=')
+
+- name: Reassign ownership
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_owner:
+ login_user: "{{ pg_user }}"
+ db: "{{ db_name }}"
+ new_owner: "{{ pg_user }}"
+ reassign_owned_by: "{{ item }}"
+ loop:
+ - "{{ db_user2 }}"
+ - "{{ db_user3 }}"
+
+- name: Drop a role for which the default privileges have been altered
+ become: yes
+ become_user: "{{ pg_user }}"
+ postgresql_query:
+ login_db: "{{ db_name }}"
+ query: "DROP OWNED BY {{ item }};"
+ loop:
+ - "{{ db_user2 }}"
+ - "{{ db_user3 }}"
+
+- name: Remove user given permissions
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_user:
+ name: "{{ db_user2 }}"
+ state: absent
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+
+- name: Remove user owner of objects
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_user:
+ name: "{{ item }}"
+ state: absent
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ loop:
+ - '{{ db_user3 }}'
+ - 'test-role'
+
+- name: Destroy DB
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_db:
+ state: absent
+ name: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_privs/tasks/postgresql_privs_initial.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_privs/tasks/postgresql_privs_initial.yml
new file mode 100644
index 000000000..814bc348d
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_privs/tasks/postgresql_privs_initial.yml
@@ -0,0 +1,407 @@
+# The tests below were added initially and moved here
+# from the shared target called ``postgresql`` by @Andersson007 <aaklychkov@mail.ru>.
+# You can see modern examples of CI tests in postgresql_publication directory, for example.
+
+#
+# Test settings privileges
+#
+- name: Create db
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_db:
+ name: "{{ db_name }}"
+ state: "present"
+ login_user: "{{ pg_user }}"
+
+- name: Create some tables on the db
+ become_user: "{{ pg_user }}"
+ become: true
+ shell: echo "create table test_table1 (field text);" | psql {{ db_name }}
+
+- become_user: "{{ pg_user }}"
+ become: true
+ shell: echo "create table test_table2 (field text);" | psql {{ db_name }}
+
+- vars:
+ db_password: 'secretù' # use UTF-8
+ block:
+ - name: Create a user with some permissions on the db
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_user:
+ name: "{{ db_user1 }}"
+ encrypted: 'true'
+ password: "md5{{ (db_password ~ db_user1) | hash('md5')}}"
+ db: "{{ db_name }}"
+ priv: 'test_table1:INSERT,SELECT,UPDATE,DELETE,TRUNCATE,REFERENCES,TRIGGER/test_table2:INSERT/CREATE,CONNECT,TEMP'
+ login_user: "{{ pg_user }}"
+
+ - include_tasks: pg_authid_not_readable.yml
+
+- name: Check that the user has the requested permissions (table1)
+ become_user: "{{ pg_user }}"
+ become: true
+ shell: echo "select privilege_type from information_schema.role_table_grants where grantee='{{ db_user1 }}' and table_name='test_table1';" | psql {{ db_name }}
+ register: result_table1
+
+- name: Check that the user has the requested permissions (table2)
+ become_user: "{{ pg_user }}"
+ become: true
+ shell: echo "select privilege_type from information_schema.role_table_grants where grantee='{{ db_user1 }}' and table_name='test_table2';" | psql {{ db_name }}
+ register: result_table2
+
+- name: Check that the user has the requested permissions (database)
+ become_user: "{{ pg_user }}"
+ become: true
+ shell: echo "select datacl from pg_database where datname='{{ db_name }}';" | psql {{ db_name }}
+ register: result_database
+
+- assert:
+ that:
+ - "result_table1.stdout_lines[-1] == '(7 rows)'"
+ - "'INSERT' in result_table1.stdout"
+ - "'SELECT' in result_table1.stdout"
+ - "'UPDATE' in result_table1.stdout"
+ - "'DELETE' in result_table1.stdout"
+ - "'TRUNCATE' in result_table1.stdout"
+ - "'REFERENCES' in result_table1.stdout"
+ - "'TRIGGER' in result_table1.stdout"
+ - "result_table2.stdout_lines[-1] == '(1 row)'"
+ - "'INSERT' == '{{ result_table2.stdout_lines[-2] | trim }}'"
+ - "result_database.stdout_lines[-1] == '(1 row)'"
+ - "'{{ db_user1 }}=CTc/{{ pg_user }}' in result_database.stdout_lines[-2]"
+
+- name: Add another permission for the user
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_user:
+ name: "{{ db_user1 }}"
+ encrypted: 'true'
+ password: "md55c8ccfd9d6711fc69a7eae647fc54f51"
+ db: "{{ db_name }}"
+ priv: 'test_table2:select'
+ login_user: "{{ pg_user }}"
+ register: result
+
+- name: Check that ansible reports it changed the user
+ assert:
+ that:
+ - result is changed
+
+- name: Check that the user has the requested permissions (table2)
+ become_user: "{{ pg_user }}"
+ become: true
+ shell: echo "select privilege_type from information_schema.role_table_grants where grantee='{{ db_user1 }}' and table_name='test_table2';" | psql {{ db_name }}
+ register: result_table2
+
+- assert:
+ that:
+ - "result_table2.stdout_lines[-1] == '(2 rows)'"
+ - "'INSERT' in result_table2.stdout"
+ - "'SELECT' in result_table2.stdout"
+
+#
+# Test priv setting via postgresql_privs module
+# (Depends on state from previous _user privs tests)
+#
+
+- name: Revoke a privilege
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_privs:
+ type: "table"
+ state: "absent"
+ roles: "{{ db_user1 }}"
+ privs: "INSERT"
+ objs: "test_table2"
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ trust_input: false
+ register: result
+
+- name: Check that ansible reports it changed the user
+ assert:
+ that:
+ - result is changed
+
+- name: Check that the user has the requested permissions (table2)
+ become_user: "{{ pg_user }}"
+ become: true
+ shell: echo "select privilege_type from information_schema.role_table_grants where grantee='{{ db_user1 }}' and table_name='test_table2';" | psql {{ db_name }}
+ register: result_table2
+
+- assert:
+ that:
+ - "result_table2.stdout_lines[-1] == '(1 row)'"
+ - "'SELECT' == '{{ result_table2.stdout_lines[-2] | trim }}'"
+
+- name: Revoke many privileges on multiple tables
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_privs:
+ state: "absent"
+ roles: "{{ db_user1 }}"
+ privs: "INSERT,select,UPDATE,TRUNCATE,REFERENCES,TRIGGER,delete"
+ objs: "test_table2,test_table1"
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ trust_input: false
+ register: result
+
+- name: Check that ansible reports it changed the user
+ assert:
+ that:
+ - result is changed
+
+- name: Check that permissions were revoked (table1)
+ become_user: "{{ pg_user }}"
+ become: true
+ shell: echo "select privilege_type from information_schema.role_table_grants where grantee='{{ db_user1 }}' and table_name='test_table1';" | psql {{ db_name }}
+ register: result_table1
+
+- name: Check that permissions were revoked (table2)
+ become_user: "{{ pg_user }}"
+ become: true
+ shell: echo "select privilege_type from information_schema.role_table_grants where grantee='{{ db_user1 }}' and table_name='test_table2';" | psql {{ db_name }}
+ register: result_table2
+
+- assert:
+ that:
+ - "result_table1.stdout_lines[-1] == '(0 rows)'"
+ - "result_table2.stdout_lines[-1] == '(0 rows)'"
+
+- name: Revoke database privileges
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_privs:
+ type: "database"
+ state: "absent"
+ roles: "{{ db_user1 }}"
+ privs: "Create,connect,TEMP"
+ objs: "{{ db_name }}"
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ trust_input: false
+
+- name: Check that the user has the requested permissions (database)
+ become_user: "{{ pg_user }}"
+ become: true
+ shell: echo "select datacl from pg_database where datname='{{ db_name }}';" | psql {{ db_name }}
+ register: result_database
+
+- assert:
+ that:
+ - "result_database.stdout_lines[-1] == '(1 row)'"
+ - "'{{ db_user1 }}' not in result_database.stdout"
+
+- name: Grant database privileges
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_privs:
+ type: "database"
+ state: "present"
+ roles: "{{ db_user1 }}"
+ privs: "CREATE,connect"
+ objs: "{{ db_name }}"
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ trust_input: false
+ register: result
+
+- name: Check that ansible reports it changed the user
+ assert:
+ that:
+ - result is changed
+
+- name: Check that the user has the requested permissions (database)
+ become_user: "{{ pg_user }}"
+ become: true
+ shell: echo "select datacl from pg_database where datname='{{ db_name }}';" | psql {{ db_name }}
+ register: result_database
+
+- assert:
+ that:
+ - "result_database.stdout_lines[-1] == '(1 row)'"
+ - "'{{ db_user1 }}=Cc' in result_database.stdout"
+
+- name: Grant a single privilege on a table
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_privs:
+ state: "present"
+ roles: "{{ db_user1 }}"
+ privs: "INSERT"
+ objs: "test_table1"
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ trust_input: false
+
+- name: Check that permissions were added (table1)
+ become_user: "{{ pg_user }}"
+ become: true
+ shell: echo "select privilege_type from information_schema.role_table_grants where grantee='{{ db_user1 }}' and table_name='test_table1';" | psql {{ db_name }}
+ register: result_table1
+
+- assert:
+ that:
+ - "result_table1.stdout_lines[-1] == '(1 row)'"
+ - "'{{ result_table1.stdout_lines[-2] | trim }}' == 'INSERT'"
+
+- name: Grant many privileges on multiple tables
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_privs:
+ state: "present"
+ roles: "{{ db_user1 }}"
+ privs: 'INSERT,SELECT,UPDATE,DELETE,TRUNCATE,REFERENCES,trigger'
+ objs: "test_table2,test_table1"
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ trust_input: false
+
+- name: Check that permissions were added (table1)
+ become_user: "{{ pg_user }}"
+ become: true
+ shell: echo "select privilege_type from information_schema.role_table_grants where grantee='{{ db_user1 }}' and table_name='test_table1';" | psql {{ db_name }}
+ register: result_table1
+
+- name: Check that permissions were added (table2)
+ become_user: "{{ pg_user }}"
+ become: true
+ shell: echo "select privilege_type from information_schema.role_table_grants where grantee='{{ db_user1 }}' and table_name='test_table2';" | psql {{ db_name }}
+ register: result_table2
+
+- assert:
+ that:
+ - "result_table1.stdout_lines[-1] == '(7 rows)'"
+ - "'INSERT' in result_table1.stdout"
+ - "'SELECT' in result_table1.stdout"
+ - "'UPDATE' in result_table1.stdout"
+ - "'DELETE' in result_table1.stdout"
+ - "'TRUNCATE' in result_table1.stdout"
+ - "'REFERENCES' in result_table1.stdout"
+ - "'TRIGGER' in result_table1.stdout"
+ - "result_table2.stdout_lines[-1] == '(7 rows)'"
+ - "'INSERT' in result_table2.stdout"
+ - "'SELECT' in result_table2.stdout"
+ - "'UPDATE' in result_table2.stdout"
+ - "'DELETE' in result_table2.stdout"
+ - "'TRUNCATE' in result_table2.stdout"
+ - "'REFERENCES' in result_table2.stdout"
+ - "'TRIGGER' in result_table2.stdout"
+
+# Check passing roles with dots
+# https://github.com/ansible/ansible/issues/63204
+- name: Create roles for further tests
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_user:
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ name: "{{ item }}"
+ loop:
+ - "{{ db_user_with_dots1 }}"
+ - "{{ db_user_with_dots2 }}"
+
+- name: Pass role with dots in its name to roles parameter
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_privs:
+ state: "present"
+ roles: "{{ db_user_with_dots1 }}"
+ privs: "INSERT"
+ objs: "test_table1"
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ trust_input: false
+
+- name: Check that permissions were added (table1)
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ query: "select privilege_type from information_schema.role_table_grants where grantee='{{ db_user_with_dots1 }}' and table_name='test_table1'"
+ register: result
+
+- assert:
+ that:
+ - result.rowcount == 1
+
+# We don't need to check anything here, only that nothing failed
+- name: Pass role with dots in its name to target_roles parameter
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_privs:
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ state: "present"
+ roles: "{{ db_user_with_dots1 }}"
+ privs: "INSERT"
+ objs: TABLES
+ type: default_privs
+ target_roles: "{{ db_user_with_dots2 }}"
+ trust_input: false
+
+# Bugfix for https://github.com/ansible-collections/community.general/issues/857
+- name: Test passing lowercase PUBLIC role
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_privs:
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ type: 'database'
+ privs: 'connect'
+ role: 'public'
+ register: result
+
+- assert:
+ that:
+ - result is changed
+ - result.queries == ["GRANT CONNECT ON database \"{{ db_name }}\" TO PUBLIC;"]
+
+#
+# Cleanup
+#
+- name: Cleanup db
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_db:
+ name: "{{ db_name }}"
+ state: "absent"
+ login_user: "{{ pg_user }}"
+
+- name: Check that database was destroyed
+ become_user: "{{ pg_user }}"
+ become: true
+ shell: echo "select datname from pg_database where datname = '{{ db_name }}';" | psql -d postgres
+ register: result
+
+- assert:
+ that:
+ - "result.stdout_lines[-1] == '(0 rows)'"
+
+- name: Cleanup test user
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_user:
+ name: "{{ item }}"
+ state: 'absent'
+ login_user: "{{ pg_user }}"
+ db: postgres
+ loop:
+ - "{{ db_user1 }}"
+ - "{{ db_user2 }}"
+ - "{{ db_user3 }}"
+ - "{{ db_user_with_dots1 }}"
+ - "{{ db_user_with_dots2 }}"
+
+- name: Check that they were removed
+ become_user: "{{ pg_user }}"
+ become: true
+ shell: echo "select * from pg_user where usename='{{ db_user1 }}';" | psql -d postgres
+ register: result
+
+- assert:
+ that:
+ - "result.stdout_lines[-1] == '(0 rows)'"
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_privs/tasks/postgresql_privs_session_role.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_privs/tasks/postgresql_privs_session_role.yml
new file mode 100644
index 000000000..e0c083e90
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_privs/tasks/postgresql_privs_session_role.yml
@@ -0,0 +1,102 @@
+- name: Create a high privileged user
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_user:
+ name: "{{ db_session_role1 }}"
+ state: "present"
+ password: "password"
+ role_attr_flags: "CREATEDB,LOGIN,CREATEROLE"
+ login_user: "{{ pg_user }}"
+ db: postgres
+
+- name: Create a low privileged user using the newly created user
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_user:
+ name: "{{ db_session_role2 }}"
+ state: "present"
+ password: "password"
+ role_attr_flags: "LOGIN"
+ login_user: "{{ pg_user }}"
+ session_role: "{{ db_session_role1 }}"
+ db: postgres
+
+- name: Create DB as session_role
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_db:
+ state: present
+ name: "{{ db_session_role1 }}"
+ login_user: "{{ pg_user }}"
+ session_role: "{{ db_session_role1 }}"
+ register: result
+
+- name: Create table to be able to grant privileges
+ become_user: "{{ pg_user }}"
+ become: true
+ shell: echo "CREATE TABLE test(i int); CREATE TABLE test2(i int);" | psql -AtXq "{{ db_session_role1 }}"
+
+- name: Grant all privileges on test1 table to low privileged user
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_privs:
+ db: "{{ db_session_role1 }}"
+ type: table
+ objs: test
+ roles: "{{ db_session_role2 }}"
+ login_user: "{{ pg_user }}"
+ privs: select
+ admin_option: true
+
+- name: Verify admin option was successful for grants
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_privs:
+ db: "{{ db_session_role1 }}"
+ type: table
+ objs: test
+ roles: "{{ db_session_role1 }}"
+ login_user: "{{ pg_user }}"
+ privs: select
+ session_role: "{{ db_session_role2 }}"
+
+- name: Verify no grants can be granted for test2 table
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_privs:
+ db: "{{ db_session_role1 }}"
+ type: table
+ objs: test2
+ roles: "{{ db_session_role1 }}"
+ login_user: "{{ pg_user }}"
+ privs: update
+ session_role: "{{ db_session_role2 }}"
+ ignore_errors: true
+ register: result
+
+- assert:
+ that:
+ - result is failed
+
+########################
+# Test trust_input param
+
+- name: Verify trust_input parameter
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_privs:
+ db: "{{ db_session_role1 }}"
+ type: table
+ objs: test2
+ roles: "{{ db_session_role1 }}"
+ login_user: "{{ pg_user }}"
+ privs: update
+ session_role: "{{ dangerous_name }}"
+ trust_input: false
+ ignore_errors: true
+ register: result
+
+- assert:
+ that:
+ - result is failed
+ - result.msg == 'Passed input \'{{ dangerous_name }}\' is potentially dangerous'
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_privs/tasks/test_target_role.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_privs/tasks/test_target_role.yml
new file mode 100644
index 000000000..42ece0bad
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_privs/tasks/test_target_role.yml
@@ -0,0 +1,120 @@
+# Setup
+- name: Create a test user
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_user:
+ name: "{{ db_user1 }}"
+ login_user: "{{ pg_user }}"
+ db: postgres
+
+- name: Create DB
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_db:
+ state: present
+ name: "{{ db_name }}"
+ owner: "{{ db_user1 }}"
+ login_user: "{{ pg_user }}"
+
+- name: Create a user to be given permissions and other tests
+ postgresql_user:
+ name: "{{ db_user2 }}"
+ state: present
+ encrypted: true
+ password: password
+ role_attr_flags: LOGIN
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+
+#######################################
+# Test default_privs with target_role #
+#######################################
+
+# Test
+- name: Grant default privileges for new table objects
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_privs:
+ db: "{{ db_name }}"
+ objs: TABLES
+ privs: SELECT
+ type: default_privs
+ role: "{{ db_user2 }}"
+ target_roles: "{{ db_user1 }}"
+ login_user: "{{ pg_user }}"
+ register: result
+
+# Checks
+- assert:
+ that: result is changed
+
+- name: Check that default privileges are set
+ become: true
+ become_user: "{{ pg_user }}"
+ shell: psql {{ db_name }} -c "SELECT defaclrole, defaclobjtype, defaclacl FROM pg_default_acl a JOIN pg_roles b ON a.defaclrole=b.oid;" -t
+ register: result
+
+- assert:
+ that: "'{{ db_user2 }}=r/{{ db_user1 }}' in '{{ result.stdout_lines[0] }}'"
+
+# Test
+- name: Revoke default privileges for new table objects
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_privs:
+ db: "{{ db_name }}"
+ state: absent
+ objs: TABLES
+ privs: SELECT
+ type: default_privs
+ role: "{{ db_user2 }}"
+ target_roles: "{{ db_user1 }}"
+ login_user: "{{ pg_user }}"
+ register: result
+
+# Checks
+- assert:
+ that: result is changed
+
+# Cleanup
+- name: Remove user given permissions
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_user:
+ name: "{{ db_user2 }}"
+ state: absent
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+
+- name: Remove user owner of objects
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_user:
+ name: "{{ db_user3 }}"
+ state: absent
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+
+- name: Destroy DBs
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_db:
+ state: absent
+ name: "{{ item }}"
+ login_user: "{{ pg_user }}"
+ loop:
+ - "{{ db_name }}"
+ - "{{ db_session_role1 }}"
+
+- name: Remove test users
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_user:
+ name: "{{ item }}"
+ state: absent
+ db: postgres
+ login_user: "{{ pg_user }}"
+ loop:
+ - "{{ db_user1 }}"
+ - "{{ db_session_role1 }}"
+ - "{{ db_session_role2 }}"
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_publication/aliases b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_publication/aliases
new file mode 100644
index 000000000..142e8aa07
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_publication/aliases
@@ -0,0 +1,3 @@
+destructive
+shippable/posix/group1
+skip/freebsd
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_publication/meta/main.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_publication/meta/main.yml
new file mode 100644
index 000000000..4ce5a5837
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_publication/meta/main.yml
@@ -0,0 +1,2 @@
+dependencies:
+ - setup_postgresql_db
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_publication/tasks/main.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_publication/tasks/main.yml
new file mode 100644
index 000000000..507c1e234
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_publication/tasks/main.yml
@@ -0,0 +1,8 @@
+####################################################################
+# WARNING: These are designed specifically for Ansible tests #
+# and should not be used as examples of how to write Ansible roles #
+####################################################################
+
+# Initial CI tests of postgresql_publication module
+- import_tasks: postgresql_publication_initial.yml
+ when: postgres_version_resp.stdout is version('10', '>=')
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_publication/tasks/postgresql_publication_initial.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_publication/tasks/postgresql_publication_initial.yml
new file mode 100644
index 000000000..584a4848b
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_publication/tasks/postgresql_publication_initial.yml
@@ -0,0 +1,436 @@
+# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) <aaklychkov@mail.ru>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+# The file for testing postgresql_copy module.
+
+- vars:
+ test_table1: acme1
+ test_table2: acme2
+ test_table3: acme3
+ test_pub: acme_publ
+ test_role: alice
+ dangerous_name: 'curious.anonymous"; SELECT * FROM information_schema.tables; --'
+ test_schema: acme_schema
+ test_db: acme_db
+ task_parameters: &task_parameters
+ become_user: '{{ pg_user }}'
+ become: true
+ register: result
+ pg_parameters: &pg_parameters
+ login_user: '{{ pg_user }}'
+ login_db: '{{ test_db }}'
+
+ block:
+ #################################################
+ # Test preparation, create database test objects:
+ - name: postgresql_publication - create test db
+ <<: *task_parameters
+ postgresql_db:
+ login_user: '{{ pg_user }}'
+ maintenance_db: postgres
+ name: '{{ test_db }}'
+
+ - name: postgresql_publication - create test schema
+ <<: *task_parameters
+ postgresql_schema:
+ <<: *pg_parameters
+ name: '{{ test_schema }}'
+
+ - name: postgresql_publication - create test role
+ <<: *task_parameters
+ postgresql_user:
+ <<: *pg_parameters
+ name: '{{ test_role }}'
+ role_attr_flags: SUPERUSER
+
+ - name: postgresql_publication - create test tables
+ <<: *task_parameters
+ postgresql_table:
+ <<: *pg_parameters
+ name: '{{ item }}'
+ columns:
+ - id int
+ loop:
+ - '{{ test_table1 }}'
+ - '{{ test_schema }}.{{ test_table2 }}'
+ - '{{ test_table3 }}'
+
+
+ ################
+ # Do main tests:
+
+ # Test
+ - name: postgresql_publication - create publication, check_mode
+ <<: *task_parameters
+ postgresql_publication:
+ <<: *pg_parameters
+ name: '{{ test_pub }}'
+ trust_input: false
+ check_mode: true
+
+ - assert:
+ that:
+ - result is changed
+ - result.exists == false
+ - result.queries == ["CREATE PUBLICATION \"{{ test_pub }}\" FOR ALL TABLES"]
+
+ # Check
+ - name: postgresql_publication - check that nothing has been changed
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: SELECT * FROM pg_publication WHERE pubname = '{{ test_pub }}'
+
+ - assert:
+ that:
+ - result.rowcount == 0
+
+ # Test
+ - name: postgresql_publication - create publication
+ <<: *task_parameters
+ postgresql_publication:
+ <<: *pg_parameters
+ name: '{{ test_pub }}'
+ trust_input: false
+
+ - assert:
+ that:
+ - result is changed
+ - result.exists == true
+ - result.queries == ["CREATE PUBLICATION \"{{ test_pub }}\" FOR ALL TABLES"]
+ - result.owner == '{{ pg_user }}'
+ - result.alltables == true
+ - result.tables == []
+ - result.parameters.publish != {}
+
+ # Check
+ - name: postgresql_publication - check that nothing has been changed
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: >
+ SELECT * FROM pg_publication WHERE pubname = '{{ test_pub }}'
+ AND pubowner = '10' AND puballtables = 't'
+
+ - assert:
+ that:
+ - result.rowcount == 1
+
+ # Test
+ - name: postgresql_publication - drop publication, check_mode
+ <<: *task_parameters
+ postgresql_publication:
+ <<: *pg_parameters
+ name: '{{ test_pub }}'
+ state: absent
+ trust_input: false
+ check_mode: true
+
+ - assert:
+ that:
+ - result is changed
+ - result.exists == true
+ - result.queries == ["DROP PUBLICATION \"{{ test_pub }}\""]
+ - result.owner == '{{ pg_user }}'
+ - result.alltables == true
+ - result.tables == []
+ - result.parameters.publish != {}
+
+ # Check
+ - name: postgresql_publication - check that nothing has been changed
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: SELECT * FROM pg_publication WHERE pubname = '{{ test_pub }}'
+
+ - assert:
+ that:
+ - result.rowcount == 1
+
+ # Test
+ - name: postgresql_publication - drop publication
+ <<: *task_parameters
+ postgresql_publication:
+ <<: *pg_parameters
+ name: '{{ test_pub }}'
+ state: absent
+ cascade: true
+
+ - assert:
+ that:
+ - result is changed
+ - result.exists == false
+ - result.queries == ["DROP PUBLICATION \"{{ test_pub }}\" CASCADE"]
+
+ # Check
+ - name: postgresql_publication - check that publication does not exist
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: SELECT * FROM pg_publication WHERE pubname = '{{ test_pub }}'
+
+ - assert:
+ that:
+ - result.rowcount == 0
+
+ # Test
+ - name: postgresql_publication - create publication with tables, owner, params
+ <<: *task_parameters
+ postgresql_publication:
+ <<: *pg_parameters
+ name: '{{ test_pub }}'
+ owner: '{{ test_role }}'
+ tables:
+ - '{{ test_table1 }}'
+ - '{{ test_schema }}.{{ test_table2 }}'
+ parameters:
+ publish: 'insert'
+ trust_input: false
+
+ - assert:
+ that:
+ - result is changed
+ - result.queries == ["CREATE PUBLICATION \"{{ test_pub }}\" FOR TABLE \"public\".\"{{ test_table1 }}\", \"{{ test_schema }}\".\"{{ test_table2 }}\" WITH (publish = 'insert')", "ALTER PUBLICATION \"{{ test_pub }}\" OWNER TO \"{{ test_role }}\""]
+ - result.owner == '{{ test_role }}'
+ - result.tables == ["\"public\".\"{{ test_table1 }}\"", "\"{{ test_schema }}\".\"{{ test_table2 }}\""]
+ - result.parameters.publish.insert == true
+ - result.parameters.publish.delete == false
+
+ # Check 1
+ - name: postgresql_publication - check that test publication exists
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: >
+ SELECT * FROM pg_publication WHERE pubname = '{{ test_pub }}'
+ AND pubowner != '10' AND puballtables = 'f' AND pubinsert = 't' AND pubdelete = 'f'
+
+ - assert:
+ that:
+ - result.rowcount == 1
+
+ # Check 2
+ - name: postgresql_publication - check that test_table1 from schema public is in publication
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: SELECT * FROM pg_publication_tables WHERE pubname = '{{ test_pub }}' AND schemaname = 'public'
+
+ - assert:
+ that:
+ - result.rowcount == 1
+
+ # Check 3
+ - name: postgresql_publication - check that test_table2 from test schema is in publication
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: SELECT * FROM pg_publication_tables WHERE pubname = '{{ test_pub }}' AND schemaname = '{{ test_schema }}'
+
+ - assert:
+ that:
+ - result.rowcount == 1
+
+ # Test
+ - name: postgresql_publication - test trust_input parameter
+ <<: *task_parameters
+ postgresql_publication:
+ <<: *pg_parameters
+ name: '{{ test_pub }}'
+ session_role: '{{ dangerous_name }}'
+ owner: '{{ dangerous_name }}'
+ trust_input: false
+ ignore_errors: true
+
+ - assert:
+ that:
+ - result is failed
+ - result.msg is search('is potentially dangerous')
+
+ # Test
+ - name: postgresql_publication - add table to publication, change owner, check_mode
+ <<: *task_parameters
+ postgresql_publication:
+ <<: *pg_parameters
+ name: '{{ test_pub }}'
+ owner: '{{ pg_user }}'
+ tables:
+ - '{{ test_table1 }}'
+ - '{{ test_schema }}.{{ test_table2 }}'
+ - '{{ test_table3 }}'
+ trust_input: false
+ check_mode: true
+
+ - assert:
+ that:
+ - result is changed
+ - result.queries == ["ALTER PUBLICATION \"{{ test_pub }}\" ADD TABLE \"public\".\"{{ test_table3 }}\"", "ALTER PUBLICATION \"{{ test_pub }}\" OWNER TO \"{{ pg_user }}\""]
+ - result.tables == ["\"public\".\"{{ test_table1 }}\"", "\"{{ test_schema }}\".\"{{ test_table2 }}\""]
+
+ # Check
+ - name: postgresql_publication - check that nothing changes after the previous step
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: >
+ SELECT * FROM pg_publication WHERE pubname = '{{ test_pub }}'
+ AND pubowner != '10' AND puballtables = 'f' AND pubinsert = 't' AND pubupdate = 't'
+
+ - assert:
+ that:
+ - result.rowcount == 0
+
+ # Check
+ - name: postgresql_publication - check that 2 tables are in publication
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: SELECT * FROM pg_publication_tables WHERE pubname = '{{ test_pub }}'
+
+ - assert:
+ that:
+ - result.rowcount == 2
+
+ # Test
+ - name: postgresql_publication - add table to publication, change owner
+ <<: *task_parameters
+ postgresql_publication:
+ <<: *pg_parameters
+ name: '{{ test_pub }}'
+ owner: '{{ pg_user }}'
+ tables:
+ - '{{ test_table1 }}'
+ - '{{ test_schema }}.{{ test_table2 }}'
+ - '{{ test_table3 }}'
+ trust_input: false
+
+ - assert:
+ that:
+ - result is changed
+ - result.queries == ["ALTER PUBLICATION \"{{ test_pub }}\" ADD TABLE \"public\".\"{{ test_table3 }}\"", "ALTER PUBLICATION \"{{ test_pub }}\" OWNER TO \"{{ pg_user }}\""]
+ - result.tables == ["\"public\".\"{{ test_table1 }}\"", "\"{{ test_schema }}\".\"{{ test_table2 }}\"", "\"public\".\"{{ test_table3 }}\""]
+
+ # Check 1
+ - name: postgresql_publication - check owner has been changed
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: >
+ SELECT * FROM pg_publication WHERE pubname = '{{ test_pub }}' AND pubowner = '10'
+
+ - assert:
+ that:
+ - result.rowcount == 1
+
+ # Check 2
+ - name: postgresql_publication - check that 3 tables are in publication
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: SELECT * FROM pg_publication_tables WHERE pubname = '{{ test_pub }}'
+
+ - assert:
+ that:
+ - result.rowcount == 3
+
+ # Test
+ - name: postgresql_publication - remove table from publication, check_mode
+ <<: *task_parameters
+ postgresql_publication:
+ <<: *pg_parameters
+ name: '{{ test_pub }}'
+ tables:
+ - '{{ test_table1 }}'
+ - '{{ test_schema }}.{{ test_table2 }}'
+ parameters:
+ publish: 'insert'
+ trust_input: false
+ check_mode: true
+
+ - assert:
+ that:
+ - result is changed
+ - result.queries == ["ALTER PUBLICATION \"{{ test_pub }}\" DROP TABLE \"public\".\"{{ test_table3 }}\""]
+ - result.tables == ["\"public\".\"{{ test_table1 }}\"", "\"{{ test_schema }}\".\"{{ test_table2 }}\"", "\"public\".\"{{ test_table3 }}\""]
+
+ # Check 1
+ - name: postgresql_publication - check that 3 tables are in publication
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: SELECT * FROM pg_publication_tables WHERE pubname = '{{ test_pub }}'
+
+ - assert:
+ that:
+ - result.rowcount == 3
+
+ # Check 2
+ - name: postgresql_publication - check no parameters have been changed
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: SELECT * FROM pg_publication WHERE pubname = '{{ test_pub }}' AND pubinsert = 't'
+
+ - assert:
+ that:
+ - result.rowcount == 1
+
+ # Test
+ - name: postgresql_publication - remove table from publication
+ <<: *task_parameters
+ postgresql_publication:
+ <<: *pg_parameters
+ name: '{{ test_pub }}'
+ tables:
+ - '{{ test_table1 }}'
+ - '{{ test_schema }}.{{ test_table2 }}'
+ parameters:
+ publish: 'delete'
+ trust_input: false
+
+ - assert:
+ that:
+ - result is changed
+ - result.queries == ["ALTER PUBLICATION \"{{ test_pub }}\" DROP TABLE \"public\".\"{{ test_table3 }}\"", "ALTER PUBLICATION \"{{ test_pub }}\" SET (publish = 'delete')"]
+ - result.tables == ["\"public\".\"{{ test_table1 }}\"", "\"{{ test_schema }}\".\"{{ test_table2 }}\""]
+
+ # Check 1
+ - name: postgresql_publication - check that 2 tables are in publication
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: SELECT * FROM pg_publication_tables WHERE pubname = '{{ test_pub }}'
+
+ - assert:
+ that:
+ - result.rowcount == 2
+
+ # Check 2
+ - name: postgresql_publication - check parameter has been changed
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: SELECT * FROM pg_publication WHERE pubname = '{{ test_pub }}' AND pubinsert = 'f'
+
+ - assert:
+ that:
+ - result.rowcount == 1
+
+ always:
+ ###########
+ # Clean up:
+
+ - name: postgresql_publication - remove test db
+ <<: *task_parameters
+ postgresql_db:
+ login_user: '{{ pg_user }}'
+ maintenance_db: postgres
+ name: '{{ test_db }}'
+ state: absent
+
+ - name: postgresql_publication - remove test role
+ <<: *task_parameters
+ postgresql_user:
+ login_user: '{{ pg_user }}'
+ login_db: postgres
+ name: '{{ test_role }}'
+ state: absent
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_query/aliases b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_query/aliases
new file mode 100644
index 000000000..a4c92ef85
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_query/aliases
@@ -0,0 +1,2 @@
+destructive
+shippable/posix/group1
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_query/files/test0.sql b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_query/files/test0.sql
new file mode 100644
index 000000000..e8a5ca03d
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_query/files/test0.sql
@@ -0,0 +1,6 @@
+SELECT version();
+
+SELECT story FROM test_table
+ WHERE id = %s OR story = 'Данные';
+
+
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_query/files/test1.sql b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_query/files/test1.sql
new file mode 100644
index 000000000..028c192d7
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_query/files/test1.sql
@@ -0,0 +1,10 @@
+CREATE FUNCTION add(integer, integer) RETURNS integer
+ AS 'select $1 + $2;'
+ LANGUAGE SQL
+ IMMUTABLE
+ RETURNS NULL ON NULL INPUT;
+
+SELECT story FROM test_table
+ WHERE id = %s OR story = 'Данные';
+
+SELECT version();
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_query/meta/main.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_query/meta/main.yml
new file mode 100644
index 000000000..4ce5a5837
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_query/meta/main.yml
@@ -0,0 +1,2 @@
+dependencies:
+ - setup_postgresql_db
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_query/tasks/main.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_query/tasks/main.yml
new file mode 100644
index 000000000..7b24dbf92
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_query/tasks/main.yml
@@ -0,0 +1,7 @@
+####################################################################
+# WARNING: These are designed specifically for Ansible tests #
+# and should not be used as examples of how to write Ansible roles #
+####################################################################
+
+# Initial CI tests of postgresql_query module
+- import_tasks: postgresql_query_initial.yml
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_query/tasks/postgresql_query_initial.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_query/tasks/postgresql_query_initial.yml
new file mode 100644
index 000000000..5d447d608
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_query/tasks/postgresql_query_initial.yml
@@ -0,0 +1,604 @@
+- vars:
+ pg_parameters: &pg_parameters
+ login_user: '{{ pg_user }}'
+ login_db: postgres
+ connect_params:
+ connect_timeout: 30
+
+ block:
+
+ - name: postgresql_query - drop test table if exists
+ become_user: '{{ pg_user }}'
+ become: true
+ shell: psql postgres -U "{{ pg_user }}" -t -c "DROP TABLE IF EXISTS test_table;"
+ ignore_errors: true
+
+ - name: postgresql_query - create test table called test_table
+ become_user: '{{ pg_user }}'
+ become: true
+ shell: psql postgres -U "{{ pg_user }}" -t -c "CREATE TABLE test_table (id int, story text);"
+ ignore_errors: true
+
+ - name: postgresql_query - insert some data into test_table
+ become_user: '{{ pg_user }}'
+ become: true
+ shell: psql postgres -U "{{ pg_user }}" -t -c "INSERT INTO test_table (id, story) VALUES (1, 'first'), (2, 'second'), (3, 'third');"
+ ignore_errors: true
+
+ - name: Copy script files
+ become: true
+ copy:
+ src: '{{ item }}'
+ dest: '~{{ pg_user }}/{{ item }}'
+ owner: '{{ pg_user }}'
+ force: true
+ loop:
+ - test0.sql
+ - test1.sql
+ register: sql_file_created
+ ignore_errors: true
+
+ - name: postgresql_query - analyze test_table
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ <<: *pg_parameters
+ query: ANALYZE test_table
+ register: result
+ ignore_errors: true
+
+ - assert:
+ that:
+ - result is changed
+ - result.query == 'ANALYZE test_table'
+ - result.query_list == ['ANALYZE test_table']
+ - result.rowcount == 0
+ - result.statusmessage == 'ANALYZE'
+ - result.query_result == {}
+ - result.query_all_results == [{}]
+
+ - name: postgresql_query - run queries from SQL script
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ <<: *pg_parameters
+ path_to_script: ~{{ pg_user }}/test0.sql
+ positional_args:
+ - 1
+ encoding: UTF-8
+ as_single_query: false
+ register: result
+ ignore_errors: true
+ when: sql_file_created
+
+ - assert:
+ that:
+ - result is not changed
+ - result.query == "\n\nSELECT story FROM test_table\n WHERE id = 1 OR story = 'Данные'"
+ - result.query_result[0].story == 'first'
+ - result.rowcount == 2
+ - result.statusmessage == 'SELECT 1' or result.statusmessage == 'SELECT'
+ when: sql_file_created
+
+ - name: postgresql_query - simple select query to test_table
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ <<: *pg_parameters
+ query: SELECT * FROM test_table
+ register: result
+ ignore_errors: true
+
+ - assert:
+ that:
+ - result is not changed
+ - result.query == 'SELECT * FROM test_table'
+ - result.rowcount == 3
+ - result.statusmessage == 'SELECT 3' or result.statusmessage == 'SELECT'
+ - result.query_result[0].id == 1
+ - result.query_result[1].id == 2
+ - result.query_result[2].id == 3
+ - result.query_result[0].story == 'first'
+ - result.query_result[1].story == 'second'
+ - result.query_result[2].story == 'third'
+
+ - name: postgresql_query - select query with named args
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ <<: *pg_parameters
+ query: SELECT id FROM test_table WHERE id = %(id_val)s AND story = %(story_val)s
+ named_args:
+ id_val: 1
+ story_val: first
+ register: result
+ ignore_errors: true
+
+ - assert:
+ that:
+ - result is not changed
+ - result.query == "SELECT id FROM test_table WHERE id = 1 AND story = 'first'" or result.query == "SELECT id FROM test_table WHERE id = 1 AND story = E'first'"
+ - result.rowcount == 1
+ - result.statusmessage == 'SELECT 1' or result.statusmessage == 'SELECT'
+ - result.query_result[0].id == 1
+
+ - name: postgresql_query - select query with positional arguments
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ <<: *pg_parameters
+ query: SELECT story FROM test_table WHERE id = %s AND story = %s
+ positional_args:
+ - 2
+ - second
+ register: result
+ ignore_errors: true
+
+ - assert:
+ that:
+ - result is not changed
+ - result.query == "SELECT story FROM test_table WHERE id = 2 AND story = 'second'" or result.query == "SELECT story FROM test_table WHERE id = 2 AND story = E'second'"
+ - result.rowcount == 1
+ - result.statusmessage == 'SELECT 1' or result.statusmessage == 'SELECT'
+ - result.query_result[0].story == 'second'
+
+ - name: postgresql_query - simple update query
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ <<: *pg_parameters
+ query: UPDATE test_table SET story = 'new' WHERE id = 3
+ register: result
+ ignore_errors: true
+
+ - assert:
+ that:
+ - result is changed
+ - result.query == "UPDATE test_table SET story = 'new' WHERE id = 3"
+ - result.rowcount == 1
+ - result.statusmessage == 'UPDATE 1'
+ - result.query_result == {}
+
+ - name: check the previous update
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ <<: *pg_parameters
+ query: SELECT * FROM test_table WHERE story = 'new' AND id = 3
+ register: result
+
+ - assert:
+ that:
+ - result.rowcount == 1
+
+ - name: postgresql_query - simple update query in check_mode
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ <<: *pg_parameters
+ query: UPDATE test_table SET story = 'CHECK_MODE' WHERE id = 3
+ register: result
+ check_mode: true
+
+ - assert:
+ that:
+ - result is changed
+ - result.query == "UPDATE test_table SET story = 'CHECK_MODE' WHERE id = 3"
+ - result.rowcount == 1
+ - result.statusmessage == 'UPDATE 1'
+ - result.query_result == {}
+
+ - name: check the previous update that nothing has been changed
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ <<: *pg_parameters
+ query: SELECT * FROM test_table WHERE story = 'CHECK_MODE' AND id = 3
+ register: result
+
+ - assert:
+ that:
+ - result.rowcount == 0
+
+ - name: postgresql_query - try to update not existing row
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ <<: *pg_parameters
+ query: UPDATE test_table SET story = 'new' WHERE id = 100
+ register: result
+ ignore_errors: true
+
+ - assert:
+ that:
+ - result is not changed
+ - result.query == "UPDATE test_table SET story = 'new' WHERE id = 100"
+ - result.rowcount == 0
+ - result.statusmessage == 'UPDATE 0'
+ - result.query_result == {}
+
+ - name: postgresql_query - insert query
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ <<: *pg_parameters
+ query: INSERT INTO test_table (id, story) VALUES (%s, %s)
+ positional_args:
+ - 4
+ - fourth
+ register: result
+ ignore_errors: true
+
+ - assert:
+ that:
+ - result is changed
+ - result.query == "INSERT INTO test_table (id, story) VALUES (4, 'fourth')" or result.query == "INSERT INTO test_table (id, story) VALUES (4, E'fourth')"
+ - result.rowcount == 1
+ - result.statusmessage == 'INSERT 0 1'
+ - result.query_result == {}
+
+ - name: postgresql_query - truncate test_table
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ <<: *pg_parameters
+ query: TRUNCATE test_table
+ register: result
+ ignore_errors: true
+
+ - assert:
+ that:
+ - result is changed
+ - result.query == "TRUNCATE test_table"
+ - result.rowcount == 0
+ - result.statusmessage == 'TRUNCATE TABLE'
+ - result.query_result == {}
+
+ - name: postgresql_query - alter test_table
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ <<: *pg_parameters
+ query: ALTER TABLE test_table ADD COLUMN foo int
+ register: result
+ ignore_errors: true
+
+ - assert:
+ that:
+ - result is changed
+ - result.query == "ALTER TABLE test_table ADD COLUMN foo int"
+ - result.rowcount == 0
+ - result.statusmessage == 'ALTER TABLE'
+
+ - name: postgresql_query - vacuum without autocommit must fail
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ <<: *pg_parameters
+ query: VACUUM
+ register: result
+ ignore_errors: true
+
+ - assert:
+ that:
+ - result.failed == true
+
+ - name: postgresql_query - autocommit in check_mode must fail
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ <<: *pg_parameters
+ query: VACUUM
+ autocommit: true
+ check_mode: true
+ register: result
+ ignore_errors: true
+
+ - assert:
+ that:
+ - result.failed == true
+ - result.msg == "Using autocommit is mutually exclusive with check_mode"
+
+ - name: postgresql_query - vacuum with autocommit
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ <<: *pg_parameters
+ query: VACUUM
+ autocommit: true
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result.query == "VACUUM"
+ - result.rowcount == 0
+ - result.statusmessage == 'VACUUM'
+ - result.query_result == {}
+
+ - name: postgresql_query - create test table for issue 59955
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_table:
+ login_user: '{{ pg_user }}'
+ login_db: postgres
+ name: test_array_table
+ columns:
+ - arr_col int[]
+ when: postgres_version_resp.stdout is version('9.4', '>=')
+
+ - set_fact:
+ my_list:
+ - 1
+ - 2
+ - 3
+ my_arr: '{1, 2, 3}'
+ when: postgres_version_resp.stdout is version('9.4', '>=')
+
+ - name: postgresql_query - insert array into test table by positional args
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ <<: *pg_parameters
+ query: INSERT INTO test_array_table (arr_col) VALUES (%s)
+ positional_args:
+ - '{{ my_list }}'
+ register: result
+ when: postgres_version_resp.stdout is version('9.4', '>=')
+
+ - assert:
+ that:
+ - result is changed
+ - result.query == "INSERT INTO test_array_table (arr_col) VALUES ('{1, 2, 3}')"
+ when: postgres_version_resp.stdout is version('9.4', '>=')
+
+ - name: postgresql_query - select array from test table by passing positional_args
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ <<: *pg_parameters
+ query: SELECT * FROM test_array_table WHERE arr_col = %s
+ positional_args:
+ - '{{ my_list }}'
+ register: result
+ when: postgres_version_resp.stdout is version('9.4', '>=')
+
+ - assert:
+ that:
+ - result is not changed
+ - result.query == "SELECT * FROM test_array_table WHERE arr_col = '{1, 2, 3}'"
+ - result.rowcount == 1
+ when: postgres_version_resp.stdout is version('9.4', '>=')
+
+ - name: postgresql_query - select array from test table by passing named_args
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ <<: *pg_parameters
+ query: SELECT * FROM test_array_table WHERE arr_col = %(arr_val)s
+ named_args:
+ arr_val:
+ - '{{ my_list }}'
+ register: result
+ when: postgres_version_resp.stdout is version('9.4', '>=')
+
+ - assert:
+ that:
+ - result is not changed
+ - result.query == "SELECT * FROM test_array_table WHERE arr_col = '{1, 2, 3}'"
+ - result.rowcount == 1
+ when: postgres_version_resp.stdout is version('9.4', '>=')
+
+ - name: postgresql_query - select array from test table by passing positional_args as a string
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ <<: *pg_parameters
+ query: SELECT * FROM test_array_table WHERE arr_col = %s
+ positional_args:
+ - '{{ my_arr|string }}'
+ trust_input: true
+ register: result
+ when: postgres_version_resp.stdout is version('9.4', '>=')
+
+ - assert:
+ that:
+ - result is not changed
+ - result.query == "SELECT * FROM test_array_table WHERE arr_col = '{1, 2, 3}'"
+ - result.rowcount == 1
+ when: postgres_version_resp.stdout is version('9.4', '>=')
+
+ - name: postgresql_query - test trust_input parameter
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ <<: *pg_parameters
+ session_role: 'curious.anonymous"; SELECT * FROM information_schema.tables; --'
+ query: SELECT version()
+ trust_input: false
+ ignore_errors: true
+ register: result
+
+ - assert:
+ that:
+ - result is failed
+ - result.msg is search('is potentially dangerous')
+
+ - name: postgresql_query - clean up
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_table:
+ login_user: '{{ pg_user }}'
+ login_db: postgres
+ name: test_array_table
+ state: absent
+ when: postgres_version_resp.stdout is version('9.4', '>=')
+
+ #############################
+ # Check search_path parameter
+
+ - name: postgresql_set - create test schemas
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_schema:
+ login_user: '{{ pg_user }}'
+ login_db: postgres
+ name: '{{ item }}'
+ loop:
+ - query_test1
+ - query_test2
+
+ - name: postgresql_set - create test tables
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_table:
+ login_user: '{{ pg_user }}'
+ login_db: postgres
+ name: '{{ item }}'
+ columns:
+ - id int
+ loop:
+ - 'query_test1.test1'
+ - 'query_test2.test2'
+
+ - name: postgresql_query - insert data
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ <<: *pg_parameters
+ query: 'INSERT INTO {{ item }} (id) VALUES (1)'
+ search_path:
+ - query_test1
+ - query_test2
+ loop:
+ - test1
+ - test2
+
+ - name: postgresql_query - get data
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ <<: *pg_parameters
+ query: 'SELECT id FROM test1'
+ search_path:
+ - query_test1
+ - query_test2
+ register: result
+
+ - assert:
+ that:
+ - result.rowcount == 1
+
+ - name: postgresql_query - get data, must fail
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ <<: *pg_parameters
+ query: 'SELECT id FROM test1'
+ register: result
+ ignore_errors: true
+
+ - assert:
+ that:
+ - result is failed
+
+ # Tests for the as_single_query option
+ - name: Run queries from SQL script as a single query
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ <<: *pg_parameters
+ path_to_script: ~{{ pg_user }}/test1.sql
+ positional_args:
+ - 1
+ encoding: UTF-8
+ as_single_query: true
+ register: result
+
+ - name: >
+ Must pass. Not changed because we can only
+ check statusmessage of the last query
+ assert:
+ that:
+ - result is not changed
+ - result.statusmessage == 'SELECT 1' or result.statusmessage == 'SELECT'
+ - result.query_list[0] == "CREATE FUNCTION add(integer, integer) RETURNS integer\n AS 'select $1 + $2;'\n LANGUAGE SQL\n IMMUTABLE\n RETURNS NULL ON NULL INPUT;\n\nSELECT story FROM test_table\n WHERE id = %s OR story = 'Данные';\n\nSELECT version();\n"
+
+ #############################################################################
+ # Issue https://github.com/ansible-collections/community.postgresql/issues/45
+ - name: Create table containing a decimal value
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ <<: *pg_parameters
+ query: CREATE TABLE blabla (id int, num decimal)
+
+ - name: Insert data
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ <<: *pg_parameters
+ query: INSERT INTO blabla (id, num) VALUES (1, 1::decimal)
+
+ - name: Get data
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ <<: *pg_parameters
+ query: SELECT * FROM blabla
+ register: result
+
+ - assert:
+ that:
+ - result.rowcount == 1
+
+ #############################################################################
+ # Issue https://github.com/ansible-collections/community.postgresql/issues/47
+ - name: Get datetime.timedelta value
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ <<: *pg_parameters
+ query: "SELECT EXTRACT(epoch from make_interval(secs => 3)) AS extract"
+ register: result
+ when: postgres_version_resp.stdout is version('10', '>=')
+
+ - assert:
+ that:
+ - result.rowcount == 1
+ - result.query_result[0]["extract"] == 3 or result.query_result[0]["extract"] == 3.0
+ when: postgres_version_resp.stdout is version('10', '>=')
+
+ - name: Get interval value
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ <<: *pg_parameters
+ query: "SELECT make_interval(secs => 3)"
+ register: result
+ when: postgres_version_resp.stdout is version('10', '>=')
+
+ - assert:
+ that:
+ - result.rowcount == 1
+ - result.query_result[0]["make_interval"] == "0:00:03"
+ when: postgres_version_resp.stdout is version('10', '>=')
+
+ ##############################################################################
+ # Issue https://github.com/ansible-collections/community.postgresql/issues/312
+ - name: Run several queries
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_query:
+ <<: *pg_parameters
+ query:
+ - SELECT 1
+ - SELECT 1
+ - SELECT 1
+ register: result
+
+ - assert:
+ that:
+ - result.rowcount == 3
+ - result.query_result == [{"?column?": 1}]
+ - 'result.query_all_results == [[{"?column?": 1}], [{"?column?": 1}], [{"?column?": 1}]]'
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_schema/aliases b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_schema/aliases
new file mode 100644
index 000000000..a4c92ef85
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_schema/aliases
@@ -0,0 +1,2 @@
+destructive
+shippable/posix/group1
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_schema/defaults/main.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_schema/defaults/main.yml
new file mode 100644
index 000000000..ff6dd5cb9
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_schema/defaults/main.yml
@@ -0,0 +1,7 @@
+---
+db_name: 'ansible_db'
+db_user1: 'ansible_db_user1'
+db_user2: 'ansible_db_user2'
+dangerous_name: 'curious.anonymous"; SELECT * FROM information_schema.tables; --'
+db_session_role1: 'session_role1'
+db_session_role2: 'session_role2'
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_schema/meta/main.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_schema/meta/main.yml
new file mode 100644
index 000000000..4ce5a5837
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_schema/meta/main.yml
@@ -0,0 +1,2 @@
+dependencies:
+ - setup_postgresql_db
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_schema/tasks/main.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_schema/tasks/main.yml
new file mode 100644
index 000000000..d894dd040
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_schema/tasks/main.yml
@@ -0,0 +1,9 @@
+####################################################################
+# WARNING: These are designed specifically for Ansible tests #
+# and should not be used as examples of how to write Ansible roles #
+####################################################################
+
+- import_tasks: postgresql_schema_session_role.yml
+
+# Initial CI tests of postgresql_schema module
+- import_tasks: postgresql_schema_initial.yml
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_schema/tasks/postgresql_schema_initial.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_schema/tasks/postgresql_schema_initial.yml
new file mode 100644
index 000000000..58832f049
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_schema/tasks/postgresql_schema_initial.yml
@@ -0,0 +1,331 @@
+---
+
+# Setup
+- name: Create test roles
+ postgresql_user:
+ name: "{{ item }}"
+ state: present
+ encrypted: true
+ password: password
+ role_attr_flags: LOGIN
+ db: postgres
+ login_user: "{{ pg_user }}"
+ loop:
+ - "{{ db_user1 }}"
+ - "{{ db_user2 }}"
+
+- name: Create DB
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_db:
+ state: present
+ name: "{{ db_name }}"
+ owner: "{{ db_user1 }}"
+ login_user: "{{ pg_user }}"
+
+# Test: CREATE SCHEMA in checkmode
+- name: Create a new schema with name "acme" in check_mode
+ become_user: "{{ pg_user }}"
+ become: true
+ check_mode: true
+ postgresql_schema:
+ database: "{{ db_name }}"
+ name: acme
+ login_user: "{{ pg_user }}"
+ register: result
+
+# Checks
+- assert:
+ that:
+ - result is changed
+ - result.schema == 'acme'
+
+- name: Check that the new schema "acme" not exists
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_query:
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ query: "SELECT schema_name FROM information_schema.schemata WHERE schema_name = 'acme'"
+ register: result
+
+- assert:
+ that:
+ - result.rowcount == 0
+
+# Test: CREATE SCHEMA
+- name: Create a new schema with name "acme"
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_schema:
+ database: "{{ db_name }}"
+ name: acme
+ login_user: "{{ pg_user }}"
+ trust_input: true
+ register: result
+
+# Checks
+- assert:
+ that:
+ - result is changed
+ - result.schema == 'acme'
+ - result.queries == [ 'CREATE SCHEMA "acme"' ]
+
+- name: Check that the new schema "acme" exists
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_query:
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ query: "SELECT schema_name FROM information_schema.schemata WHERE schema_name = 'acme'"
+ register: result
+
+- assert:
+ that:
+ - result.rowcount == 1
+
+# Test: DROP SCHEMA in checkmode
+- name: Drop schema "acme" in check_mode
+ become_user: "{{ pg_user }}"
+ become: true
+ check_mode: true
+ postgresql_schema:
+ database: "{{ db_name }}"
+ name: acme
+ state: absent
+ login_user: "{{ pg_user }}"
+ register: result
+
+# Checks
+- assert:
+ that:
+ - result is not changed
+
+- name: Check that the new schema "acme" still exists
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_query:
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ query: "SELECT schema_name FROM information_schema.schemata WHERE schema_name = 'acme'"
+ register: result
+
+- assert:
+ that:
+ - result.rowcount == 1
+
+# Test: DROP SCHEMA
+- name: Drop schema "acme"
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_schema:
+ database: "{{ db_name }}"
+ name: acme
+ state: absent
+ login_user: "{{ pg_user }}"
+ register: result
+
+# Checks
+- assert:
+ that:
+ - result is changed
+ - result.queries == [ 'DROP SCHEMA "acme"' ]
+
+- name: Check that no schema "acme" exists
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_query:
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ query: "SELECT schema_name FROM information_schema.schemata WHERE schema_name = 'acme'"
+ register: result
+ ignore_errors: true
+
+- assert:
+ that:
+ - result.rowcount == 0
+
+# Test: trust_input parameter
+- name: Create a new schema with potentially dangerous owner name
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_schema:
+ database: "{{ db_name }}"
+ name: acme
+ login_user: "{{ pg_user }}"
+ owner: "{{ dangerous_name }}"
+ trust_input: false
+ register: result
+ ignore_errors: true
+
+# Checks
+- assert:
+ that:
+ - result is failed
+ - result.msg == 'Passed input \'{{ dangerous_name }}\' is potentially dangerous'
+
+# Test: CREATE SCHEMA; WITH TABLE for DROP CASCADE test
+- name: Create a new schema "acme"
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_schema:
+ database: "{{ db_name }}"
+ name: acme
+ login_user: "{{ pg_user }}"
+ register: result
+
+- name: Create table in schema for DROP CASCADE check
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ query: "CREATE TABLE acme.table1()"
+ register: result2
+
+# Checks
+- assert:
+ that:
+ - result is changed
+ - result.schema == 'acme'
+ - result.queries == [ 'CREATE SCHEMA "acme"' ]
+ - result2.changed == true
+ - result2.statusmessage == 'CREATE TABLE'
+
+- name: Check that the new schema "acme" exists
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_query:
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ query: "SELECT schema_name,schema_owner FROM information_schema.schemata WHERE schema_name = 'acme'"
+ register: result
+
+- assert:
+ that:
+ - result.rowcount == 1
+
+- name: Check that the new table "table1" in schema 'acme' exists
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_query:
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ query: "SELECT EXISTS (SELECT 1 FROM pg_tables WHERE schemaname = 'acme' AND tablename = 'table1')"
+ register: result
+
+- assert:
+ that:
+ - result.rowcount == 1
+
+# Test: DROP SCHEMA ... CASCADE;
+- name: Drop schema "acme" with cascade
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_schema:
+ database: "{{ db_name }}"
+ name: acme
+ state: absent
+ cascade_drop: true
+ login_user: "{{ pg_user }}"
+ register: result
+
+# Checks
+- assert:
+ that:
+ - result is changed
+ - result.queries == [ 'DROP SCHEMA "acme" CASCADE' ]
+
+- name: Check that no schema "acme" exists
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_query:
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ query: "SELECT schema_name FROM information_schema.schemata WHERE schema_name = 'acme'"
+ register: result
+ ignore_errors: true
+
+- assert:
+ that:
+ - result.rowcount == 0
+
+# Test: CREATE SCHEMA WITH OWNER ...;
+- name: Create a new schema "acme" with a user "{{ db_user2 }}" who will own it
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_schema:
+ database: "{{ db_name }}"
+ name: acme
+ owner: "{{ db_user2 }}"
+ login_user: "{{ pg_user }}"
+ register: result
+
+# Checks
+- assert:
+ that:
+ - result is changed
+ - result.schema == 'acme'
+ - result.queries == [ 'CREATE SCHEMA "acme" AUTHORIZATION "{{ db_user2 }}"' ]
+
+- name: Check that the new schema "acme" exists and "{{ db_user2 }}" own it
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_query:
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ query: "SELECT schema_name,schema_owner FROM information_schema.schemata WHERE schema_name = 'acme' AND schema_owner = '{{ db_user2 }}'"
+ register: result
+ ignore_errors: true
+
+- assert:
+ that:
+ - result.rowcount == 1
+
+# Test: DROP SCHEMA
+- name: Drop schema "acme"
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_schema:
+ database: "{{ db_name }}"
+ name: acme
+ state: absent
+ login_user: "{{ pg_user }}"
+ register: result
+
+# Checks
+- assert:
+ that:
+ - result is changed
+ - result.queries == [ 'DROP SCHEMA "acme"' ]
+
+- name: Check that no schema "acme" exists
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_query:
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ query: "SELECT schema_name FROM information_schema.schemata WHERE schema_name = 'acme'"
+ register: result
+ ignore_errors: true
+
+- assert:
+ that:
+ - result.rowcount == 0
+
+
+# Cleanup
+- name: Remove user
+ postgresql_user:
+ name: "{{ db_user2 }}"
+ state: absent
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+
+- name: Destroy DB
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_db:
+ state: absent
+ name: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_schema/tasks/postgresql_schema_session_role.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_schema/tasks/postgresql_schema_session_role.yml
new file mode 100644
index 000000000..4b8af75ff
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_schema/tasks/postgresql_schema_session_role.yml
@@ -0,0 +1,78 @@
+- name: Create a high privileged user
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_user:
+ name: "{{ db_session_role1 }}"
+ state: "present"
+ password: "password"
+ role_attr_flags: "CREATEDB,LOGIN,CREATEROLE"
+ login_user: "{{ pg_user }}"
+ db: postgres
+
+- name: Create DB as session_role
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_db:
+ state: present
+ name: "{{ db_session_role1 }}"
+ login_user: "{{ pg_user }}"
+ session_role: "{{ db_session_role1 }}"
+ register: result
+
+- name: Create schema in own database
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_schema:
+ database: "{{ db_session_role1 }}"
+ login_user: "{{ pg_user }}"
+ name: "{{ db_session_role1 }}"
+ session_role: "{{ db_session_role1 }}"
+
+- name: Create schema in own database, should be owned by session_role
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_schema:
+ database: "{{ db_session_role1 }}"
+ login_user: "{{ pg_user }}"
+ name: "{{ db_session_role1 }}"
+ owner: "{{ db_session_role1 }}"
+ register: result
+
+- assert:
+ that:
+ - result is not changed
+
+- name: Fail when creating schema in postgres database as a regular user
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_schema:
+ database: postgres
+ login_user: "{{ pg_user }}"
+ name: "{{ db_session_role1 }}"
+ session_role: "{{ db_session_role1 }}"
+ ignore_errors: true
+ register: result
+
+- assert:
+ that:
+ - result is failed
+
+- name: Drop test db
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_db:
+ state: absent
+ name: "{{ db_session_role1 }}"
+ login_user: "{{ pg_user }}"
+
+- name: Drop test users
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_user:
+ name: "{{ item }}"
+ state: absent
+ login_user: "{{ pg_user }}"
+ db: postgres
+ with_items:
+ - "{{ db_session_role1 }}"
+ - "{{ db_session_role2 }}"
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/aliases b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/aliases
new file mode 100644
index 000000000..a4c92ef85
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/aliases
@@ -0,0 +1,2 @@
+destructive
+shippable/posix/group1
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/defaults/main.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/defaults/main.yml
new file mode 100644
index 000000000..35e711215
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/defaults/main.yml
@@ -0,0 +1 @@
+db_default: postgres
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/files/test0.sql b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/files/test0.sql
new file mode 100644
index 000000000..fb9ce5162
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/files/test0.sql
@@ -0,0 +1,4 @@
+SELECT version();
+
+SELECT story FROM test_table
+ WHERE id = %s OR story = 'Данные';
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/files/test1.sql b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/files/test1.sql
new file mode 100644
index 000000000..028c192d7
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/files/test1.sql
@@ -0,0 +1,10 @@
+CREATE FUNCTION add(integer, integer) RETURNS integer
+ AS 'select $1 + $2;'
+ LANGUAGE SQL
+ IMMUTABLE
+ RETURNS NULL ON NULL INPUT;
+
+SELECT story FROM test_table
+ WHERE id = %s OR story = 'Данные';
+
+SELECT version();
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/files/test10.sql b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/files/test10.sql
new file mode 100644
index 000000000..cbfdc457a
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/files/test10.sql
@@ -0,0 +1 @@
+SELECT EXTRACT(epoch from make_interval(secs => 3)) AS extract
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/files/test11.sql b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/files/test11.sql
new file mode 100644
index 000000000..6c1596f96
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/files/test11.sql
@@ -0,0 +1 @@
+SELECT make_interval(secs => 3)
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/files/test12.sql b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/files/test12.sql
new file mode 100644
index 000000000..33bd94eba
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/files/test12.sql
@@ -0,0 +1 @@
+INSERT INTO test2 (id) VALUES (1)
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/files/test2.sql b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/files/test2.sql
new file mode 100644
index 000000000..cba55b34a
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/files/test2.sql
@@ -0,0 +1 @@
+ANALYZE test_table
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/files/test3.sql b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/files/test3.sql
new file mode 100644
index 000000000..a2ac43509
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/files/test3.sql
@@ -0,0 +1,4 @@
+SELECT version();
+
+SELECT story FROM test_table
+ WHERE id = %(item)s OR story = 'Данные';
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/files/test4.sql b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/files/test4.sql
new file mode 100644
index 000000000..5ea74a292
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/files/test4.sql
@@ -0,0 +1 @@
+INSERT INTO test_array_table (arr_col) VALUES (%s)
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/files/test5.sql b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/files/test5.sql
new file mode 100644
index 000000000..e631dfb5a
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/files/test5.sql
@@ -0,0 +1 @@
+SELECT * FROM test_array_table WHERE arr_col = %s
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/files/test6.sql b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/files/test6.sql
new file mode 100644
index 000000000..48d210d4e
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/files/test6.sql
@@ -0,0 +1 @@
+SELECT * FROM test_array_table WHERE arr_col = %(arr_val)s;
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/files/test7.sql b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/files/test7.sql
new file mode 100644
index 000000000..96dfaebdc
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/files/test7.sql
@@ -0,0 +1 @@
+INSERT INTO test1 (id) VALUES (1)
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/files/test8.sql b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/files/test8.sql
new file mode 100644
index 000000000..8ca7c86dc
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/files/test8.sql
@@ -0,0 +1 @@
+SELECT id FROM test1
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/files/test9.sql b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/files/test9.sql
new file mode 100644
index 000000000..e52a59a3d
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/files/test9.sql
@@ -0,0 +1 @@
+SELECT * FROM blabla
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/meta/main.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/meta/main.yml
new file mode 100644
index 000000000..4ce5a5837
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/meta/main.yml
@@ -0,0 +1,2 @@
+dependencies:
+ - setup_postgresql_db
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/tasks/main.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/tasks/main.yml
new file mode 100644
index 000000000..ae5922853
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/tasks/main.yml
@@ -0,0 +1,7 @@
+####################################################################
+# WARNING: These are designed specifically for Ansible tests #
+# and should not be used as examples of how to write Ansible roles #
+####################################################################
+
+# Initial CI tests of postgresql_query module
+- import_tasks: postgresql_script_initial.yml
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/tasks/postgresql_script_initial.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/tasks/postgresql_script_initial.yml
new file mode 100644
index 000000000..729f1670a
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_script/tasks/postgresql_script_initial.yml
@@ -0,0 +1,311 @@
+- vars:
+ task_parameters: &task_parameters
+ become_user: '{{ pg_user }}'
+ become: true
+ register: result
+ pg_parameters: &pg_parameters
+ login_user: '{{ pg_user }}'
+ login_db: '{{ db_default }}'
+ block:
+ - name: Drop test table if exists
+ <<: *task_parameters
+ shell: psql postgres -U "{{ pg_user }}" -t -c "DROP TABLE IF EXISTS test_table;"
+ ignore_errors: true
+
+ - name: Create test table called test_table
+ <<: *task_parameters
+ shell: psql postgres -U "{{ pg_user }}" -t -c "CREATE TABLE test_table (id int, story text);"
+ ignore_errors: true
+
+ - name: Insert some data into test_table
+ <<: *task_parameters
+ shell: psql postgres -U "{{ pg_user }}" -t -c "INSERT INTO test_table (id, story) VALUES (1, 'first'), (2, 'second'), (3, 'third');"
+ ignore_errors: true
+
+ - name: Copy script files
+ become: true
+ copy:
+ src: '{{ item }}'
+ dest: '~{{ pg_user }}/{{ item }}'
+ owner: '{{ pg_user }}'
+ force: true
+ loop:
+ - test0.sql
+ - test1.sql
+ - test2.sql
+ - test3.sql
+ - test4.sql
+ - test5.sql
+ - test6.sql
+ - test7.sql
+ - test8.sql
+ - test9.sql
+ - test10.sql
+ - test11.sql
+ - test12.sql
+
+ - name: Analyze test_table
+ <<: *task_parameters
+ postgresql_script:
+ <<: *pg_parameters
+ path: '~{{ pg_user }}/test2.sql'
+
+ - assert:
+ that:
+ - result is changed
+ - result.query == 'ANALYZE test_table\n'
+ - result.rowcount != 0
+ - result.statusmessage == 'ANALYZE'
+ - result.query_result == {}
+
+ - name: Run queries from SQL script using positional_args
+ <<: *task_parameters
+ postgresql_script:
+ <<: *pg_parameters
+ path: ~{{ pg_user }}/test0.sql
+ positional_args:
+ - 1
+ encoding: UTF-8
+
+ - assert:
+ that:
+ - result is changed
+ - result.query == "SELECT version();\n\nSELECT story FROM test_table\n WHERE id = 1 OR story = 'Данные';\n"
+ - result.query_result[0].story == 'first'
+ - result.rowcount == 1
+ - result.statusmessage == 'SELECT 1' or result.statusmessage == 'SELECT'
+
+ - name: Run queries from SQL script using named_args
+ <<: *task_parameters
+ postgresql_script:
+ <<: *pg_parameters
+ path: ~{{ pg_user }}/test3.sql
+ named_args:
+ item: 1
+ encoding: UTF-8
+
+ - assert:
+ that:
+ - result is changed
+ - result.query == "SELECT version();\n\nSELECT story FROM test_table\n WHERE id = 1 OR story = 'Данные';\n"
+ - result.query_result[0].story == 'first'
+ - result.rowcount == 1
+ - result.statusmessage == 'SELECT 1' or result.statusmessage == 'SELECT'
+
+ - name: Create test table for issue 59955
+ <<: *task_parameters
+ postgresql_table:
+ <<: *pg_parameters
+ name: test_array_table
+ columns:
+ - arr_col int[]
+ when: postgres_version_resp.stdout is version('9.4', '>=')
+
+ - set_fact:
+ my_list:
+ - 1
+ - 2
+ - 3
+ my_arr: '{1, 2, 3}'
+ when: postgres_version_resp.stdout is version('9.4', '>=')
+
+ - name: Insert array into test table by positional args
+ <<: *task_parameters
+ postgresql_script:
+ <<: *pg_parameters
+ path: ~{{ pg_user }}/test4.sql
+ positional_args:
+ - '{{ my_list }}'
+ when: postgres_version_resp.stdout is version('9.4', '>=')
+
+ - assert:
+ that:
+ - result is changed
+ - result.query == "INSERT INTO test_array_table (arr_col) VALUES ('{1, 2, 3}')\n"
+ when: postgres_version_resp.stdout is version('9.4', '>=')
+
+ - name: Select array from test table by passing positional_args
+ <<: *task_parameters
+ postgresql_script:
+ <<: *pg_parameters
+ path: ~{{ pg_user }}/test5.sql
+ positional_args:
+ - '{{ my_list }}'
+ when: postgres_version_resp.stdout is version('9.4', '>=')
+
+ - assert:
+ that:
+ - result is changed
+ - result.query == "SELECT * FROM test_array_table WHERE arr_col = '{1, 2, 3}'\n"
+ - result.rowcount == 1
+ when: postgres_version_resp.stdout is version('9.4', '>=')
+
+ - name: Select array from test table by passing named_args
+ <<: *task_parameters
+ postgresql_script:
+ <<: *pg_parameters
+ path: ~{{ pg_user }}/test6.sql
+ named_args:
+ arr_val:
+ - '{{ my_list }}'
+ when: postgres_version_resp.stdout is version('9.4', '>=')
+
+ - assert:
+ that:
+ - result is changed
+ - result.query == "SELECT * FROM test_array_table WHERE arr_col = '{1, 2, 3}';\n"
+ - result.rowcount == 1
+ when: postgres_version_resp.stdout is version('9.4', '>=')
+
+ - name: Select array from test table by passing positional_args as a string
+ <<: *task_parameters
+ postgresql_script:
+ <<: *pg_parameters
+ path: ~{{ pg_user }}/test5.sql
+ positional_args:
+ - '{{ my_arr|string }}'
+ trust_input: true
+ when: postgres_version_resp.stdout is version('9.4', '>=')
+
+ - assert:
+ that:
+ - result is changed
+ - result.query == "SELECT * FROM test_array_table WHERE arr_col = '{1, 2, 3}'\n"
+ - result.rowcount == 1
+ when: postgres_version_resp.stdout is version('9.4', '>=')
+
+ - name: Test trust_input parameter
+ <<: *task_parameters
+ postgresql_script:
+ <<: *pg_parameters
+ session_role: 'curious.anonymous"; SELECT * FROM information_schema.tables; --'
+ path: ~{{ pg_user }}/test5.sql
+ trust_input: false
+ ignore_errors: true
+
+ - assert:
+ that:
+ - result is failed
+ - result.msg is search('is potentially dangerous')
+
+ - name: Clean up
+ <<: *task_parameters
+ postgresql_table:
+ <<: *pg_parameters
+ name: test_array_table
+ state: absent
+ when: postgres_version_resp.stdout is version('9.4', '>=')
+
+ #############################
+ # Check search_path parameter
+
+ - name: Create test schemas
+ <<: *task_parameters
+ postgresql_schema:
+ <<: *pg_parameters
+ name: '{{ item }}'
+ loop:
+ - query_test1
+ - query_test2
+
+ - name: Create test tables
+ <<: *task_parameters
+ postgresql_table:
+ <<: *pg_parameters
+ name: '{{ item }}'
+ columns:
+ - id int
+ loop:
+ - 'query_test1.test1'
+ - 'query_test2.test2'
+
+ - name: Insert data
+ <<: *task_parameters
+ postgresql_script:
+ <<: *pg_parameters
+ path: ~{{ pg_user }}/{{ item }}.sql
+ search_path:
+ - query_test1
+ - query_test2
+ loop:
+ - test7
+ - test12
+
+ - name: Get data
+ <<: *task_parameters
+ postgresql_script:
+ <<: *pg_parameters
+ path: ~{{ pg_user }}/test8.sql
+ search_path:
+ - query_test1
+ - query_test2
+
+ - assert:
+ that:
+ - result.rowcount == 1
+
+ - name: Get data, must fail as we don't specify search_path
+ <<: *task_parameters
+ postgresql_script:
+ <<: *pg_parameters
+ path: ~{{ pg_user }}/test8.sql
+ ignore_errors: true
+
+ - assert:
+ that:
+ - result is failed
+
+ #############################################################################
+ # Issue https://github.com/ansible-collections/community.postgresql/issues/45
+ - name: Create table containing a decimal value
+ <<: *task_parameters
+ postgresql_table:
+ <<: *pg_parameters
+ name: blabla
+ columns:
+ - id int
+ - num decimal
+
+ - name: Insert data
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: INSERT INTO blabla (id, num) VALUES (1, 1::decimal)
+
+ - name: Get data
+ <<: *task_parameters
+ postgresql_script:
+ <<: *pg_parameters
+ path: ~{{ pg_user }}/test9.sql
+
+ - assert:
+ that:
+ - result.rowcount == 1
+
+ #############################################################################
+ # Issue https://github.com/ansible-collections/community.postgresql/issues/47
+ - name: Get datetime.timedelta value
+ <<: *task_parameters
+ postgresql_script:
+ <<: *pg_parameters
+ path: ~{{ pg_user }}/test10.sql
+ when: postgres_version_resp.stdout is version('10', '>=')
+
+ - assert:
+ that:
+ - result.rowcount == 1
+ - result.query_result[0]["extract"] == 3 or result.query_result[0]["extract"] == 3.0
+ when: postgres_version_resp.stdout is version('10', '>=')
+
+ - name: Get interval value
+ <<: *task_parameters
+ postgresql_script:
+ <<: *pg_parameters
+ path: ~{{ pg_user }}/test11.sql
+ when: postgres_version_resp.stdout is version('10', '>=')
+
+ - assert:
+ that:
+ - result.rowcount == 1
+ - result.query_result[0]["make_interval"] == "0:00:03"
+ when: postgres_version_resp.stdout is version('10', '>=')
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_sequence/aliases b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_sequence/aliases
new file mode 100644
index 000000000..a4c92ef85
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_sequence/aliases
@@ -0,0 +1,2 @@
+destructive
+shippable/posix/group1
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_sequence/defaults/main.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_sequence/defaults/main.yml
new file mode 100644
index 000000000..049b5531f
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_sequence/defaults/main.yml
@@ -0,0 +1,5 @@
+---
+db_name: 'ansible_db'
+db_user1: 'ansible_db_user1'
+db_user2: 'ansible_db_user2'
+db_default: 'postgres'
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_sequence/meta/main.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_sequence/meta/main.yml
new file mode 100644
index 000000000..4ce5a5837
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_sequence/meta/main.yml
@@ -0,0 +1,2 @@
+dependencies:
+ - setup_postgresql_db
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_sequence/tasks/main.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_sequence/tasks/main.yml
new file mode 100644
index 000000000..b53069005
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_sequence/tasks/main.yml
@@ -0,0 +1,8 @@
+####################################################################
+# WARNING: These are designed specifically for Ansible tests #
+# and should not be used as examples of how to write Ansible roles #
+####################################################################
+
+# Initial CI tests of postgresql_sequence module
+- import_tasks: postgresql_sequence_initial.yml
+ when: postgres_version_resp.stdout is version('9.4', '>=')
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_sequence/tasks/postgresql_sequence_initial.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_sequence/tasks/postgresql_sequence_initial.yml
new file mode 100644
index 000000000..c498033bd
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_sequence/tasks/postgresql_sequence_initial.yml
@@ -0,0 +1,730 @@
+---
+# Copyright: (c) 2019, Tobias Birkefeld (@tcraxs) <t@craxs.de>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+# Preparation for tests.
+- name: postgresql_sequence - create a user to be owner of a database
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_user:
+ name: "{{ db_user1 }}"
+ state: present
+ encrypted: true
+ password: password
+ role_attr_flags: LOGIN
+ db: "{{ db_default }}"
+ login_user: "{{ pg_user }}"
+
+- name: postgresql_sequence - create DB
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_db:
+ state: present
+ name: "{{ db_name }}"
+ owner: "{{ db_user1 }}"
+ login_user: "{{ pg_user }}"
+
+- name: Create a user to be owner of a sequence
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_user:
+ name: "{{ db_user2 }}"
+ state: present
+ encrypted: true
+ password: password
+ role_attr_flags: LOGIN
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+
+- name: postgresql_sequence - create a schema
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_schema:
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ name: foobar_schema
+
+####################
+# Test: create sequence in checkmode
+- name: postgresql_sequence - create a new sequence with name "foobar" in check_mode
+ become_user: "{{ pg_user }}"
+ become: true
+ check_mode: true
+ postgresql_sequence:
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ name: foobar
+ register: result
+
+# Checks
+- name: postgresql_sequence - check with assert the output
+ assert:
+ that:
+ - result is changed
+ - result.sequence == 'foobar'
+ - result.queries == ["CREATE SEQUENCE \"public\".\"foobar\""]
+
+# Real SQL check
+- name: postgresql_sequence - check that the new sequence "foobar" not exists
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_query:
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ query: "SELECT sequence_name FROM information_schema.sequences WHERE sequence_name = 'foobar'"
+ register: result
+
+- name: postgresql_sequence - check with assert the output
+ assert:
+ that:
+ - result.rowcount == 0
+ - result.statusmessage == 'SELECT 0'
+
+####################
+# Test: create sequence
+- name: postgresql_sequence - create a new sequence with name "foobar"
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_sequence:
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ name: foobar
+ register: result
+
+# Checks
+- name: postgresql_sequence - check with assert the output
+ assert:
+ that:
+ - result is changed
+ - result.sequence == 'foobar'
+ - result.queries == ["CREATE SEQUENCE \"public\".\"foobar\""]
+
+# Real SQL check
+- name: postgresql_sequence - check that the new sequence "foobar" exists
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ query: "SELECT sequence_name FROM information_schema.sequences WHERE sequence_name = 'foobar'"
+ register: result
+
+- name: postgresql_sequence - check with assert the output
+ assert:
+ that:
+ - result.rowcount == 1
+
+####################
+# Test: drop sequence in checkmode
+- name: postgresql_sequence - drop a sequence called foobar
+ become_user: "{{ pg_user }}"
+ become: true
+ check_mode: true
+ postgresql_sequence:
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ name: foobar
+ state: absent
+ register: result
+
+# Checks
+- name: postgresql_sequence - check with assert the output
+ assert:
+ that:
+ - result is changed
+ - result.sequence == 'foobar'
+ - result.queries == ["DROP SEQUENCE \"public\".\"foobar\""]
+
+# Real SQL check
+- name: postgresql_sequence - check that the sequence "foobar" still exists
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ query: "SELECT sequence_name FROM information_schema.sequences WHERE sequence_name = 'foobar'"
+ register: result
+
+- name: postgresql_sequence - check with assert the output
+ assert:
+ that:
+ - result.rowcount == 1
+
+####################
+# Test: drop sequence
+- name: postgresql_sequence - drop a sequence called foobar
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_sequence:
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ name: foobar
+ state: absent
+ register: result
+
+# Checks
+- name: postgresql_sequence - check with assert the output
+ assert:
+ that:
+ - result is changed
+ - result.sequence == 'foobar'
+ - result.queries == ["DROP SEQUENCE \"public\".\"foobar\""]
+
+# Real SQL check
+- name: postgresql_sequence - check that the sequence "foobar" not exists
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ query: "SELECT sequence_name FROM information_schema.sequences WHERE sequence_name = 'foobar'"
+ register: result
+
+- name: postgresql_sequence - check with assert the output
+ assert:
+ that:
+ - result.rowcount == 0
+
+####################
+# Test: drop nonexistent sequence
+- name: postgresql_sequence - drop a sequence called foobar which does not exists
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_sequence:
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ name: foobar
+ state: absent
+ register: result
+
+# Checks
+- name: postgresql_sequence - check with assert the output
+ assert:
+ that:
+ - result is not changed
+ - result.sequence == 'foobar'
+ - result.queries == []
+
+# Real SQL check
+- name: postgresql_sequence - check that the sequence "foobar" not exists
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ query: "SELECT sequence_name FROM information_schema.sequences WHERE sequence_name = 'foobar'"
+ register: result
+
+- name: postgresql_sequence - check with assert the output
+ assert:
+ that:
+ - result.rowcount == 0
+
+####################
+# Test: create sequence with options
+- name: postgresql_sequence - create an descending sequence called foobar_desc, starting at 101 and which cycle between 1 to 1000
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_sequence:
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ name: foobar_desc
+ increment: -1
+ start: 101
+ minvalue: 1
+ maxvalue: 1000
+ cycle: true
+ register: result
+
+# Checks
+- name: postgresql_sequence - check with assert the output
+ assert:
+ that:
+ - result is changed
+ - result.sequence == 'foobar_desc'
+ - result.increment == '-1'
+ - result.minvalue == '1'
+ - result.maxvalue == '1000'
+ - result.cycle == 'YES'
+ - result.queries == ["CREATE SEQUENCE \"public\".\"foobar_desc\" INCREMENT BY -1 MINVALUE 1 MAXVALUE 1000 START WITH 101 CYCLE"]
+
+# Real SQL check
+- name: postgresql_sequence - check that the new sequence "foobar_desc" exists
+ postgresql_query:
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ query: "SELECT sequence_name FROM information_schema.sequences WHERE sequence_name = 'foobar_desc'"
+ register: result
+
+- name: postgresql_sequence - check with assert the output
+ assert:
+ that:
+ - result.rowcount == 1
+
+####################
+# Test: rename a sequence in checkmode
+- name: postgresql_sequence - rename an existing sequence named foobar_desc to foobar_with_options
+ become_user: "{{ pg_user }}"
+ become: true
+ check_mode: true
+ postgresql_sequence:
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ name: foobar_desc
+ rename_to: foobar_with_options
+ register: result
+
+# Checks
+- name: postgresql_sequence - check with assert the output
+ assert:
+ that:
+ - result is changed
+ - result.sequence == 'foobar_desc'
+ - result.newname == 'foobar_with_options'
+ - result.queries == ["ALTER SEQUENCE \"public\".\"foobar_desc\" RENAME TO \"foobar_with_options\""]
+
+# Real SQL check
+- name: postgresql_sequence - check that the sequence "foobar_desc" still exists and is not renamed
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ query: "SELECT sequence_name FROM information_schema.sequences WHERE sequence_name = 'foobar_desc'"
+ register: result
+
+- name: postgresql_sequence - check with assert the output
+ assert:
+ that:
+ - result.rowcount == 1
+
+####################
+# Test: rename a sequence
+- name: postgresql_sequence - rename an existing sequence named foobar_desc to foobar_with_options
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_sequence:
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ name: foobar_desc
+ rename_to: foobar_with_options
+ register: result
+
+# Checks
+- name: postgresql_sequence - check with assert the output
+ assert:
+ that:
+ - result is changed
+ - result.sequence == 'foobar_desc'
+ - result.newname == 'foobar_with_options'
+ - result.queries == ["ALTER SEQUENCE \"public\".\"foobar_desc\" RENAME TO \"foobar_with_options\""]
+
+# Real SQL check
+- name: postgresql_sequence - check that the renamed sequence "foobar_with_options" exists
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ query: "SELECT sequence_name FROM information_schema.sequences WHERE sequence_name = 'foobar_with_options'"
+ register: result
+
+- name: postgresql_sequence - check with assert the output
+ assert:
+ that:
+ - result.rowcount == 1
+
+####################
+# Test: change schema of a sequence in checkmode
+- name: postgresql_sequence - change schema of an existing sequence from public to foobar_schema
+ become_user: "{{ pg_user }}"
+ become: true
+ check_mode: true
+ postgresql_sequence:
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ name: foobar_with_options
+ newschema: foobar_schema
+ register: result
+
+# Checks
+- name: postgresql_sequence - check with assert the output
+ assert:
+ that:
+ - result is changed
+ - result.sequence == 'foobar_with_options'
+ - result.schema == 'public'
+ - result.newschema == 'foobar_schema'
+ - result.queries == ["ALTER SEQUENCE \"public\".\"foobar_with_options\" SET SCHEMA \"foobar_schema\""]
+
+# Real SQL check
+- name: postgresql_sequence - check that the sequence "foobar_with_options" still exists in the old schema
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ query: "SELECT sequence_name,sequence_schema FROM information_schema.sequences WHERE sequence_name = 'foobar_with_options' AND sequence_schema = 'public'"
+ register: result
+
+- name: postgresql_sequence - check with assert the output
+ assert:
+ that:
+ - result.rowcount == 1
+
+####################
+# Test: change schema of a sequence
+- name: postgresql_sequence - change schema of an existing sequence from public to foobar_schema
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_sequence:
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ name: foobar_with_options
+ newschema: foobar_schema
+ register: result
+
+# Checks
+- name: postgresql_sequence - check with assert the output
+ assert:
+ that:
+ - result is changed
+ - result.sequence == 'foobar_with_options'
+ - result.schema == 'public'
+ - result.newschema == 'foobar_schema'
+ - result.queries == ["ALTER SEQUENCE \"public\".\"foobar_with_options\" SET SCHEMA \"foobar_schema\""]
+
+# Real SQL check
+- name: postgresql_sequence - check that the sequence "foobar_with_options" exists in new schema
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ query: "SELECT sequence_name,sequence_schema FROM information_schema.sequences WHERE sequence_name = 'foobar_with_options' AND sequence_schema = 'foobar_schema'"
+ register: result
+
+- name: postgresql_sequence - check with assert the output
+ assert:
+ that:
+ - result.rowcount == 1
+
+####################
+# Test: change owner of a sequence in checkmode
+- name: postgresql_sequence - change owner of an existing sequence from "{{ pg_user }}" to "{{ db_user1 }}"
+ become_user: "{{ pg_user }}"
+ become: true
+ check_mode: true
+ postgresql_sequence:
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ name: foobar_with_options
+ schema: foobar_schema
+ owner: "{{ db_user1 }}"
+ register: result
+
+# Checks
+- name: postgresql_sequence - check with assert the output
+ assert:
+ that:
+ - result is changed
+ - result.sequence == 'foobar_with_options'
+ - result.owner == "{{ pg_user }}"
+ - result.queries == ["ALTER SEQUENCE \"foobar_schema\".\"foobar_with_options\" OWNER TO \"{{ db_user1 }}\""]
+
+# Real SQL check
+- name: postgresql_sequence - check that the sequence "foobar_with_options" has still the old owner
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ query: "SELECT c.relname,a.rolname,n.nspname
+ FROM pg_class as c
+ JOIN pg_authid as a on (c.relowner = a.oid)
+ JOIN pg_namespace as n on (c.relnamespace = n.oid)
+ WHERE c.relkind = 'S' and
+ c.relname = 'foobar_with_options' and
+ n.nspname = 'foobar_schema' and
+ a.rolname = '{{ pg_user }}'"
+ register: result
+
+- name: postgresql_sequence - check with assert the output
+ assert:
+ that:
+ - result.rowcount == 1
+
+####################
+# Test: change owner of a sequence
+- name: postgresql_sequence - change owner of an existing sequence from "{{ pg_user }}" to "{{ db_user1 }}"
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_sequence:
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ name: foobar_with_options
+ schema: foobar_schema
+ owner: "{{ db_user1 }}"
+ register: result
+
+# Checks
+- name: postgresql_sequence - check with assert the output
+ assert:
+ that:
+ - result is changed
+ - result.sequence == 'foobar_with_options'
+ - result.owner == "{{ pg_user }}"
+ - result.queries == ["ALTER SEQUENCE \"foobar_schema\".\"foobar_with_options\" OWNER TO \"{{ db_user1 }}\""]
+
+# Real SQL check
+- name: postgresql_sequence - check that the sequence "foobar_with_options" has a new owner
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ query: "SELECT c.relname,a.rolname,n.nspname
+ FROM pg_class as c
+ JOIN pg_authid as a on (c.relowner = a.oid)
+ JOIN pg_namespace as n on (c.relnamespace = n.oid)
+ WHERE c.relkind = 'S' and
+ c.relname = 'foobar_with_options' and
+ n.nspname = 'foobar_schema' and
+ a.rolname = '{{ db_user1 }}'"
+ register: result
+
+- name: postgresql_sequence - check with assert the output
+ assert:
+ that:
+ - result.rowcount == 1
+
+####################
+# Test: drop sequence with cascade
+
+# CREATE SEQUENCE seq1;
+# CREATE TABLE t1 (f1 INT NOT NULL DEFAULT nextval('seq1'));
+# DROP SEQUENCE seq1 CASCADE;
+- name: postgresql_sequence - create sequence for drop cascade test
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_sequence:
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ name: seq1
+
+- name: postgresql_sequence - create table which use sequence for drop cascade test
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_table:
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ name: t1
+ columns:
+ - f1 INT NOT NULL DEFAULT nextval('seq1')
+
+####################
+# Test: drop sequence with cascade in checkmode
+- name: postgresql_sequence - drop with cascade a sequence called seq1
+ become_user: "{{ pg_user }}"
+ become: true
+ check_mode: true
+ postgresql_sequence:
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ name: seq1
+ state: absent
+ cascade: true
+ register: result
+
+# Checks
+- name: postgresql_sequence - check with assert the output
+ assert:
+ that:
+ - result is changed
+ - result.sequence == 'seq1'
+ - result.queries == ["DROP SEQUENCE \"public\".\"seq1\" CASCADE"]
+
+# Real SQL check
+- name: postgresql_sequence - check that the sequence "seq1" still exists
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ query: "SELECT sequence_name FROM information_schema.sequences WHERE sequence_name = 'seq1'"
+ register: result
+
+- name: postgresql_sequence - check with assert the output
+ assert:
+ that:
+ - result.rowcount == 1
+
+####################
+# Test: drop sequence with cascade
+- name: postgresql_sequence - drop with cascade a sequence called seq1
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_sequence:
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ name: seq1
+ state: absent
+ cascade: true
+ register: result
+
+# Checks
+- name: postgresql_sequence - check with assert the output
+ assert:
+ that:
+ - result is changed
+ - result.sequence == 'seq1'
+ - result.queries == ["DROP SEQUENCE \"public\".\"seq1\" CASCADE"]
+
+# Real SQL check
+- name: postgresql_sequence - check that the sequence "seq1" not exists
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ query: "SELECT sequence_name FROM information_schema.sequences WHERE sequence_name = 'seq1'"
+ register: result
+
+- name: postgresql_sequence - check with assert the output
+ assert:
+ that:
+ - result.rowcount == 0
+
+####################
+# Test: create sequence with owner in checkmode
+- name: postgresql_sequence - create a new sequence with name "foobar2" with owner "{{ db_user2 }}"
+ become_user: "{{ pg_user }}"
+ become: true
+ check_mode: true
+ postgresql_sequence:
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ name: foobar2
+ owner: "{{ db_user2 }}"
+ register: result
+
+# Checks
+- name: postgresql_sequence - check with assert the output
+ assert:
+ that:
+ - result is changed
+ - result.sequence == 'foobar2'
+ - result.queries == ["CREATE SEQUENCE \"public\".\"foobar2\"", "ALTER SEQUENCE \"public\".\"foobar2\" OWNER TO \"ansible_db_user2\""]
+
+# Real SQL check
+- name: postgresql_sequence - check that the new sequence "foobar2" does not exists
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ query: "SELECT sequence_name FROM information_schema.sequences WHERE sequence_name = 'foobar2'"
+ register: result
+
+- name: postgresql_sequence - check with assert the output
+ assert:
+ that:
+ - result.rowcount == 0
+
+####################
+# Test: create sequence with owner
+- name: postgresql_sequence - create a new sequence with name "foobar2" with owner "{{ db_user2 }}"
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_sequence:
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ name: foobar2
+ owner: "{{ db_user2 }}"
+ register: result
+
+# Checks
+- name: postgresql_sequence - check with assert the output
+ assert:
+ that:
+ - result is changed
+ - result.sequence == 'foobar2'
+ - result.queries == ["CREATE SEQUENCE \"public\".\"foobar2\"", "ALTER SEQUENCE \"public\".\"foobar2\" OWNER TO \"ansible_db_user2\""]
+
+# Real SQL check
+- name: postgresql_sequence - check that the new sequence "foobar2" exists
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ query: "SELECT sequence_name FROM information_schema.sequences WHERE sequence_name = 'foobar2'"
+ register: result
+
+- name: postgresql_sequence - check with assert the output
+ assert:
+ that:
+ - result.rowcount == 1
+
+- name: postgresql_sequence - check that the sequence "foobar2" has owner "{{ db_user2 }}"
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ query: "SELECT c.relname,a.rolname,n.nspname
+ FROM pg_class as c
+ JOIN pg_authid as a on (c.relowner = a.oid)
+ JOIN pg_namespace as n on (c.relnamespace = n.oid)
+ WHERE c.relkind = 'S' and
+ c.relname = 'foobar2' and
+ n.nspname = 'public' and
+ a.rolname = '{{ db_user2 }}'"
+ register: result
+
+- name: postgresql_sequence - check with assert the output
+ assert:
+ that:
+ - result.rowcount == 1
+
+####################
+# Test: create sequence with trust_input
+- name: postgresql_sequence - check that trust_input works as expected
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_sequence:
+ db: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+ name: 'just_a_name"; SELECT * FROM information_schema.tables; --'
+ trust_input: false
+ owner: "{{ db_user2 }}"
+ ignore_errors: true
+ register: result
+
+# Checks
+- name: postgresql_sequence - check with assert the output
+ assert:
+ that:
+ - result is failed
+ - result.msg is search('is potentially dangerous')
+
+# Cleanup
+- name: postgresql_sequence - destroy DB
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_db:
+ state: absent
+ name: "{{ db_name }}"
+ login_user: "{{ pg_user }}"
+
+- name: remove test roles
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_user:
+ state: absent
+ login_db: "{{ db_default }}"
+ login_user: "{{ pg_user }}"
+ name: "{{ item }}"
+ loop:
+ - "{{ db_user1 }}"
+ - "{{ db_user2 }}"
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_set/aliases b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_set/aliases
new file mode 100644
index 000000000..a4c92ef85
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_set/aliases
@@ -0,0 +1,2 @@
+destructive
+shippable/posix/group1
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_set/meta/main.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_set/meta/main.yml
new file mode 100644
index 000000000..4ce5a5837
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_set/meta/main.yml
@@ -0,0 +1,2 @@
+dependencies:
+ - setup_postgresql_db
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_set/tasks/main.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_set/tasks/main.yml
new file mode 100644
index 000000000..3f16eb0d6
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_set/tasks/main.yml
@@ -0,0 +1,11 @@
+####################################################################
+# WARNING: These are designed specifically for Ansible tests #
+# and should not be used as examples of how to write Ansible roles #
+####################################################################
+
+# Initial CI tests of postgresql_initial module
+- include_tasks: postgresql_set_initial.yml
+ when: postgres_version_resp.stdout is version('9.6', '>=')
+
+- include_tasks: options_coverage.yml
+ when: postgres_version_resp.stdout is version('9.6', '>=')
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_set/tasks/options_coverage.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_set/tasks/options_coverage.yml
new file mode 100644
index 000000000..c4598d2a9
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_set/tasks/options_coverage.yml
@@ -0,0 +1,71 @@
+# Test code for the postgresql_set module
+# Copyright: (c) 2021, Andrew Klychkov (@Andersson007) <aaklychkov@mail.ru>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+- vars:
+ task_parameters: &task_parameters
+ become_user: '{{ pg_user }}'
+ become: true
+ pg_parameters: &pg_parameters
+ login_user: '{{ pg_user }}'
+ login_db: postgres
+
+ block:
+ - name: Define a test setting map
+ set_fact:
+ setting_map:
+ allow_system_table_mods: on
+ archive_command: /bin/true
+ archive_timeout: 10min
+ autovacuum_work_mem: '-1'
+ backend_flush_after: 0
+ autovacuum_vacuum_scale_factor: 0.5
+ client_encoding: UTF-8
+ bgwriter_delay: 400
+ maintenance_work_mem: 32mb
+ effective_cache_size: 1024kB
+ shared_buffers: 1GB
+ wal_level: replica
+ log_statement: mod
+ track_functions: none
+ shared_preload_libraries: 'pg_stat_statements, pgaudit'
+ log_line_prefix: 'db=%d,user=%u,app=%a,client=%h '
+
+ # Check mode:
+ - name: Set settings in check mode
+ <<: *task_parameters
+ postgresql_set:
+ <<: *pg_parameters
+ name: '{{ item.key }}'
+ value: '{{ item.value }}'
+ check_mode: true
+ with_dict: '{{ setting_map }}'
+
+ # Actual mode:
+ - name: Set settings in actual mode
+ <<: *task_parameters
+ postgresql_set:
+ <<: *pg_parameters
+ name: '{{ item.key }}'
+ value: '{{ item.value }}'
+ with_dict: '{{ setting_map }}'
+
+ # https://github.com/ansible-collections/community.postgresql/issues/78
+ - name: Test param with comma containing values
+ <<: *task_parameters
+ shell: "grep shared_preload_libraries {{ pg_auto_conf }}"
+ register: result
+
+ - assert:
+ that:
+ - result.stdout == "shared_preload_libraries = 'pg_stat_statements, pgaudit'"
+
+ # Test for single-value params with commas and spaces in value
+ - name: Test single-value param with commas and spaces in value
+ <<: *task_parameters
+ shell: "grep log_line_prefix {{ pg_auto_conf }}"
+ register: result
+
+ - assert:
+ that:
+ - result.stdout == "log_line_prefix = 'db=%d,user=%u,app=%a,client=%h '"
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_set/tasks/postgresql_set_initial.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_set/tasks/postgresql_set_initial.yml
new file mode 100644
index 000000000..ddff916aa
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_set/tasks/postgresql_set_initial.yml
@@ -0,0 +1,442 @@
+# Test code for the postgresql_set module
+# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) <aaklychkov@mail.ru>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+#
+# Notice: assertions are different for Ubuntu 16.04 and FreeBSD because they don't work
+# correctly for these tests. There are some stranges exactly in Shippable CI.
+# However I checked it manually for all points (including Ubuntu 16.05 and FreeBSD)
+# and it worked as expected.
+
+- vars:
+ task_parameters: &task_parameters
+ become_user: '{{ pg_user }}'
+ become: true
+ pg_parameters: &pg_parameters
+ login_user: '{{ pg_user }}'
+ login_db: postgres
+
+ block:
+ - name: postgresql_set - preparation to the next step
+ <<: *task_parameters
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_set:
+ <<: *pg_parameters
+ name: work_mem
+ reset: true
+
+ #####################
+ # Testing check_mode:
+ - name: postgresql_set - get work_mem initial value
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: SHOW work_mem
+ register: before
+
+ - name: postgresql_set - set work_mem (restart is not required), check_mode
+ <<: *task_parameters
+ postgresql_set:
+ <<: *pg_parameters
+ name: work_mem
+ value: 12MB
+ register: set_wm
+ check_mode: true
+
+ - assert:
+ that:
+ - set_wm.name == 'work_mem'
+ - set_wm.changed == true
+ - set_wm.prev_val_pretty == before.query_result[0].work_mem
+ - set_wm.value_pretty == '12MB'
+ - set_wm.restart_required == false
+
+ - name: postgresql_set - get work_mem value to check, must be the same as initial
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: SHOW work_mem
+ register: after
+
+ - assert:
+ that:
+ - before.query_result[0].work_mem == after.query_result[0].work_mem
+ ######
+ #
+
+ - name: postgresql_set - set work_mem (restart is not required)
+ <<: *task_parameters
+ postgresql_set:
+ <<: *pg_parameters
+ name: work_mem
+ value: 12mb
+ register: set_wm
+
+ - assert:
+ that:
+ - set_wm.name == 'work_mem'
+ - set_wm.changed == true
+ - set_wm.value_pretty == '12MB'
+ - set_wm.value_pretty != set_wm.prev_val_pretty
+ - set_wm.restart_required == false
+ - set_wm.value.value == 12582912
+ - set_wm.value.unit == 'b'
+ when:
+ - ansible_distribution != "Ubuntu"
+ - ansible_distribution_major_version != '16'
+ - ansible_distribution != "FreeBSD"
+
+ - assert:
+ that:
+ - set_wm.name == 'work_mem'
+ - set_wm.changed == true
+ - set_wm.restart_required == false
+ when:
+ - ansible_distribution == "Ubuntu"
+ - ansible_distribution_major_version == '16'
+
+ - name: postgresql_set - reset work_mem (restart is not required)
+ <<: *task_parameters
+ postgresql_set:
+ <<: *pg_parameters
+ name: work_mem
+ reset: true
+ register: reset_wm
+
+ - assert:
+ that:
+ - reset_wm.name == 'work_mem'
+ - reset_wm.changed == true
+ - reset_wm.value_pretty != reset_wm.prev_val_pretty
+ - reset_wm.restart_required == false
+ - reset_wm.value.value != '12582912'
+ when:
+ - ansible_distribution != "Ubuntu"
+ - ansible_distribution_major_version != '16'
+ - ansible_distribution != "FreeBSD"
+
+ - assert:
+ that:
+ - reset_wm.name == 'work_mem'
+ - reset_wm.changed == true
+ - reset_wm.restart_required == false
+ when:
+ - ansible_distribution == "Ubuntu"
+ - ansible_distribution_major_version == '16'
+
+ - name: postgresql_set - reset work_mem again to check that nothing changed (restart is not required)
+ <<: *task_parameters
+ postgresql_set:
+ <<: *pg_parameters
+ name: work_mem
+ reset: true
+ register: reset_wm2
+
+ - assert:
+ that:
+ - reset_wm2.name == 'work_mem'
+ - reset_wm2.changed == false
+ - reset_wm2.value_pretty == reset_wm2.prev_val_pretty
+ - reset_wm2.restart_required == false
+ when:
+ - ansible_distribution != "Ubuntu"
+ - ansible_distribution_major_version != '16'
+
+ - assert:
+ that:
+ - reset_wm2.name == 'work_mem'
+ - reset_wm2.changed == false
+ - reset_wm2.restart_required == false
+ when:
+ - ansible_distribution == "Ubuntu"
+ - ansible_distribution_major_version == '16'
+
+ - name: postgresql_set - preparation to the next step
+ <<: *task_parameters
+ postgresql_set:
+ <<: *pg_parameters
+ name: work_mem
+ value: 14MB
+
+ - name: postgresql_set - set work_mem to initial state (restart is not required)
+ <<: *task_parameters
+ postgresql_set:
+ <<: *pg_parameters
+ name: work_mem
+ value: default
+ register: def_wm
+
+ - assert:
+ that:
+ - def_wm.name == 'work_mem'
+ - def_wm.changed == true
+ - def_wm.value_pretty != def_wm.prev_val_pretty
+ - def_wm.restart_required == false
+ - def_wm.value.value != '14680064'
+ when:
+ - ansible_distribution != "Ubuntu"
+ - ansible_distribution_major_version != '16'
+ - ansible_distribution != 'FreeBSD'
+
+ - assert:
+ that:
+ - def_wm.name == 'work_mem'
+ - def_wm.changed == true
+ - def_wm.restart_required == false
+ when:
+ - ansible_distribution == "Ubuntu"
+ - ansible_distribution_major_version == '16'
+ - ansible_distribution != 'FreeBSD'
+
+ - name: postgresql_set - set shared_buffers (restart is required)
+ <<: *task_parameters
+ postgresql_set:
+ <<: *pg_parameters
+ name: shared_buffers
+ value: 111MB
+ register: set_shb
+
+ - assert:
+ that:
+ - set_shb.name == 'shared_buffers'
+ - set_shb.changed == true
+ - set_shb.restart_required == true
+
+ # We don't check value.unit because it is none
+ - name: postgresql_set - set autovacuum (enabled by default, restart is not required)
+ <<: *task_parameters
+ postgresql_set:
+ <<: *pg_parameters
+ name: autovacuum
+ value: off
+ register: set_aut
+
+ - assert:
+ that:
+ - set_aut.name == 'autovacuum'
+ - set_aut.changed == true
+ - set_aut.restart_required == false
+ - set_aut.value.value == 'off'
+
+ # Test check_mode, step 1. At the previous test we set autovacuum = 'off'
+ - name: postgresql - try to change autovacuum again in check_mode
+ <<: *task_parameters
+ postgresql_set:
+ <<: *pg_parameters
+ name: autovacuum
+ value: on
+ register: set_aut
+ check_mode: true
+
+ - assert:
+ that:
+ - set_aut.name == 'autovacuum'
+ - set_aut.changed == true
+ - set_aut.restart_required == false
+ - set_aut.value.value == 'off'
+
+ # Test check_mode, step 2
+ - name: postgresql - check that autovacuum wasn't actually changed after change in check_mode
+ <<: *task_parameters
+ postgresql_set:
+ <<: *pg_parameters
+ name: autovacuum
+ value: off
+ register: set_aut
+ check_mode: true
+
+ - assert:
+ that:
+ - set_aut.name == 'autovacuum'
+ - set_aut.changed == false
+ - set_aut.restart_required == false
+ - set_aut.value.value == 'off'
+
+ # Additional check by SQL query:
+ - name: postgresql_set - get autovacuum value to check, must be off
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: SHOW autovacuum
+ register: result
+
+ - assert:
+ that:
+ - result.query_result[0].autovacuum == 'off'
+
+ # Test check_mode, step 3. It is different from
+ # the prev test - it runs without check_mode: true.
+ # Before the check_mode tests autovacuum was off
+ - name: postgresql - check that autovacuum wasn't actually changed after change in check_mode
+ <<: *task_parameters
+ postgresql_set:
+ <<: *pg_parameters
+ name: autovacuum
+ value: off
+ register: set_aut
+
+ - assert:
+ that:
+ - set_aut.name == 'autovacuum'
+ - set_aut.changed == false
+ - set_aut.restart_required == false
+ - set_aut.value.value == 'off'
+
+ #################
+ # Bugfix of 67377
+ - name: archive command with mb
+ <<: *task_parameters
+ postgresql_set:
+ <<: *pg_parameters
+ trust_input: true
+ name: archive_command
+ value: 'test ! -f /mnt/postgres/mb/%f && cp %p /mnt/postgres/mb/%f'
+
+ # Check:
+ - name: check value
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: select reset_val from pg_settings where name = 'archive_command'
+ register: result
+
+ - assert:
+ that:
+ - result.query_result.0.reset_val == "test ! -f /mnt/postgres/mb/%f && cp %p /mnt/postgres/mb/%f"
+
+ #############################
+ # Check trust_input parameter
+ - name: postgresql_set - check trust_input
+ <<: *task_parameters
+ postgresql_set:
+ <<: *pg_parameters
+ name: shared_buffers
+ value: 111MB
+ trust_input: false
+ session_role: 'curious.anonymous"; SELECT * FROM information_schema.tables; --'
+ register: result
+ ignore_errors: true
+
+ - assert:
+ that:
+ - result is failed
+ - result.msg is search('is potentially dangerous')
+
+ ###############################################################################
+ # Bugfix of https://github.com/ansible-collections/community.general/issues/775
+ - name: postgresql_set - turn on archive mode
+ <<: *task_parameters
+ postgresql_set:
+ <<: *pg_parameters
+ name: archive_mode
+ value: 'on'
+
+ - name: Restart PostgreSQL
+ become: true
+ service:
+ name: "{{ postgresql_service }}"
+ state: restarted
+
+ - name: postgresql_set - set empty string as value
+ <<: *task_parameters
+ postgresql_set:
+ <<: *pg_parameters
+ name: archive_command
+ value: ''
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+
+ - name: postgresql_set - set empty string as value again
+ <<: *task_parameters
+ postgresql_set:
+ <<: *pg_parameters
+ name: archive_command
+ value: ''
+ register: result
+
+ - assert:
+ that:
+ - result is not changed
+
+ - name: postgresql_set - set empty string as value again in check mode
+ <<: *task_parameters
+ postgresql_set:
+ <<: *pg_parameters
+ name: archive_command
+ value: ''
+ register: result
+ check_mode: true
+
+ - assert:
+ that:
+ - result is not changed
+
+ - name: Pass non-existent parameter
+ <<: *task_parameters
+ postgresql_set:
+ <<: *pg_parameters
+ name: Timezone
+ value: utc
+ register: result
+ ignore_errors: true
+
+ - assert:
+ that:
+ - result is failed
+ - result.msg is search('No such parameter')
+
+ #######################################################################
+ # https://github.com/ansible-collections/community.postgresql/issues/48
+ - name: Pass a parameter containing b in check_mode
+ <<: *task_parameters
+ postgresql_set:
+ <<: *pg_parameters
+ name: archive_command
+ value: '/usr/bin/touch %f'
+ register: result
+ check_mode: true
+
+ - assert:
+ that:
+ - result is changed
+
+ - name: Pass a parameter containing b
+ <<: *task_parameters
+ postgresql_set:
+ <<: *pg_parameters
+ name: archive_command
+ value: '/usr/bin/touch %f'
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+
+ - name: Pass another parameter containing B in check_mode
+ <<: *task_parameters
+ postgresql_set:
+ <<: *pg_parameters
+ name: track_activity_query_size
+ value: '4096B'
+ register: result
+ check_mode: true
+
+ - assert:
+ that:
+ - result is changed
+
+ - name: Pass another parameter containing b in check_mode
+ <<: *task_parameters
+ postgresql_set:
+ <<: *pg_parameters
+ name: track_activity_query_size
+ value: '2048b'
+ register: result
+ check_mode: true
+
+ - assert:
+ that:
+ - result is changed
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_slot/aliases b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_slot/aliases
new file mode 100644
index 000000000..a4c92ef85
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_slot/aliases
@@ -0,0 +1,2 @@
+destructive
+shippable/posix/group1
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_slot/meta/main.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_slot/meta/main.yml
new file mode 100644
index 000000000..4ce5a5837
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_slot/meta/main.yml
@@ -0,0 +1,2 @@
+dependencies:
+ - setup_postgresql_db
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_slot/tasks/main.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_slot/tasks/main.yml
new file mode 100644
index 000000000..d44aab9de
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_slot/tasks/main.yml
@@ -0,0 +1,9 @@
+####################################################################
+# WARNING: These are designed specifically for Ansible tests #
+# and should not be used as examples of how to write Ansible roles #
+####################################################################
+
+# Initial CI tests of postgresql_slot module
+# Physical replication slots are available since PostgreSQL 9.4
+- import_tasks: postgresql_slot_initial.yml
+ when: postgres_version_resp.stdout is version('9.4', '>=')
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_slot/tasks/postgresql_slot_initial.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_slot/tasks/postgresql_slot_initial.yml
new file mode 100644
index 000000000..23a1cfb0e
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_slot/tasks/postgresql_slot_initial.yml
@@ -0,0 +1,735 @@
+---
+# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) <aaklychkov@mail.ru>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+- name: postgresql_slot - set max_replication_slots
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_set:
+ login_user: "{{ pg_user }}"
+ db: postgres
+ name: max_replication_slots
+ value: '10'
+
+- name: postgresql_slot - set wal_level to logical
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_set:
+ login_user: "{{ pg_user }}"
+ db: postgres
+ name: wal_level
+ value: logical
+
+# To avoid CI timeouts
+- name: Kill all postgres processes
+ shell: 'pkill -u {{ pg_user }}'
+ become: true
+ when: ansible_facts.distribution == 'CentOS' and ansible_facts.distribution_major_version == '8'
+ ignore_errors: true
+
+- name: postgresql_slot - stop PostgreSQL
+ become: true
+ service:
+ name: "{{ postgresql_service }}"
+ state: stopped
+ when: (ansible_facts.distribution_major_version != '8' and ansible_facts.distribution == 'CentOS') or ansible_facts.distribution != 'CentOS'
+
+- name: postgresql_slot - pause between stop and start PostgreSQL
+ ansible.builtin.pause:
+ seconds: 5
+
+- name: postgresql_slot - start PostgreSQL
+ become: true
+ service:
+ name: "{{ postgresql_service }}"
+ state: started
+
+#
+# state=present
+#
+
+# check_mode
+- name: postgresql_slot - create slot in check_mode
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_slot:
+ login_user: "{{ pg_user }}"
+ db: postgres
+ name: slot0
+ check_mode: true
+ register: result
+
+- assert:
+ that:
+ - result is changed
+ - result.queries == []
+
+# Check, rowcount must be 0
+- name: postgresql_slot - check that nothing changed after the previous step
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ query: "SELECT 1 FROM pg_replication_slots WHERE slot_name = 'slot0'"
+ ignore_errors: true
+ register: result
+
+- assert:
+ that:
+ - result.rowcount == 0
+
+# true mode
+- name: postgresql_slot - create physical slot
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_slot:
+ login_user: "{{ pg_user }}"
+ db: postgres
+ name: slot0
+ register: result
+
+- assert:
+ that:
+ - result is changed
+ - result.queries == ["SELECT pg_create_physical_replication_slot('slot0', false)"]
+ when: postgres_version_resp.stdout is version('9.6', '>=')
+
+- assert:
+ that:
+ - result is changed
+ - result.queries == ["SELECT pg_create_physical_replication_slot('slot0')"]
+ when: postgres_version_resp.stdout is version('9.6', '<')
+
+# Check, rowcount must be 1
+- name: postgresql_slot - check that the slot exists after the previous step
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ query: "SELECT 1 FROM pg_replication_slots WHERE slot_name = 'slot0' and slot_type = 'physical'"
+ ignore_errors: true
+ register: result
+
+- assert:
+ that:
+ - result.rowcount == 1
+
+# check mode
+- name: postgresql_slot - try create physical slot again in check_mode
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_slot:
+ login_user: "{{ pg_user }}"
+ db: postgres
+ name: slot0
+ check_mode: true
+ register: result
+
+- assert:
+ that:
+ - result is not changed
+ - result.queries == []
+
+# Check, rowcount must be 1
+- name: postgresql_slot - check that nothing changed after the previous step
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ query: "SELECT 1 FROM pg_replication_slots WHERE slot_name = 'slot0' and slot_type = 'physical'"
+ ignore_errors: true
+ register: result
+
+- assert:
+ that:
+ - result.rowcount == 1
+
+# true mode
+- name: postgresql_slot - try create physical slot again
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_slot:
+ login_user: "{{ pg_user }}"
+ db: postgres
+ name: slot0
+ slot_type: physical
+ register: result
+
+- assert:
+ that:
+ - result is not changed
+ - result.queries == []
+
+# Check, rowcount must be 1
+- name: postgresql_slot - check that nothing changed after the previous step
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ query: "SELECT 1 FROM pg_replication_slots WHERE slot_name = 'slot0' and slot_type = 'physical'"
+ ignore_errors: true
+ register: result
+
+- assert:
+ that:
+ - result.rowcount == 1
+
+#
+# immediately_reserve
+#
+
+- name: postgresql_slot - create physical slot with immediately_reserve
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_slot:
+ login_user: "{{ pg_user }}"
+ db: postgres
+ name: slot1
+ immediately_reserve: true
+ register: result
+ when: postgres_version_resp.stdout is version('9.6', '>=')
+
+- assert:
+ that:
+ - result is changed
+ - result.queries == ["SELECT pg_create_physical_replication_slot('slot1', true)"]
+ when: postgres_version_resp.stdout is version('9.6', '>=')
+
+# Check, rowcount must be 1
+- name: postgresql_slot - check that the slot exists after the previous step
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ query: "SELECT 1 FROM pg_replication_slots WHERE slot_name = 'slot1' and slot_type = 'physical' and restart_lsn is not NULL"
+ ignore_errors: true
+ register: result
+ when: postgres_version_resp.stdout is version('9.6', '>=')
+
+- assert:
+ that:
+ - result.rowcount == 1
+ when: postgres_version_resp.stdout is version('9.6', '>=')
+
+#
+# slot_type: logical
+#
+# available from postgresql 10
+#
+# on RedHat family tests failed:
+# ERROR: could not access file "test_decoding": No such file or directory
+# "Your distrib did not compile the test decoder."
+# So the tests are restricted by Ubuntu because of the module functionality
+# depends on PostgreSQL server version only.
+
+# check_mode
+- name: postgresql_slot - create slot in check_mode
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_slot:
+ login_user: "{{ pg_user }}"
+ db: postgres
+ name: slot2
+ slot_type: logical
+ check_mode: true
+ register: result
+ when: postgres_version_resp.stdout is version('10', '>=') and ansible_distribution == 'Ubuntu'
+
+- assert:
+ that:
+ - result is changed
+ - result.queries == []
+ when: postgres_version_resp.stdout is version('10', '>=') and ansible_distribution == 'Ubuntu'
+
+# Check, rowcount must be 0
+- name: postgresql_slot - check that nothing changed after the previous step
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ query: "SELECT 1 FROM pg_replication_slots WHERE slot_name = 'slot2'"
+ ignore_errors: true
+ register: result
+ when: postgres_version_resp.stdout is version('10', '>=') and ansible_distribution == 'Ubuntu'
+
+- assert:
+ that:
+ - result.rowcount == 0
+ when: postgres_version_resp.stdout is version('10', '>=') and ansible_distribution == 'Ubuntu'
+
+# true mode
+- name: postgresql_slot - create logical slot
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_slot:
+ login_user: "{{ pg_user }}"
+ db: postgres
+ name: slot2
+ slot_type: logical
+ register: result
+ when: postgres_version_resp.stdout is version('10', '>=') and ansible_distribution == 'Ubuntu'
+
+- assert:
+ that:
+ - result is changed
+ - result.queries == ["SELECT pg_create_logical_replication_slot('slot2', 'test_decoding')"]
+ when: postgres_version_resp.stdout is version('10', '>=') and ansible_distribution == 'Ubuntu'
+
+# Check, rowcount must be 1
+- name: postgresql_slot - check that the slot exists after the previous step
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ query: "SELECT 1 FROM pg_replication_slots WHERE slot_name = 'slot2' and slot_type = 'logical'"
+ ignore_errors: true
+ register: result
+ when: postgres_version_resp.stdout is version('10', '>=') and ansible_distribution == 'Ubuntu'
+
+- assert:
+ that:
+ - result.rowcount == 1
+ when: postgres_version_resp.stdout is version('10', '>=') and ansible_distribution == 'Ubuntu'
+
+# check mode
+- name: postgresql_slot - try create logical slot again in check_mode
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_slot:
+ login_user: "{{ pg_user }}"
+ db: postgres
+ name: slot2
+ slot_type: logical
+ check_mode: true
+ register: result
+ when: postgres_version_resp.stdout is version('10', '>=') and ansible_distribution == 'Ubuntu'
+
+- assert:
+ that:
+ - result is not changed
+ - result.queries == []
+ when: postgres_version_resp.stdout is version('10', '>=') and ansible_distribution == 'Ubuntu'
+
+# Check, rowcount must be 1
+- name: postgresql_slot - check that nothing changed after the previous step
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ query: "SELECT 1 FROM pg_replication_slots WHERE slot_name = 'slot2' and slot_type = 'logical'"
+ ignore_errors: true
+ register: result
+ when: postgres_version_resp.stdout is version('10', '>=')
+
+- assert:
+ that:
+ - result.rowcount == 1
+ when: postgres_version_resp.stdout is version('10', '>=') and ansible_distribution == 'Ubuntu'
+
+# true mode
+- name: postgresql_slot - try create logical slot again
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_slot:
+ login_user: "{{ pg_user }}"
+ db: postgres
+ name: slot2
+ slot_type: logical
+ register: result
+ when: postgres_version_resp.stdout is version('10', '>=') and ansible_distribution == 'Ubuntu'
+
+- assert:
+ that:
+ - result is not changed
+ - result.queries == []
+ when: postgres_version_resp.stdout is version('10', '>=') and ansible_distribution == 'Ubuntu'
+
+# Check, rowcount must be 1
+- name: postgresql_slot - check that nothing changed after the previous step
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ query: "SELECT 1 FROM pg_replication_slots WHERE slot_name = 'slot2' and slot_type = 'logical'"
+ ignore_errors: true
+ register: result
+ when: postgres_version_resp.stdout is version('10', '>=') and ansible_distribution == 'Ubuntu'
+
+- assert:
+ that:
+ - result.rowcount == 1
+ when: postgres_version_resp.stdout is version('10', '>=') and ansible_distribution == 'Ubuntu'
+
+#
+# output_plugin: test_decoding
+#
+
+- name: postgresql_slot - create logical slot with output_plugin
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_slot:
+ login_user: "{{ pg_user }}"
+ db: postgres
+ name: slot3
+ slot_type: logical
+ output_plugin: test_decoding
+ register: result
+ when: postgres_version_resp.stdout is version('10', '>=') and ansible_distribution == 'Ubuntu'
+
+- assert:
+ that:
+ - result is changed
+ - result.queries == ["SELECT pg_create_logical_replication_slot('slot3', 'test_decoding')"]
+ when: postgres_version_resp.stdout is version('10', '>=') and ansible_distribution == 'Ubuntu'
+
+# Check, rowcount must be 1
+- name: postgresql_slot - check that the slot exists after the previous step
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ query: "SELECT 1 FROM pg_replication_slots WHERE slot_name = 'slot3' and slot_type = 'logical' and plugin = 'test_decoding'"
+ ignore_errors: true
+ register: result
+ when: postgres_version_resp.stdout is version('10', '>=') and ansible_distribution == 'Ubuntu'
+
+- assert:
+ that:
+ - result.rowcount == 1
+ when: postgres_version_resp.stdout is version('10', '>=') and ansible_distribution == 'Ubuntu'
+
+#
+# state: absent for logical slots
+#
+
+# check_mode
+- name: postgresql_slot - drop logical slot in check_mode
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_slot:
+ login_user: "{{ pg_user }}"
+ db: postgres
+ name: slot2
+ state: absent
+ check_mode: true
+ register: result
+ when: postgres_version_resp.stdout is version('10', '>=') and ansible_distribution == 'Ubuntu'
+
+- assert:
+ that:
+ - result is changed
+ - result.queries == []
+ when: postgres_version_resp.stdout is version('10', '>=') and ansible_distribution == 'Ubuntu'
+
+# Check, rowcount must be 1
+- name: postgresql_slot - check that nothing changed after the previous step
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ query: "SELECT 1 FROM pg_replication_slots WHERE slot_name = 'slot2'"
+ ignore_errors: true
+ register: result
+ when: postgres_version_resp.stdout is version('10', '>=') and ansible_distribution == 'Ubuntu'
+
+- assert:
+ that:
+ - result.rowcount == 1
+ when: postgres_version_resp.stdout is version('10', '>=') and ansible_distribution == 'Ubuntu'
+
+# true mode
+- name: postgresql_slot - drop logical slot
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_slot:
+ login_user: "{{ pg_user }}"
+ db: postgres
+ name: slot2
+ state: absent
+ register: result
+ when: postgres_version_resp.stdout is version('10', '>=') and ansible_distribution == 'Ubuntu'
+
+- assert:
+ that:
+ - result is changed
+ - result.queries == ["SELECT pg_drop_replication_slot('slot2')"]
+ when: postgres_version_resp.stdout is version('10', '>=') and ansible_distribution == 'Ubuntu'
+
+# Check, rowcount must be 0
+- name: postgresql_slot - check that the slot does not exist after the previous step
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ query: "SELECT 1 FROM pg_replication_slots WHERE slot_name = 'slot2' and slot_type = 'logical'"
+ ignore_errors: true
+ register: result
+ when: postgres_version_resp.stdout is version('10', '>=') and ansible_distribution == 'Ubuntu'
+
+- assert:
+ that:
+ - result.rowcount == 0
+ when: postgres_version_resp.stdout is version('10', '>=') and ansible_distribution == 'Ubuntu'
+
+# check mode
+- name: postgresql_slot - try drop logical slot again in check_mode
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_slot:
+ login_user: "{{ pg_user }}"
+ db: postgres
+ name: slot2
+ state: absent
+ check_mode: true
+ register: result
+ when: postgres_version_resp.stdout is version('10', '>=') and ansible_distribution == 'Ubuntu'
+
+- assert:
+ that:
+ - result is not changed
+ - result.queries == []
+ when: postgres_version_resp.stdout is version('10', '>=') and ansible_distribution == 'Ubuntu'
+
+# Check, rowcount must be 0
+- name: postgresql_slot - check that nothing changed after the previous step
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ query: "SELECT 1 FROM pg_replication_slots WHERE slot_name = 'slot2' and slot_type = 'logical'"
+ ignore_errors: true
+ register: result
+ when: postgres_version_resp.stdout is version('10', '>=')
+
+- assert:
+ that:
+ - result.rowcount == 0
+ when: postgres_version_resp.stdout is version('10', '>=') and ansible_distribution == 'Ubuntu'
+
+# true mode
+- name: postgresql_slot - try drop logical slot again
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_slot:
+ login_user: "{{ pg_user }}"
+ db: postgres
+ name: slot2
+ state: absent
+ register: result
+ when: postgres_version_resp.stdout is version('10', '>=') and ansible_distribution == 'Ubuntu'
+
+- assert:
+ that:
+ - result is not changed
+ - result.queries == []
+ when: postgres_version_resp.stdout is version('10', '>=') and ansible_distribution == 'Ubuntu'
+
+# Check, rowcount must be 0
+- name: postgresql_slot - check that nothing changed after the previous step
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ query: "SELECT 1 FROM pg_replication_slots WHERE slot_name = 'slot2' and slot_type = 'logical'"
+ ignore_errors: true
+ register: result
+ when: postgres_version_resp.stdout is version('10', '>=') and ansible_distribution == 'Ubuntu'
+
+- assert:
+ that:
+ - result.rowcount == 0
+ when: postgres_version_resp.stdout is version('10', '>=') and ansible_distribution == 'Ubuntu'
+
+#
+# state=absent for physical slots
+#
+
+# check_mode
+- name: postgresql_slot - drop physical slot in check_mode
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_slot:
+ login_user: "{{ pg_user }}"
+ db: postgres
+ name: slot1
+ state: absent
+ check_mode: true
+ register: result
+
+- assert:
+ that:
+ - result is changed
+ - result.queries == []
+ when: postgres_version_resp.stdout is version('9.6', '>=')
+
+# Check, rowcount must be 1
+- name: postgresql_slot - check that nothing changed after the previous step
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ query: "SELECT 1 FROM pg_replication_slots WHERE slot_name = 'slot1'"
+ ignore_errors: true
+ register: result
+ when: postgres_version_resp.stdout is version('9.6', '>=')
+
+- assert:
+ that:
+ - result.rowcount == 1
+ when: postgres_version_resp.stdout is version('9.6', '>=')
+
+# true mode
+- name: postgresql_slot - drop physical slot
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_slot:
+ login_user: "{{ pg_user }}"
+ db: postgres
+ name: slot1
+ state: absent
+ register: result
+ when: postgres_version_resp.stdout is version('9.6', '>=')
+
+- assert:
+ that:
+ - result is changed
+ - result.queries == ["SELECT pg_drop_replication_slot('slot1')"]
+ when: postgres_version_resp.stdout is version('9.6', '>=')
+
+# Check, rowcount must be 0
+- name: postgresql_slot - check that the slot does not exist after the previous step
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ query: "SELECT 1 FROM pg_replication_slots WHERE slot_name = 'slot1' and slot_type = 'physical'"
+ ignore_errors: true
+ register: result
+ when: postgres_version_resp.stdout is version('9.6', '>=')
+
+- assert:
+ that:
+ - result.rowcount == 0
+ when: postgres_version_resp.stdout is version('9.6', '>=')
+
+# check mode
+- name: postgresql_slot - try drop physical slot again in check_mode
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_slot:
+ login_user: "{{ pg_user }}"
+ db: postgres
+ name: slot1
+ state: absent
+ check_mode: true
+ register: result
+ when: postgres_version_resp.stdout is version('9.6', '>=')
+
+- assert:
+ that:
+ - result is not changed
+ - result.queries == []
+ when: postgres_version_resp.stdout is version('9.6', '>=')
+
+# Check, rowcount must be 0
+- name: postgresql_slot - check that nothing changed after the previous step
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ query: "SELECT 1 FROM pg_replication_slots WHERE slot_name = 'slot1' and slot_type = 'physical'"
+ ignore_errors: true
+ register: result
+ when: postgres_version_resp.stdout is version('9.6', '>=')
+
+- assert:
+ that:
+ - result.rowcount == 0
+ when: postgres_version_resp.stdout is version('9.6', '>=')
+
+# true mode
+- name: postgresql_slot - try drop physical slot again
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_slot:
+ login_user: "{{ pg_user }}"
+ db: postgres
+ name: slot1
+ state: absent
+ register: result
+ when: postgres_version_resp.stdout is version('9.6', '>=')
+
+- assert:
+ that:
+ - result is not changed
+ - result.queries == []
+ when: postgres_version_resp.stdout is version('9.6', '>=')
+
+# Check, rowcount must be 0
+- name: postgresql_slot - check that nothing changed after the previous step
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ query: "SELECT 1 FROM pg_replication_slots WHERE slot_name = 'slot1' and slot_type = 'physical'"
+ ignore_errors: true
+ register: result
+ when: postgres_version_resp.stdout is version('9.6', '>=')
+
+- assert:
+ that:
+ - result.rowcount == 0
+ when: postgres_version_resp.stdout is version('9.6', '>=')
+
+# Check trust input
+- name: postgresql_slot - try using a bad name
+ postgresql_slot:
+ session_role: 'curious.anonymous"; SELECT * FROM information_schema.tables; --'
+ db: postgres
+ name: slot1
+ trust_input: false
+ register: result
+ ignore_errors: true
+ when: postgres_version_resp.stdout is version('9.6', '>=')
+
+- name: postgresql_slot - check that using a dangerous name fails
+ assert:
+ that:
+ - result is failed
+ - result.msg is search('is potentially dangerous')
+ when: postgres_version_resp.stdout is version('9.6', '>=')
+
+#
+# clean up
+#
+- name: postgresql_slot - clean up
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_slot:
+ login_user: "{{ pg_user }}"
+ db: postgres
+ name: "{{ item }}"
+ state: absent
+ ignore_errors: true
+ when: postgres_version_resp.stdout is version('10', '>=') and ansible_distribution == 'Ubuntu'
+ with_items:
+ - slot0
+ - slot3
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_subscription/aliases b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_subscription/aliases
new file mode 100644
index 000000000..786e05315
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_subscription/aliases
@@ -0,0 +1,4 @@
+destructive
+shippable/posix/group1
+skip/freebsd
+skip/rhel
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_subscription/defaults/main.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_subscription/defaults/main.yml
new file mode 100644
index 000000000..8709694ba
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_subscription/defaults/main.yml
@@ -0,0 +1,13 @@
+pg_user: postgres
+db_default: postgres
+
+test_table1: acme1
+test_pub: first_publication
+test_pub2: second_publication
+replication_role: logical_replication
+replication_pass: alsdjfKJKDf1#
+test_db: acme_db
+test_subscription: test
+test_role1: alice
+test_role2: bob
+conn_timeout: 100
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_subscription/meta/main.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_subscription/meta/main.yml
new file mode 100644
index 000000000..d72e4d23c
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_subscription/meta/main.yml
@@ -0,0 +1,2 @@
+dependencies:
+ - setup_postgresql_replication
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_subscription/tasks/main.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_subscription/tasks/main.yml
new file mode 100644
index 000000000..e440e8c80
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_subscription/tasks/main.yml
@@ -0,0 +1,12 @@
+####################################################################
+# WARNING: These are designed specifically for Ansible tests #
+# and should not be used as examples of how to write Ansible roles #
+####################################################################
+
+# Initial tests of postgresql_subscription module:
+
+- import_tasks: setup_publication.yml
+ when: ansible_distribution == 'Ubuntu' and ansible_distribution_major_version >= '18'
+
+- import_tasks: postgresql_subscription_initial.yml
+ when: ansible_distribution == 'Ubuntu' and ansible_distribution_major_version >= '18'
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_subscription/tasks/postgresql_subscription_initial.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_subscription/tasks/postgresql_subscription_initial.yml
new file mode 100644
index 000000000..b464c3dbe
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_subscription/tasks/postgresql_subscription_initial.yml
@@ -0,0 +1,672 @@
+# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) <aaklychkov@mail.ru>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+- vars:
+ dangerous_name: 'curious.anonymous"; SELECT * FROM information_schema.tables; --'
+ task_parameters: &task_parameters
+ become_user: '{{ pg_user }}'
+ become: true
+ register: result
+ pg_parameters: &pg_parameters
+ login_user: '{{ pg_user }}'
+ login_db: '{{ test_db }}'
+
+ block:
+
+ - name: Create roles to test owner parameter
+ <<: *task_parameters
+ postgresql_user:
+ <<: *pg_parameters
+ login_port: '{{ replica_port }}'
+ name: '{{ item }}'
+ role_attr_flags: SUPERUSER,LOGIN
+ loop:
+ - '{{ test_role1 }}'
+ - '{{ test_role2 }}'
+
+ ####################
+ # Test mode: present
+ ####################
+ - name: Create subscription
+ <<: *task_parameters
+ postgresql_subscription:
+ <<: *pg_parameters
+ login_port: '{{ replica_port }}'
+ name: '{{ test_subscription }}'
+ state: present
+ publications: '{{ test_pub }}'
+ connparams:
+ host: 127.0.0.1
+ port: '{{ primary_port }}'
+ user: '{{ replication_role }}'
+ password: '{{ replication_pass }}'
+ dbname: '{{ test_db }}'
+ trust_input: false
+
+ - assert:
+ that:
+ - result is changed
+ - result.name == '{{ test_subscription }}'
+ - result.queries == ["CREATE SUBSCRIPTION test CONNECTION 'host=127.0.0.1 port={{ primary_port }} user={{ replication_role }} password={{ replication_pass }} dbname={{ test_db }}' PUBLICATION {{ test_pub }}"]
+ - result.exists == true
+ - result.initial_state == {}
+ - result.final_state.owner == '{{ pg_user }}'
+ - result.final_state.enabled == true
+ - result.final_state.publications == ["{{ test_pub }}"]
+ - result.final_state.synccommit == true
+ - result.final_state.slotname == '{{ test_subscription }}'
+ - result.final_state.conninfo.dbname == '{{ test_db }}'
+ - result.final_state.conninfo.host == '127.0.0.1'
+ - result.final_state.conninfo.port == {{ primary_port }}
+ - result.final_state.conninfo.user == '{{ replication_role }}'
+ - result.final_state.conninfo.password == '{{ replication_pass }}'
+
+ - name: Check
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ login_port: '{{ replica_port }}'
+ query: "SELECT subname FROM pg_subscription WHERE subname = '{{ test_subscription }}'"
+
+ - assert:
+ that:
+ - result.rowcount == 1
+
+ ###################
+ # Test mode: absent
+ ###################
+
+ - name: Drop subscription in check mode
+ <<: *task_parameters
+ postgresql_subscription:
+ <<: *pg_parameters
+ login_port: '{{ replica_port }}'
+ name: '{{ test_subscription }}'
+ state: absent
+ trust_input: false
+ check_mode: true
+
+ - assert:
+ that:
+ - result is changed
+ - result.queries == ["DROP SUBSCRIPTION {{ test_subscription }}"]
+ - result.final_state == result.initial_state
+
+ - name: Check
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ login_port: '{{ replica_port }}'
+ query: "SELECT subname FROM pg_subscription WHERE subname = '{{ test_subscription }}'"
+
+ - assert:
+ that:
+ - result.rowcount == 1
+
+ - name: Drop subscription
+ <<: *task_parameters
+ postgresql_subscription:
+ <<: *pg_parameters
+ login_port: '{{ replica_port }}'
+ name: '{{ test_subscription }}'
+ state: absent
+
+ - assert:
+ that:
+ - result is changed
+ - result.queries == ["DROP SUBSCRIPTION {{ test_subscription }}"]
+ - result.final_state != result.initial_state
+
+ - name: Check
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ login_port: '{{ replica_port }}'
+ query: "SELECT subname FROM pg_subscription WHERE subname = '{{ test_subscription }}'"
+
+ - assert:
+ that:
+ - result.rowcount == 0
+
+ ##################
+ # Test owner param
+ ##################
+
+ - name: Create with owner
+ <<: *task_parameters
+ postgresql_subscription:
+ <<: *pg_parameters
+ login_port: '{{ replica_port }}'
+ name: '{{ test_subscription }}'
+ state: present
+ publications: '{{ test_pub }}'
+ owner: '{{ test_role1 }}'
+ connparams:
+ host: 127.0.0.1
+ port: '{{ primary_port }}'
+ user: '{{ replication_role }}'
+ password: '{{ replication_pass }}'
+ dbname: '{{ test_db }}'
+ trust_input: false
+
+ - assert:
+ that:
+ - result.final_state.owner == '{{ test_role1 }}'
+ - result.queries[1] == 'ALTER SUBSCRIPTION {{ test_subscription }} OWNER TO "{{ test_role1 }}"'
+
+ - name: Try to set this owner again
+ <<: *task_parameters
+ postgresql_subscription:
+ <<: *pg_parameters
+ login_port: '{{ replica_port }}'
+ name: '{{ test_subscription }}'
+ state: present
+ publications: '{{ test_pub }}'
+ owner: '{{ test_role1 }}'
+ trust_input: false
+
+ - assert:
+ that:
+ - result is not changed
+ - result.initial_state == result.final_state
+ - result.final_state.owner == '{{ test_role1 }}'
+
+ - name: Check
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ login_port: '{{ replica_port }}'
+ query: >
+ SELECT subname FROM pg_subscription AS s
+ JOIN pg_catalog.pg_roles AS r ON s.subowner = r.oid
+ WHERE subname = '{{ test_subscription }}' and r.rolname = '{{ test_role1 }}'
+
+ - assert:
+ that:
+ - result.rowcount == 1
+
+ - name: Set another owner in check mode
+ <<: *task_parameters
+ postgresql_subscription:
+ <<: *pg_parameters
+ login_port: '{{ replica_port }}'
+ name: '{{ test_subscription }}'
+ state: present
+ publications: '{{ test_pub }}'
+ owner: '{{ test_role2 }}'
+ trust_input: false
+ check_mode: true
+
+ - assert:
+ that:
+ - result is changed
+ - result.initial_state == result.final_state
+ - result.final_state.owner == '{{ test_role1 }}'
+ - result.queries == ['ALTER SUBSCRIPTION {{ test_subscription }} OWNER TO "{{ test_role2 }}"']
+
+ - name: Check
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ login_port: '{{ replica_port }}'
+ query: >
+ SELECT subname FROM pg_subscription AS s
+ JOIN pg_catalog.pg_roles AS r ON s.subowner = r.oid
+ WHERE subname = '{{ test_subscription }}' and r.rolname = '{{ test_role1 }}'
+
+ - assert:
+ that:
+ - result.rowcount == 1
+
+ - name: Set another owner
+ <<: *task_parameters
+ postgresql_subscription:
+ <<: *pg_parameters
+ login_port: '{{ replica_port }}'
+ name: '{{ test_subscription }}'
+ state: present
+ publications: '{{ test_pub }}'
+ owner: '{{ test_role2 }}'
+ trust_input: false
+
+ - assert:
+ that:
+ - result is changed
+ - result.initial_state != result.final_state
+ - result.final_state.owner == '{{ test_role2 }}'
+ - result.queries == ['ALTER SUBSCRIPTION {{ test_subscription }} OWNER TO "{{ test_role2 }}"']
+
+ - name: Check
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ login_port: '{{ replica_port }}'
+ query: >
+ SELECT subname FROM pg_subscription AS s
+ JOIN pg_catalog.pg_roles AS r ON s.subowner = r.oid
+ WHERE subname = '{{ test_subscription }}' and r.rolname = '{{ test_role2 }}'
+
+ - assert:
+ that:
+ - result.rowcount == 1
+
+ ##########################
+ # Test trust_input param #
+ ##########################
+
+ - name: Test trust_input parameter
+ <<: *task_parameters
+ postgresql_subscription:
+ <<: *pg_parameters
+ login_port: '{{ replica_port }}'
+ name: '{{ test_subscription }}'
+ state: present
+ publications: '{{ test_pub }}'
+ session_role: '{{ dangerous_name }}'
+ owner: '{{ test_role1 }}'
+ trust_input: false
+ connparams:
+ host: 127.0.0.1
+ port: '{{ primary_port }}'
+ user: '{{ replication_role }}'
+ password: '{{ replication_pass }}'
+ dbname: '{{ test_db }}'
+ ignore_errors: true
+
+ - assert:
+ that:
+ - result is failed
+ - result.msg is search('is potentially dangerous')
+
+ ##############
+ # Test cascade
+ ##############
+
+ - name: Drop subscription cascade in check mode
+ <<: *task_parameters
+ postgresql_subscription:
+ <<: *pg_parameters
+ login_port: '{{ replica_port }}'
+ name: '{{ test_subscription }}'
+ state: absent
+ cascade: true
+ trust_input: false
+ check_mode: true
+
+ - assert:
+ that:
+ - result is changed
+ - result.queries == ["DROP SUBSCRIPTION {{ test_subscription }} CASCADE"]
+ - result.final_state == result.initial_state
+
+ - name: Check
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ login_port: '{{ replica_port }}'
+ query: "SELECT subname FROM pg_subscription WHERE subname = '{{ test_subscription }}'"
+
+ - assert:
+ that:
+ - result.rowcount == 1
+
+ - name: Drop subscription cascade
+ <<: *task_parameters
+ postgresql_subscription:
+ <<: *pg_parameters
+ login_port: '{{ replica_port }}'
+ name: '{{ test_subscription }}'
+ state: absent
+ cascade: true
+
+ - assert:
+ that:
+ - result is changed
+ - result.queries == ["DROP SUBSCRIPTION {{ test_subscription }} CASCADE"]
+ - result.final_state != result.initial_state
+
+ - name: Check
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ login_port: '{{ replica_port }}'
+ query: "SELECT subname FROM pg_subscription WHERE subname = '{{ test_subscription }}'"
+
+ - assert:
+ that:
+ - result.rowcount == 0
+
+ ###########################
+ # Test subsparams parameter
+ ###########################
+
+ - name: Create subscription with subsparams
+ <<: *task_parameters
+ postgresql_subscription:
+ <<: *pg_parameters
+ login_port: '{{ replica_port }}'
+ name: '{{ test_subscription }}'
+ state: present
+ publications: '{{ test_pub }}'
+ connparams:
+ host: 127.0.0.1
+ port: '{{ primary_port }}'
+ user: '{{ replication_role }}'
+ password: '{{ replication_pass }}'
+ dbname: '{{ test_db }}'
+ subsparams:
+ enabled: false
+ synchronous_commit: false
+ trust_input: false
+
+ - assert:
+ that:
+ - result is changed
+ - result.name == '{{ test_subscription }}'
+ - result.queries == ["CREATE SUBSCRIPTION test CONNECTION 'host=127.0.0.1 port={{ primary_port }} user={{ replication_role }} password={{ replication_pass }} dbname={{ test_db }}' PUBLICATION {{ test_pub }} WITH (enabled = false, synchronous_commit = false)"]
+ - result.exists == true
+ - result.final_state.enabled == false
+ - result.final_state.synccommit == false
+
+ - name: Check
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ login_port: '{{ replica_port }}'
+ query: >
+ SELECT subname FROM pg_subscription WHERE subname = '{{ test_subscription }}'
+ AND subenabled = 'f' AND subsynccommit = 'false'
+
+ - assert:
+ that:
+ - result.rowcount == 1
+
+ - name: Enable changed params
+ <<: *task_parameters
+ postgresql_subscription:
+ <<: *pg_parameters
+ login_port: '{{ replica_port }}'
+ name: '{{ test_subscription }}'
+ publications: '{{ test_pub }}'
+ subsparams:
+ enabled: true
+ synchronous_commit: true
+ trust_input: false
+
+ - assert:
+ that:
+ - result is changed
+ - result.name == '{{ test_subscription }}'
+ - result.queries == ["ALTER SUBSCRIPTION {{ test_subscription }} ENABLE", "ALTER SUBSCRIPTION {{ test_subscription }} SET (synchronous_commit = true)"]
+ - result.exists == true
+ - result.final_state.enabled == true
+ - result.final_state.synccommit == true
+
+ - name: Check
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ login_port: '{{ replica_port }}'
+ query: >
+ SELECT subname FROM pg_subscription WHERE subname = '{{ test_subscription }}'
+ AND subenabled = 't' AND subsynccommit = 'true'
+
+ - assert:
+ that:
+ - result.rowcount == 1
+
+ - name: Enable the same params again
+ <<: *task_parameters
+ postgresql_subscription:
+ <<: *pg_parameters
+ login_port: '{{ replica_port }}'
+ name: '{{ test_subscription }}'
+ publications: '{{ test_pub }}'
+ subsparams:
+ enabled: true
+ synchronous_commit: true
+ trust_input: false
+
+ - assert:
+ that:
+ - result is not changed
+ - result.name == '{{ test_subscription }}'
+ - result.queries == []
+ - result.exists == true
+ - result.final_state == result.initial_state
+ - result.final_state.enabled == true
+ - result.final_state.synccommit == true
+
+ ##########################
+ # Test change publications
+ ##########################
+
+ - name: Change publications in check mode
+ <<: *task_parameters
+ postgresql_subscription:
+ <<: *pg_parameters
+ login_port: '{{ replica_port }}'
+ name: '{{ test_subscription }}'
+ state: present
+ publications:
+ - '{{ test_pub }}'
+ - '{{ test_pub2 }}'
+ trust_input: false
+ check_mode: true
+
+ - assert:
+ that:
+ - result is changed
+ - result.name == '{{ test_subscription }}'
+ - result.final_state.publications == result.initial_state.publications
+ - result.final_state.publications == ['{{ test_pub }}']
+ - result.queries == ['ALTER SUBSCRIPTION {{ test_subscription }} SET PUBLICATION {{ test_pub }}, {{ test_pub2 }}']
+
+ - name: Check
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ login_port: '{{ replica_port }}'
+ query: >
+ SELECT subname FROM pg_subscription WHERE subname = '{{ test_subscription }}'
+ AND subpublications = '{"{{ test_pub }}"}'
+
+ - assert:
+ that:
+ - result.rowcount == 1
+
+ - name: Change publications
+ <<: *task_parameters
+ postgresql_subscription:
+ <<: *pg_parameters
+ login_port: '{{ replica_port }}'
+ name: '{{ test_subscription }}'
+ state: present
+ publications:
+ - '{{ test_pub }}'
+ - '{{ test_pub2 }}'
+ trust_input: false
+
+ - assert:
+ that:
+ - result is changed
+ - result.name == '{{ test_subscription }}'
+ - result.final_state.publications != result.initial_state.publications
+ - result.final_state.publications == ['{{ test_pub }}', '{{ test_pub2 }}']
+ - result.queries == ['ALTER SUBSCRIPTION {{ test_subscription }} SET PUBLICATION {{ test_pub }}, {{ test_pub2 }}']
+
+ - name: Check
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ login_port: '{{ replica_port }}'
+ query: >
+ SELECT subname FROM pg_subscription WHERE subname = '{{ test_subscription }}'
+ AND subpublications = '{"{{ test_pub }}", "{{ test_pub2 }}"}'
+
+ - assert:
+ that:
+ - result.rowcount == 1
+
+ - name: Change publications with the same values again
+ <<: *task_parameters
+ postgresql_subscription:
+ <<: *pg_parameters
+ login_port: '{{ replica_port }}'
+ name: '{{ test_subscription }}'
+ state: present
+ publications:
+ - '{{ test_pub }}'
+ - '{{ test_pub2 }}'
+ trust_input: false
+
+ - assert:
+ that:
+ - result is not changed
+ - result.name == '{{ test_subscription }}'
+ - result.final_state.publications == result.initial_state.publications
+ - result.final_state.publications == ['{{ test_pub }}', '{{ test_pub2 }}']
+ - result.queries == []
+
+ - name: Check
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ login_port: '{{ replica_port }}'
+ query: >
+ SELECT subname FROM pg_subscription WHERE subname = '{{ test_subscription }}'
+ AND subpublications = '{"{{ test_pub }}", "{{ test_pub2 }}"}'
+
+ - assert:
+ that:
+ - result.rowcount == 1
+
+ ######################
+ # Test update conninfo
+ ######################
+
+ - name: Change conninfo in check mode
+ <<: *task_parameters
+ postgresql_subscription:
+ <<: *pg_parameters
+ login_port: '{{ replica_port }}'
+ name: '{{ test_subscription }}'
+ state: present
+ connparams:
+ host: 127.0.0.1
+ port: '{{ primary_port }}'
+ user: '{{ replication_role }}'
+ password: '{{ replication_pass }}'
+ dbname: '{{ test_db }}'
+ connect_timeout: '{{ conn_timeout }}'
+ trust_input: false
+ check_mode: true
+
+ - assert:
+ that:
+ - result is changed
+ - result.name == '{{ test_subscription }}'
+ - result.queries == ["ALTER SUBSCRIPTION {{ test_subscription }} CONNECTION 'host=127.0.0.1 port={{ primary_port }} user={{ replication_role }} password={{ replication_pass }} dbname={{ test_db }} connect_timeout={{ conn_timeout }}'"]
+ - result.initial_state.conninfo == result.final_state.conninfo
+
+ - name: Change conninfo
+ <<: *task_parameters
+ postgresql_subscription:
+ <<: *pg_parameters
+ login_port: '{{ replica_port }}'
+ name: '{{ test_subscription }}'
+ state: present
+ connparams:
+ host: 127.0.0.1
+ port: '{{ primary_port }}'
+ user: '{{ replication_role }}'
+ password: '{{ replication_pass }}'
+ dbname: '{{ test_db }}'
+ connect_timeout: '{{ conn_timeout }}'
+ trust_input: false
+
+ - assert:
+ that:
+ - result is changed
+ - result.name == '{{ test_subscription }}'
+ - result.queries == ["ALTER SUBSCRIPTION {{ test_subscription }} CONNECTION 'host=127.0.0.1 port={{ primary_port }} user={{ replication_role }} password={{ replication_pass }} dbname={{ test_db }} connect_timeout={{ conn_timeout }}'"]
+ - result.initial_state.conninfo != result.final_state.conninfo
+
+ - name: Check
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ login_port: '{{ replica_port }}'
+ query: "SELECT * FROM pg_subscription WHERE subname = '{{ test_subscription }}'"
+
+ - assert:
+ that:
+ - result.query_result[0].subconninfo == "host=127.0.0.1 port={{ primary_port }} user={{ replication_role }} password={{ replication_pass }} dbname={{ test_db }} connect_timeout={{ conn_timeout }}"
+
+ - name: Try to change conninfo again with the same values
+ <<: *task_parameters
+ postgresql_subscription:
+ <<: *pg_parameters
+ login_port: '{{ replica_port }}'
+ name: '{{ test_subscription }}'
+ state: present
+ connparams:
+ host: 127.0.0.1
+ port: '{{ primary_port }}'
+ user: '{{ replication_role }}'
+ password: '{{ replication_pass }}'
+ dbname: '{{ test_db }}'
+ connect_timeout: '{{ conn_timeout }}'
+ trust_input: false
+
+ - assert:
+ that:
+ - result is not changed
+ - result.name == '{{ test_subscription }}'
+ - result.queries == []
+ - result.initial_state.conninfo == result.final_state.conninfo
+ - result.final_state.conninfo.connect_timeout == {{ conn_timeout }}
+
+ ####################
+ # Test state refresh
+ ####################
+
+ - name: Refresh in check mode
+ <<: *task_parameters
+ postgresql_subscription:
+ <<: *pg_parameters
+ login_port: '{{ replica_port }}'
+ name: '{{ test_subscription }}'
+ state: refresh
+ check_mode: true
+
+ - assert:
+ that:
+ - result is changed
+ - result.name == '{{ test_subscription }}'
+ - result.queries == ["ALTER SUBSCRIPTION {{ test_subscription }} REFRESH PUBLICATION"]
+
+ - name: Refresh
+ <<: *task_parameters
+ postgresql_subscription:
+ <<: *pg_parameters
+ login_port: '{{ replica_port }}'
+ name: '{{ test_subscription }}'
+ state: refresh
+
+ - assert:
+ that:
+ - result is changed
+ - result.name == '{{ test_subscription }}'
+ - result.queries == ["ALTER SUBSCRIPTION {{ test_subscription }} REFRESH PUBLICATION"]
+
+ ##########
+ # Clean up
+ ##########
+ - name: Drop subscription
+ <<: *task_parameters
+ postgresql_subscription:
+ <<: *pg_parameters
+ login_port: '{{ replica_port }}'
+ name: '{{ test_subscription }}'
+ state: absent
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_subscription/tasks/setup_publication.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_subscription/tasks/setup_publication.yml
new file mode 100644
index 000000000..712b3701d
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_subscription/tasks/setup_publication.yml
@@ -0,0 +1,85 @@
+# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) <aaklychkov@mail.ru>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+# Preparation for further tests of postgresql_subscription module.
+
+- vars:
+ task_parameters: &task_parameters
+ become_user: '{{ pg_user }}'
+ become: true
+ register: result
+ pg_parameters: &pg_parameters
+ login_user: '{{ pg_user }}'
+ login_db: '{{ test_db }}'
+
+ block:
+ - name: postgresql_publication - create test db
+ <<: *task_parameters
+ postgresql_db:
+ login_user: '{{ pg_user }}'
+ login_port: '{{ item }}'
+ maintenance_db: '{{ db_default }}'
+ name: '{{ test_db }}'
+ loop:
+ - '{{ primary_port }}'
+ - '{{ replica_port }}'
+
+ - name: postgresql_publication - create test role
+ <<: *task_parameters
+ postgresql_user:
+ <<: *pg_parameters
+ login_port: '{{ item }}'
+ name: '{{ replication_role }}'
+ password: '{{ replication_pass }}'
+ role_attr_flags: LOGIN,REPLICATION
+ loop:
+ - '{{ primary_port }}'
+ - '{{ replica_port }}'
+
+ - name: postgresql_publication - create test table
+ <<: *task_parameters
+ postgresql_table:
+ <<: *pg_parameters
+ login_port: '{{ item }}'
+ name: '{{ test_table1 }}'
+ columns:
+ - id int
+ loop:
+ - '{{ primary_port }}'
+ - '{{ replica_port }}'
+
+ - name: postgresql_publication - create publication
+ <<: *task_parameters
+ postgresql_publication:
+ <<: *pg_parameters
+ login_port: '{{ primary_port }}'
+ name: '{{ test_pub }}'
+
+ - assert:
+ that:
+ - result is changed
+ - result.exists == true
+ - result.queries == ["CREATE PUBLICATION \"{{ test_pub }}\" FOR ALL TABLES"]
+ - result.owner == '{{ pg_user }}'
+ - result.alltables == true
+ - result.tables == []
+ - result.parameters.publish != {}
+
+ - name: postgresql_publication - create one more publication
+ <<: *task_parameters
+ postgresql_publication:
+ <<: *pg_parameters
+ login_port: '{{ primary_port }}'
+ name: '{{ test_pub2 }}'
+
+ - name: postgresql_publication - check the publication was created
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ login_port: '{{ primary_port }}'
+ query: >
+ SELECT * FROM pg_publication WHERE pubname = '{{ test_pub }}'
+ AND pubowner = '10' AND puballtables = 't'
+
+ - assert:
+ that:
+ - result.rowcount == 1
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_table/aliases b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_table/aliases
new file mode 100644
index 000000000..0d91b7de0
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_table/aliases
@@ -0,0 +1,2 @@
+destructive
+shippable/posix/group5
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_table/meta/main.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_table/meta/main.yml
new file mode 100644
index 000000000..4ce5a5837
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_table/meta/main.yml
@@ -0,0 +1,2 @@
+dependencies:
+ - setup_postgresql_db
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_table/tasks/main.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_table/tasks/main.yml
new file mode 100644
index 000000000..3534c73b0
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_table/tasks/main.yml
@@ -0,0 +1,7 @@
+####################################################################
+# WARNING: These are designed specifically for Ansible tests #
+# and should not be used as examples of how to write Ansible roles #
+####################################################################
+
+# Initial CI tests of postgresql_table module
+- import_tasks: postgresql_table_initial.yml
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_table/tasks/postgresql_table_initial.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_table/tasks/postgresql_table_initial.yml
new file mode 100644
index 000000000..db0f2732e
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_table/tasks/postgresql_table_initial.yml
@@ -0,0 +1,899 @@
+# Test code for the postgresql_set module
+
+# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) <aaklychkov@mail.ru>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+# Create a role for tests:
+- name: postgresql_table - create a role for tests
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_user:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ name: alice
+
+- name: postgresql_table - create test schema
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_schema:
+ database: postgres
+ login_user: "{{ pg_user }}"
+ name: acme
+
+#
+# Check table creation
+#
+
+# Create a simple table in check_mode:
+- name: postgresql_table - create table in check_mode
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_table:
+ login_db: postgres
+ login_port: 5432
+ login_user: "{{ pg_user }}"
+ name: test1
+ owner: alice
+ columns: id int
+ register: result
+ ignore_errors: true
+ check_mode: true
+
+- assert:
+ that:
+ - result is changed
+ - result.table == 'test1'
+ - result.queries == ['CREATE TABLE "test1" (id int)', 'ALTER TABLE "test1" OWNER TO "alice"']
+ - result.state == 'absent'
+
+# Check that the table doesn't exist after the previous step, rowcount must be 0
+- name: postgresql_table - check that table doesn't exist after the previous step
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ query: "SELECT 1 FROM pg_stat_all_tables WHERE relname = 'test1'"
+ ignore_errors: true
+ register: result
+
+- assert:
+ that:
+ - result.rowcount == 0
+
+# Create a simple table:
+- name: postgresql_table - create table
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_table:
+ login_db: postgres
+ login_port: 5432
+ login_user: "{{ pg_user }}"
+ name: test1
+ owner: alice
+ columns: id int
+ register: result
+ ignore_errors: true
+
+- assert:
+ that:
+ - result is changed
+ - result.table == 'test1'
+ - result.queries == ['CREATE TABLE "test1" (id int)', 'ALTER TABLE "test1" OWNER TO "alice"']
+ - result.state == 'present'
+ - result.storage_params == []
+ - result.tablespace == ""
+ - result.owner == "alice"
+
+# Check that the table exists after the previous step, rowcount must be 1
+- name: postgresql_table - check that table exists after the previous step
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ query: "SELECT 1 FROM pg_stat_all_tables WHERE relname = 'test1'"
+ ignore_errors: true
+ register: result
+
+- assert:
+ that:
+ - result.rowcount == 1
+
+# Check that the tableowner is alice
+- name: postgresql_table - check that table owner is alice
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ query: "SELECT 1 FROM pg_tables WHERE tablename = 'test1' AND tableowner = 'alice'"
+ ignore_errors: true
+ register: result
+
+- assert:
+ that:
+ - result.rowcount == 1
+
+#
+# Check create table like another table
+#
+
+# Create a table LIKE another table without any additional parameters in check_mode:
+- name: postgresql_table - create table like in check_mode
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_table:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ name: test2
+ like: test1
+ register: result
+ ignore_errors: true
+ check_mode: true
+
+- assert:
+ that:
+ - result is changed
+ - result.table == 'test2'
+ - result.queries == ['CREATE TABLE "test2" (LIKE "test1")']
+ - result.state == 'absent'
+
+# Check that the table doesn't exist after the previous step, rowcount must be 0
+- name: postgresql_table - check that table doesn't exist after the previous step
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ query: "SELECT 1 FROM pg_stat_all_tables WHERE relname = 'test2'"
+ ignore_errors: true
+ register: result
+
+- assert:
+ that:
+ - result.rowcount == 0
+
+# Create a table LIKE another table without any additional parameters:
+- name: postgresql_table - create table like
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_table:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ name: test2
+ like: test1
+ register: result
+ ignore_errors: true
+
+- assert:
+ that:
+ - result is changed
+ - result.table == 'test2'
+ - result.queries == ['CREATE TABLE "test2" (LIKE "test1")']
+ - result.state == 'present'
+ - result.storage_params == []
+ - result.tablespace == ""
+ - result.owner == "{{ pg_user }}"
+
+# Check that the table exists after the previous step, rowcount must be 1
+- name: postgresql_table - check that table exists after the previous step
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ query: "SELECT 1 FROM pg_stat_all_tables WHERE relname = 'test2'"
+ ignore_errors: true
+ register: result
+
+- assert:
+ that:
+ - result.rowcount == 1
+
+#
+# Check drop table
+#
+
+# Drop a table in check_mode:
+- name: postgresql_table - drop table in check_mode
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_table:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ name: test2
+ state: absent
+ register: result
+ ignore_errors: true
+ check_mode: true
+
+- assert:
+ that:
+ - result is changed
+ - result.queries == ['DROP TABLE "test2"']
+ - result.state == 'present'
+ - result.storage_params == []
+ - result.tablespace == ""
+ - result.owner == "{{ pg_user }}"
+
+# Check that the table exists after the previous step, rowcount must be 1
+- name: postgresql_table - check that table exists after the previous step
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ query: "SELECT 1 FROM pg_stat_all_tables WHERE relname = 'test2'"
+ ignore_errors: true
+ register: result
+
+- assert:
+ that:
+ - result.rowcount == 1
+
+# Drop a table:
+- name: postgresql_table - drop table
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_table:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ name: test2
+ state: absent
+ register: result
+ ignore_errors: true
+
+- assert:
+ that:
+ - result is changed
+ - result.queries == ['DROP TABLE "test2"']
+ - result.state == 'absent'
+
+# Check that the table doesn't exist after the previous step, rowcount must be 0
+- name: postgresql_table - check that table doesn't exist after the previous step
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ query: "SELECT 1 FROM pg_stat_all_tables WHERE relname = 'test2'"
+ ignore_errors: true
+ register: result
+
+- assert:
+ that:
+ - result.rowcount == 0
+
+# Create a table like another table including:
+- name: postgresql_table - create table like with including indexes
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_table:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ name: test2
+ like: test1
+ including: indexes
+ register: result
+ ignore_errors: true
+
+- assert:
+ that:
+ - result is changed
+ - result.queries == ['CREATE TABLE "test2" (LIKE "test1" INCLUDING indexes)']
+ - result.state == 'present'
+ - result.storage_params == []
+ - result.tablespace == ""
+ - result.owner == "{{ pg_user }}"
+
+# Check to create table if it exists:
+- name: postgresql_table - try to create existing table again
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_table:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ name: test2
+ like: test1
+ including: indexes
+ register: result
+ ignore_errors: true
+
+- assert:
+ that:
+ - result is not changed
+
+# Drop the table to prepare for the next step:
+- name: postgresql_table - drop table
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_table:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ name: test2
+ state: absent
+ register: result
+ ignore_errors: true
+
+# Try to drop non existing table:
+- name: postgresql_table - try drop dropped table again
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_table:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ name: test2
+ state: absent
+ register: result
+ ignore_errors: true
+
+- assert:
+ that:
+ - result is not changed
+
+#
+# Change ownership
+#
+
+# Create user to prepare for the next step:
+- name: postgresql_table - create the new user test_user
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_user:
+ login_user: "{{ pg_user }}"
+ db: postgres
+ name: test_user
+ state: present
+ ignore_errors: true
+
+# Try to change owner to test_user in check_mode
+- name: postgresql_table - change table ownership to test_user in check_mode
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_table:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ name: test1
+ owner: test_user
+ register: result
+ ignore_errors: true
+ check_mode: true
+
+- assert:
+ that:
+ - result.owner == 'alice'
+ - result.queries == ['ALTER TABLE "test1" OWNER TO "test_user"']
+ - result.state == 'present'
+ - result is changed
+
+# Check that the tableowner was not changed to test_user
+- name: postgresql_table - check that table owner was not changed
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ query: "SELECT 1 FROM pg_tables WHERE tablename = 'test1' AND tableowner = 'test_user'"
+ ignore_errors: true
+ register: result
+
+- assert:
+ that:
+ - result is not changed
+
+# Try to change owner to test_user
+- name: postgresql_table - change table ownership to test_user
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_table:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ name: test1
+ owner: test_user
+ register: result
+ ignore_errors: true
+
+- assert:
+ that:
+ - result.owner == 'test_user'
+ - result.queries == ['ALTER TABLE "test1" OWNER TO "test_user"']
+ - result.state == 'present'
+ - result is changed
+
+# Check that the tableowner was changed to test_user
+- name: postgresql_table - check that table owner was changed
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ query: "SELECT 1 FROM pg_tables WHERE tablename = 'test1' AND tableowner = 'test_user'"
+ ignore_errors: true
+ register: result
+
+- assert:
+ that:
+ - result.rowcount == 1
+
+#
+# Additional storage parameters
+#
+
+# Create a table with additional storage parameters:
+- name: postgresql_table - create table with storage_params
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_table:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ name: test3
+ columns:
+ - id int
+ - name text
+ storage_params:
+ - fillfactor=10
+ - autovacuum_analyze_threshold=1
+ register: result
+ ignore_errors: true
+
+- assert:
+ that:
+ - result is changed
+ - result.state == 'present'
+ - result.queries == ['CREATE TABLE "test3" (id int,name text) WITH (fillfactor=10,autovacuum_analyze_threshold=1)']
+ - result.storage_params == [ "fillfactor=10", "autovacuum_analyze_threshold=1" ]
+
+# Check storage parameters
+- name: postgresql_table - check storage parameters
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ query: "SELECT reloptions FROM pg_class WHERE relname = 'test3'"
+ ignore_errors: true
+ register: result
+
+- assert:
+ that:
+ - result.query_result[0].reloptions == ["fillfactor=10", "autovacuum_analyze_threshold=1"]
+#
+# Check truncate table
+#
+
+# Insert a row to test table:
+- name: postgresql_table - insert a row
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ query: "INSERT INTO test3 (id, name) VALUES (1, 'first')"
+
+# Truncate a table in check_mode:
+- name: postgresql_table - truncate table
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_table:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ name: test3
+ truncate: true
+ register: result
+ ignore_errors: true
+ check_mode: true
+
+- assert:
+ that:
+ - result is changed
+ - result.queries == ['TRUNCATE TABLE "test3"']
+ - result.state == "present"
+
+# Check the row exists:
+- name: postgresql_table - check that row exists after the previous step
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ query: "SELECT * FROM test3 WHERE id = '1'"
+ ignore_errors: true
+ register: result
+
+- assert:
+ that:
+ - result.rowcount == 1
+
+# Truncate a table. It always returns changed == true
+# because it always creates a new table with the same schema and drop the old table:
+- name: postgresql_table - truncate table
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_table:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ name: test3
+ truncate: true
+ register: result
+ ignore_errors: true
+
+- assert:
+ that:
+ - result is changed
+ - result.queries == ['TRUNCATE TABLE "test3"']
+ - result.state == "present"
+
+# Check the row exists:
+- name: postgresql_table - check that row doesn't exist after the previous step
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ query: "SELECT * FROM test3 WHERE id = '1'"
+ ignore_errors: true
+ register: result
+
+- assert:
+ that:
+ - result.rowcount == 0
+
+#
+# Check rename table
+#
+
+# Rename a table in check_mode.
+# In check_mode test4 won't be exist after the following playbook,
+# so result.changed == 'absent' for the table with this name
+- name: postgresql_table - rename table in check_mode
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_table:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ name: test3
+ rename: test4
+ register: result
+ ignore_errors: true
+ check_mode: true
+
+- assert:
+ that:
+ - result is changed
+ - result.queries == ['ALTER TABLE "test3" RENAME TO "test4"']
+ - result.state == "absent"
+
+# Check that the table exists after the previous step, rowcount must be 1
+- name: postgresql_table - check that table exists after the previous step
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ query: "SELECT 1 FROM pg_stat_all_tables WHERE relname = 'test3'"
+ ignore_errors: true
+ register: result
+
+- assert:
+ that:
+ - result.rowcount == 1
+
+# Rename a table:
+- name: postgresql_table - rename table
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_table:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ name: test3
+ rename: test4
+ register: result
+ ignore_errors: true
+
+- assert:
+ that:
+ - result is changed
+ - result.queries == ['ALTER TABLE "test3" RENAME TO "test4"']
+ - result.state == "present"
+
+# Check that the table test 3 doesn't exist after the previous step, rowcount must be - 0
+- name: postgresql_table - check that table exists after the previous step
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ query: "SELECT 1 FROM pg_stat_all_tables WHERE relname = 'test3'"
+ ignore_errors: true
+ register: result
+
+- assert:
+ that:
+ - result.rowcount == 0
+
+# Check that the table test 4 exists after the previous step, rowcount must be - 1
+- name: postgresql_table - check that table exists after the previous step
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ query: "SELECT 1 FROM pg_stat_all_tables WHERE relname = 'test4'"
+ ignore_errors: true
+ register: result
+
+- assert:
+ that:
+ - result.rowcount == 1
+
+#
+# Check create unlogged table
+#
+
+# Create unlogged table in check_mode:
+- name: postgresql_table - create unlogged table in check_mode
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_table:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ name: test5
+ unlogged: true
+ register: result
+ ignore_errors: true
+ check_mode: true
+
+- assert:
+ that:
+ - result is changed
+ - result.queries == ['CREATE UNLOGGED TABLE "test5" ()']
+ when: postgres_version_resp.stdout is version('9.1', '>=')
+
+# Check that the table doesn't exist after the previous step, rowcount must be - 0
+- name: postgresql_table - check that table doesn't exist after the previous step
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ query: "SELECT 1 FROM pg_stat_all_tables WHERE relname = 'test5'"
+ ignore_errors: true
+ register: result
+
+- assert:
+ that:
+ - result.rowcount == 0
+
+# Create unlogged table:
+- name: postgresql_table - create unlogged table
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_table:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ name: test5
+ unlogged: true
+ register: result
+ ignore_errors: true
+
+- assert:
+ that:
+ - result is changed
+ - result.queries == ['CREATE UNLOGGED TABLE "test5" ()']
+ when: postgres_version_resp.stdout is version('9.1', '>=')
+
+# Check that the table exists after the previous step, rowcount must be - 1
+- name: postgresql_table - check that table exists after the previous step
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ query: "SELECT 1 FROM pg_stat_all_tables WHERE relname = 'test5'"
+ ignore_errors: true
+ register: result
+ when: postgres_version_resp.stdout is version('9.1', '>=')
+
+- assert:
+ that:
+ - result.rowcount == 1
+ when: postgres_version_resp.stdout is version('9.1', '>=')
+
+# Drop table CASCADE:
+- name: postgresql_table - drop table cascade
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_table:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ name: test5
+ state: absent
+ cascade: true
+ register: result
+ ignore_errors: true
+
+- assert:
+ that:
+ - result is changed
+ - result.queries == ['DROP TABLE "test5" CASCADE']
+ when: postgres_version_resp.stdout is version('9.1', '>=')
+
+# Check that the table doesn't exist after the previous step, rowcount must be - 0
+- name: postgresql_table - check that table doesn't exist after the previous step
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ query: "SELECT 1 FROM pg_stat_all_tables WHERE relname = 'test5'"
+ ignore_errors: true
+ register: result
+ when: postgres_version_resp.stdout is version('9.1', '>=')
+
+- assert:
+ that:
+ - result.rowcount == 0
+ when: postgres_version_resp.stdout is version('9.1', '>=')
+
+#
+# Create, drop, and rename table in a specific schema:
+#
+- name: postgresql_table - create table in a specific schema
+ postgresql_table:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ name: acme.test_schema_table
+ register: result
+
+- assert:
+ that:
+ - result is changed
+ - result.queries == ['CREATE TABLE "acme"."test_schema_table" ()']
+
+- name: postgresql_table - check that table exists after the previous step
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ query: "SELECT 1 FROM pg_stat_all_tables WHERE relname = 'test_schema_table' and schemaname = 'acme'"
+ ignore_errors: true
+ register: result
+
+- assert:
+ that:
+ - result.rowcount == 1
+
+- name: postgresql_table - try to create a table with the same name and schema again
+ postgresql_table:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ name: acme.test_schema_table
+ register: result
+
+- assert:
+ that:
+ - result is not changed
+
+- name: postgresql_table - create a table in the default schema for the next test
+ postgresql_table:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ name: test_schema_table
+ register: result
+
+- assert:
+ that:
+ - result is changed
+
+- name: postgresql_table - drop the table from schema acme
+ postgresql_table:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ name: postgres.acme.test_schema_table
+ state: absent
+ register: result
+
+- assert:
+ that:
+ - result is changed
+ - result.queries == ['DROP TABLE "postgres"."acme"."test_schema_table"']
+
+- name: postgresql_table - check that the table doesn't exist after the previous step
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ query: "SELECT 1 FROM pg_stat_all_tables WHERE relname = 'test_schema_table' and schemaname = 'acme'"
+ ignore_errors: true
+ register: result
+
+- assert:
+ that:
+ - result.rowcount == 0
+
+- name: postgresql_table - try to drop the table from schema acme again
+ postgresql_table:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ name: acme.test_schema_table
+ state: absent
+ register: result
+
+- assert:
+ that:
+ - result is not changed
+
+- name: postgresql_table - check that the table with the same name in schema public exists
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_query:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ query: "SELECT 1 FROM pg_stat_all_tables WHERE relname = 'test_schema_table' and schemaname = 'public'"
+ ignore_errors: true
+ register: result
+
+- assert:
+ that:
+ - result.rowcount == 1
+
+- name: postgresql_table - rename the table that contents a schema name
+ postgresql_table:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ name: public.test_schema_table
+ rename: new_test_schema_table
+ trust_input: true
+ register: result
+
+- assert:
+ that:
+ - result is changed
+ - result.queries == ['ALTER TABLE "public"."test_schema_table" RENAME TO "new_test_schema_table"']
+
+############################
+# Test trust_input parameter
+- name: postgresql_table - check trust_input
+ postgresql_table:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ name: postgres.acme.test_schema_table
+ state: absent
+ trust_input: false
+ session_role: 'curious.anonymous"; SELECT * FROM information_schema.tables; --'
+ register: result
+ ignore_errors: true
+
+- assert:
+ that:
+ - result is failed
+ - result.msg is search('is potentially dangerous')
+
+#
+# Clean up
+#
+- name: postgresql_table - drop test schema
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_schema:
+ database: postgres
+ login_user: "{{ pg_user }}"
+ name: acme
+ state: absent
+ cascade_drop: true
+
+- name: postgresql_table - drop test role
+ become_user: "{{ pg_user }}"
+ become: true
+ postgresql_user:
+ db: postgres
+ login_user: "{{ pg_user }}"
+ name: "{{ item }}"
+ state: absent
+ loop:
+ - test_user
+ - alice
+ ignore_errors: true
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_tablespace/aliases b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_tablespace/aliases
new file mode 100644
index 000000000..a4c92ef85
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_tablespace/aliases
@@ -0,0 +1,2 @@
+destructive
+shippable/posix/group1
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_tablespace/defaults/main.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_tablespace/defaults/main.yml
new file mode 100644
index 000000000..1eb5b843f
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_tablespace/defaults/main.yml
@@ -0,0 +1,3 @@
+---
+test_tablespace_path: "/ssd"
+dangerous_name: 'curious.anonymous"; SELECT * FROM information_schema.tables; --'
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_tablespace/meta/main.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_tablespace/meta/main.yml
new file mode 100644
index 000000000..4ce5a5837
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_tablespace/meta/main.yml
@@ -0,0 +1,2 @@
+dependencies:
+ - setup_postgresql_db
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_tablespace/tasks/main.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_tablespace/tasks/main.yml
new file mode 100644
index 000000000..21a47ee3b
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_tablespace/tasks/main.yml
@@ -0,0 +1,7 @@
+####################################################################
+# WARNING: These are designed specifically for Ansible tests #
+# and should not be used as examples of how to write Ansible roles #
+####################################################################
+
+# Initial CI tests of postgresql_tablespace module
+- import_tasks: postgresql_tablespace_initial.yml
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_tablespace/tasks/postgresql_tablespace_initial.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_tablespace/tasks/postgresql_tablespace_initial.yml
new file mode 100644
index 000000000..b8e6d0b54
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_tablespace/tasks/postgresql_tablespace_initial.yml
@@ -0,0 +1,245 @@
+- name: postgresql_tablespace - drop dir for test tablespace
+ become: true
+ file:
+ path: '{{ test_tablespace_path }}'
+ state: absent
+ ignore_errors: true
+
+- name: postgresql_tablespace - disable selinux
+ become: true
+ shell: setenforce 0
+ ignore_errors: true
+
+- name: postgresql_tablespace - create dir for test tablespace
+ become: true
+ file:
+ path: '{{ test_tablespace_path }}'
+ state: directory
+ owner: '{{ pg_user }}'
+ group: '{{ pg_user }}'
+ mode: '0700'
+ ignore_errors: true
+
+- name: postgresql_tablespace - create test role to test change ownership
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_user:
+ db: postgres
+ login_user: '{{ pg_user }}'
+ name: bob
+ state: present
+ ignore_errors: true
+
+- name: postgresql_tablespace - create test role to test change ownership
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_user:
+ db: postgres
+ login_user: '{{ pg_user }}'
+ name: alice
+ state: present
+ ignore_errors: true
+
+- name: postgresql_tablespace - create a new tablespace called acme and set bob as an its owner
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_tablespace:
+ db: postgres
+ login_user: '{{ pg_user }}'
+ name: acme
+ owner: bob
+ location: /ssd
+ register: result
+ ignore_errors: true
+
+- assert:
+ that:
+ - result is changed
+ - result.owner == 'bob'
+ - result.queries == ["CREATE TABLESPACE \"acme\" LOCATION '/ssd'", "ALTER TABLESPACE \"acme\" OWNER TO \"bob\""]
+ - result.state == 'present'
+ - result.tablespace == 'acme'
+ - result.options == {}
+ - result.location == '/ssd'
+
+- name: postgresql_tablespace - try to create the same tablespace with different location
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_tablespace:
+ db: postgres
+ login_user: '{{ pg_user }}'
+ name: acme
+ location: /another-ssd
+ register: result
+ ignore_errors: true
+
+- assert:
+ that:
+ - result is not changed
+ - result.msg == "Tablespace 'acme' exists with different location '/ssd'"
+
+- name: postgresql_tablespace - change tablespace owner to alice
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_tablespace:
+ db: postgres
+ login_user: '{{ pg_user }}'
+ name: acme
+ owner: alice
+ register: result
+ ignore_errors: true
+
+- assert:
+ that:
+ - result is changed
+ - result.owner == 'alice'
+ - result.queries == ["ALTER TABLESPACE \"acme\" OWNER TO \"alice\""]
+ - result.state == 'present'
+ - result.tablespace == 'acme'
+ - result.options == {}
+
+- name: postgresql_tablespace - try to change tablespace owner to alice again to be sure that nothing changes
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_tablespace:
+ db: postgres
+ login_user: '{{ pg_user }}'
+ name: acme
+ owner: alice
+ register: result
+ ignore_errors: true
+
+- assert:
+ that:
+ - result is not changed
+ - result.owner == 'alice'
+ - result.queries == []
+ - result.state == 'present'
+ - result.tablespace == 'acme'
+ - result.options == {}
+
+- name: postgresql_tablespace - change tablespace options
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_tablespace:
+ db: postgres
+ login_user: '{{ pg_user }}'
+ name: acme
+ set:
+ seq_page_cost: 4
+ register: result
+ ignore_errors: true
+
+- assert:
+ that:
+ - result is changed
+ - result.owner == 'alice'
+ - result.queries == ["ALTER TABLESPACE \"acme\" SET (seq_page_cost = '4')"]
+ - result.state == 'present'
+ - result.tablespace == 'acme'
+ - result.options.seq_page_cost == '4'
+ when: postgres_version_resp.stdout is version('9.0', '>=')
+
+- name: postgresql_tablespace - reset seq_page_cost option
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_tablespace:
+ login_db: postgres
+ login_user: '{{ pg_user }}'
+ name: acme
+ set:
+ seq_page_cost: reset
+ register: result
+ ignore_errors: true
+
+- assert:
+ that:
+ - result is changed
+ - result.queries == ["ALTER TABLESPACE \"acme\" RESET (seq_page_cost)"]
+ when: postgres_version_resp.stdout is version('9.0', '>=')
+
+- name: postgresql_tablespace - reset seq_page_cost option again
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_tablespace:
+ db: postgres
+ login_user: '{{ pg_user }}'
+ name: acme
+ set:
+ seq_page_cost: reset
+ register: result
+ ignore_errors: true
+
+- assert:
+ that:
+ - result is not changed
+ - result.queries == []
+ when: postgres_version_resp.stdout is version('9.0', '>=')
+
+- name: postgresql_tablespace - rename tablespace
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_tablespace:
+ db: postgres
+ login_user: '{{ pg_user }}'
+ name: acme
+ rename_to: foo
+ register: result
+ ignore_errors: true
+
+- assert:
+ that:
+ - result is changed
+ - result.newname == 'foo'
+ - result.queries == ["ALTER TABLESPACE \"acme\" RENAME TO \"foo\""]
+
+- name: postgresql_tablespace - rename tablespace to potentially dangerous name
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_tablespace:
+ db: postgres
+ login_user: '{{ pg_user }}'
+ name: foo
+ rename_to: '{{ dangerous_name }}'
+ trust_input: false
+ register: result
+ ignore_errors: true
+
+- assert:
+ that:
+ - result is failed
+ - result.msg == 'Passed input \'{{ dangerous_name }}\' is potentially dangerous'
+
+- name: postgresql_tablespace - drop tablespace
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_tablespace:
+ db: postgres
+ login_user: '{{ pg_user }}'
+ name: foo
+ state: absent
+ trust_input: true
+ register: result
+ ignore_errors: true
+
+- assert:
+ that:
+ - result is changed
+ - result.state == 'absent'
+ - result.queries == ["DROP TABLESPACE \"foo\""]
+
+- name: postgresql_tablespace - try to drop nonexistent tablespace
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_tablespace:
+ db: postgres
+ login_user: '{{ pg_user }}'
+ name: foo
+ state: absent
+ register: result
+ ignore_errors: true
+
+- assert:
+ that:
+ - result is not changed
+ - result.msg == "Tries to drop nonexistent tablespace 'foo'"
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_user/aliases b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_user/aliases
new file mode 100644
index 000000000..a4c92ef85
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_user/aliases
@@ -0,0 +1,2 @@
+destructive
+shippable/posix/group1
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_user/defaults/main.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_user/defaults/main.yml
new file mode 100644
index 000000000..dbcbea120
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_user/defaults/main.yml
@@ -0,0 +1,4 @@
+db_name: 'ansible_db'
+db_user1: 'ansible_db_user1'
+db_user2: 'ansible_db_user2'
+dangerous_name: 'curious.anonymous"; SELECT * FROM information_schema.tables; --'
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_user/meta/main.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_user/meta/main.yml
new file mode 100644
index 000000000..4ce5a5837
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_user/meta/main.yml
@@ -0,0 +1,2 @@
+dependencies:
+ - setup_postgresql_db
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_user/tasks/main.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_user/tasks/main.yml
new file mode 100644
index 000000000..150d26efd
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_user/tasks/main.yml
@@ -0,0 +1,12 @@
+####################################################################
+# WARNING: These are designed specifically for Ansible tests #
+# and should not be used as examples of how to write Ansible roles #
+####################################################################
+
+# Initial CI tests of postgresql_user module:
+- import_tasks: postgresql_user_initial.yml
+ when: postgres_version_resp.stdout is version('9.4', '>=')
+
+# General tests:
+- import_tasks: postgresql_user_general.yml
+ when: postgres_version_resp.stdout is version('9.4', '>=')
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_user/tasks/postgresql_user_general.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_user/tasks/postgresql_user_general.yml
new file mode 100644
index 000000000..cde95b0c6
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_user/tasks/postgresql_user_general.yml
@@ -0,0 +1,802 @@
+# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) <aaklychkov@mail.ru>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+# Integration tests for postgresql_user module.
+
+- vars:
+ test_user: hello.user.with.dots
+ test_user2: hello
+ test_group1: group1
+ test_group2: group2
+ test_table: test
+ test_comment1: 'comment1'
+ test_comment2: 'comment2'
+ task_parameters: &task_parameters
+ become_user: '{{ pg_user }}'
+ become: true
+ register: result
+ pg_parameters: &pg_parameters
+ login_user: '{{ pg_user }}'
+ login_db: postgres
+
+ block:
+ #
+ # Common tests
+ #
+ - name: Create role in check_mode
+ <<: *task_parameters
+ check_mode: true
+ postgresql_user:
+ <<: *pg_parameters
+ name: '{{ test_user }}'
+ trust_input: false
+
+ - assert:
+ that:
+ - result is changed
+ - result.user == '{{ test_user }}'
+
+ - name: check that the user doesn't exist
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: "SELECT rolname FROM pg_roles WHERE rolname = '{{ test_user }}'"
+
+ - assert:
+ that:
+ - result.rowcount == 0
+
+ - name: Create role in actual mode
+ <<: *task_parameters
+ postgresql_user:
+ <<: *pg_parameters
+ name: '{{ test_user }}'
+
+ - assert:
+ that:
+ - result is changed
+ - result.user == '{{ test_user }}'
+
+ - name: check that the user exists
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: "SELECT rolname FROM pg_roles WHERE rolname = '{{ test_user }}'"
+
+ - assert:
+ that:
+ - result.rowcount == 1
+
+ - name: Add a comment on the user
+ <<: *task_parameters
+ postgresql_user:
+ <<: *pg_parameters
+ name: '{{ test_user }}'
+ comment: '{{ test_comment1 }}'
+
+ - assert:
+ that:
+ - result is changed
+ - result.queries == ["COMMENT ON ROLE \"{{ test_user }}\" IS '{{ test_comment1 }}'"]
+
+ - name: check the comment
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: >
+ SELECT pg_catalog.shobj_description(r.oid, 'pg_authid') AS comment
+ FROM pg_catalog.pg_roles r WHERE r.rolname = '{{ test_user }}'
+
+ - assert:
+ that:
+ - result.rowcount == 1
+ - result.query_result[0].comment == '{{ test_comment1 }}'
+
+ - name: Try to add the same comment on the user
+ <<: *task_parameters
+ postgresql_user:
+ <<: *pg_parameters
+ name: '{{ test_user }}'
+ comment: '{{ test_comment1 }}'
+ trust_input: false
+
+ - assert:
+ that:
+ - result is not changed
+
+ - name: Try to add another comment on the user
+ <<: *task_parameters
+ postgresql_user:
+ <<: *pg_parameters
+ name: '{{ test_user }}'
+ comment: '{{ test_comment2 }}'
+
+ - assert:
+ that:
+ - result is changed
+ - result.queries == ["COMMENT ON ROLE \"{{ test_user }}\" IS '{{ test_comment2 }}'"]
+
+ - name: check the comment
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: >
+ SELECT pg_catalog.shobj_description(r.oid, 'pg_authid') AS comment
+ FROM pg_catalog.pg_roles r WHERE r.rolname = '{{ test_user }}'
+
+ - assert:
+ that:
+ - result.rowcount == 1
+ - result.query_result[0].comment == '{{ test_comment2 }}'
+
+ - name: Try to create role again in check_mode
+ <<: *task_parameters
+ check_mode: true
+ postgresql_user:
+ <<: *pg_parameters
+ name: '{{ test_user }}'
+
+ - assert:
+ that:
+ - result is not changed
+ - result.user == '{{ test_user }}'
+
+ - name: check that the user exists
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: "SELECT rolname FROM pg_roles WHERE rolname = '{{ test_user }}'"
+
+ - assert:
+ that:
+ - result.rowcount == 1
+
+ - name: Try to create role again
+ <<: *task_parameters
+ postgresql_user:
+ <<: *pg_parameters
+ name: '{{ test_user }}'
+
+ - assert:
+ that:
+ - result is not changed
+ - result.user == '{{ test_user }}'
+
+ - name: check that the user exists
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: "SELECT rolname FROM pg_roles WHERE rolname = '{{ test_user }}'"
+
+ - assert:
+ that:
+ - result.rowcount == 1
+
+ - name: Drop role in check_mode
+ <<: *task_parameters
+ check_mode: true
+ postgresql_user:
+ <<: *pg_parameters
+ name: '{{ test_user }}'
+ state: absent
+
+ - assert:
+ that:
+ - result is changed
+ - result.user == '{{ test_user }}'
+
+ - name: check that the user actually exists
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: "SELECT rolname FROM pg_roles WHERE rolname = '{{ test_user }}'"
+
+ - assert:
+ that:
+ - result.rowcount == 1
+
+ - name: Drop role in actual mode
+ <<: *task_parameters
+ postgresql_user:
+ <<: *pg_parameters
+ name: '{{ test_user }}'
+ state: absent
+
+ - assert:
+ that:
+ - result is changed
+ - result.user == '{{ test_user }}'
+
+ - name: check that the user doesn't exist
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: "SELECT rolname FROM pg_roles WHERE rolname = '{{ test_user }}'"
+
+ - assert:
+ that:
+ - result.rowcount == 0
+
+ - name: Try to drop role in check mode again
+ <<: *task_parameters
+ check_mode: true
+ postgresql_user:
+ <<: *pg_parameters
+ name: '{{ test_user }}'
+ state: absent
+
+ - assert:
+ that:
+ - result is not changed
+ - result.user == '{{ test_user }}'
+
+ - name: Try to drop role in actual mode again
+ <<: *task_parameters
+ postgresql_user:
+ <<: *pg_parameters
+ name: '{{ test_user }}'
+ state: absent
+
+ - assert:
+ that:
+ - result is not changed
+ - result.user == '{{ test_user }}'
+
+ #
+ # password, no_password_changes, encrypted, expires parameters
+ #
+
+ - name: Create role with password, passed as hashed md5
+ <<: *task_parameters
+ postgresql_user:
+ <<: *pg_parameters
+ name: '{{ test_user }}'
+ password: md59543f1d82624df2b31672ec0f7050460
+
+ - assert:
+ that:
+ - result is changed
+ - result.user == '{{ test_user }}'
+
+ - name: Check that the user exist with a proper password
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: "SELECT rolname FROM pg_authid WHERE rolname = '{{ test_user }}' and rolpassword = 'md59543f1d82624df2b31672ec0f7050460'"
+
+ - assert:
+ that:
+ - result.rowcount == 1
+
+ - name: Test no_password_changes
+ <<: *task_parameters
+ postgresql_user:
+ <<: *pg_parameters
+ name: '{{ test_user }}'
+ password: u123
+ no_password_changes: true
+
+ - assert:
+ that:
+ - result is not changed
+ - result.user == '{{ test_user }}'
+
+
+ - name: Check that nothing changed
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: "SELECT rolname FROM pg_authid WHERE rolname = '{{ test_user }}' and rolpassword = 'md59543f1d82624df2b31672ec0f7050460'"
+
+ - assert:
+ that:
+ - result.rowcount == 1
+
+ # Storing unencrypted passwords is not available from PostgreSQL 10
+ - name: Change password, passed as unencrypted
+ <<: *task_parameters
+ postgresql_user:
+ <<: *pg_parameters
+ name: '{{ test_user }}'
+ password: myunencryptedpass
+ encrypted: false
+ when: postgres_version_resp.stdout is version('10', '<')
+
+ - assert:
+ that:
+ - result is changed
+ - result.user == '{{ test_user }}'
+ when: postgres_version_resp.stdout is version('10', '<')
+
+ - name: Check that the user exist with the unencrypted password
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: "SELECT rolname FROM pg_authid WHERE rolname = '{{ test_user }}' and rolpassword = 'myunencryptedpass'"
+ when: postgres_version_resp.stdout is version('10', '<')
+
+ - assert:
+ that:
+ - result.rowcount == 1
+ when: postgres_version_resp.stdout is version('10', '<')
+
+ - name: Change password, explicit encrypted=true
+ <<: *task_parameters
+ postgresql_user:
+ <<: *pg_parameters
+ name: '{{ test_user }}'
+ password: myunencryptedpass
+ encrypted: true
+
+ - assert:
+ that:
+ - result is changed
+ - result.user == '{{ test_user }}'
+
+ - name: Check that the user exist with encrypted password
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: "SELECT rolname FROM pg_authid WHERE rolname = '{{ test_user }}' and rolpassword != 'myunencryptedpass'"
+
+ - assert:
+ that:
+ - result.rowcount == 1
+
+ - name: Change rolvaliduntil attribute
+ <<: *task_parameters
+ postgresql_user:
+ <<: *pg_parameters
+ name: '{{ test_user }}'
+ expires: 'Jan 31 2020'
+ trust_input: false
+
+ - assert:
+ that:
+ - result is changed
+ - result.user == '{{ test_user }}'
+
+ - name: Check the prev step
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: >
+ SELECT rolname FROM pg_authid WHERE rolname = '{{ test_user }}'
+ AND rolvaliduntil::text like '2020-01-31%'
+
+ - assert:
+ that:
+ - result.rowcount == 1
+
+ - name: Try to set the same rolvaliduntil value again
+ <<: *task_parameters
+ postgresql_user:
+ <<: *pg_parameters
+ name: '{{ test_user }}'
+ expires: 'Jan 31 2020'
+
+ - assert:
+ that:
+ - result is not changed
+ - result.user == '{{ test_user }}'
+
+ - name: Check that nothing changed
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: >
+ SELECT rolname FROM pg_authid WHERE rolname = '{{ test_user }}'
+ AND rolvaliduntil::text like '2020-01-31%'
+
+ - assert:
+ that:
+ - result.rowcount == 1
+
+ #
+ # role_attr_flags
+ #
+ - name: Set role attributes
+ <<: *task_parameters
+ postgresql_user:
+ <<: *pg_parameters
+ name: '{{ test_user }}'
+ role_attr_flags: CREATEROLE,CREATEDB
+ trust_input: false
+
+ - assert:
+ that:
+ - result is changed
+ - result.user == '{{ test_user }}'
+
+ - name: Check the prev step
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: >
+ SELECT rolname FROM pg_authid WHERE rolname = '{{ test_user }}'
+ AND rolcreaterole = 't' and rolcreatedb = 't'
+
+ - assert:
+ that:
+ - result.rowcount == 1
+
+ - name: Set the same role attributes again
+ <<: *task_parameters
+ postgresql_user:
+ <<: *pg_parameters
+ name: '{{ test_user }}'
+ role_attr_flags: CREATEROLE,CREATEDB
+
+ - assert:
+ that:
+ - result is not changed
+ - result.user == '{{ test_user }}'
+
+ - name: Check the prev step
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: >
+ SELECT rolname FROM pg_authid WHERE rolname = '{{ test_user }}'
+ AND rolcreaterole = 't' and rolcreatedb = 't'
+
+ - name: Set role attributes
+ <<: *task_parameters
+ postgresql_user:
+ <<: *pg_parameters
+ name: '{{ test_user }}'
+ role_attr_flags: NOCREATEROLE,NOCREATEDB
+
+ - assert:
+ that:
+ - result is changed
+ - result.user == '{{ test_user }}'
+
+ - name: Check the prev step
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: >
+ SELECT rolname FROM pg_authid WHERE rolname = '{{ test_user }}'
+ AND rolcreaterole = 'f' and rolcreatedb = 'f'
+
+ - assert:
+ that:
+ - result.rowcount == 1
+
+ - name: Set role attributes
+ <<: *task_parameters
+ postgresql_user:
+ <<: *pg_parameters
+ name: '{{ test_user }}'
+ role_attr_flags: NOCREATEROLE,NOCREATEDB
+
+ - assert:
+ that:
+ - result is not changed
+ - result.user == '{{ test_user }}'
+
+ - name: Check the prev step
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: >
+ SELECT rolname FROM pg_authid WHERE rolname = '{{ test_user }}'
+ AND rolcreaterole = 'f' and rolcreatedb = 'f'
+
+ #
+ # priv
+ #
+ - name: Create test table
+ <<: *task_parameters
+ postgresql_table:
+ <<: *pg_parameters
+ name: '{{ test_table }}'
+ columns:
+ - id int
+
+ - name: Insert data to test table
+ <<: *task_parameters
+ postgresql_query:
+ query: "INSERT INTO {{ test_table }} (id) VALUES ('1')"
+ <<: *pg_parameters
+
+ - name: Check that test_user is not allowed to read the data
+ <<: *task_parameters
+ postgresql_query:
+ db: postgres
+ login_user: '{{ pg_user }}'
+ session_role: '{{ test_user }}'
+ query: 'SELECT * FROM {{ test_table }}'
+ ignore_errors: true
+
+ - assert:
+ that:
+ - result is failed
+ - "'permission denied' in result.msg"
+
+ - name: Grant privileges
+ <<: *task_parameters
+ postgresql_user:
+ <<: *pg_parameters
+ name: '{{ test_user }}'
+ priv: '{{ test_table }}:SELECT'
+ trust_input: false
+
+ - assert:
+ that:
+ - result is changed
+
+ - name: Check that test_user is allowed to read the data
+ <<: *task_parameters
+ postgresql_query:
+ db: postgres
+ login_user: '{{ pg_user }}'
+ session_role: '{{ test_user }}'
+ query: 'SELECT * FROM {{ test_table }}'
+
+ - assert:
+ that:
+ - result.rowcount == 1
+
+ - name: Grant the same privileges again
+ <<: *task_parameters
+ postgresql_user:
+ <<: *pg_parameters
+ name: '{{ test_user }}'
+ priv: '{{ test_table }}:SELECT'
+
+ - assert:
+ that:
+ - result is not changed
+
+ - name: Remove test table
+ <<: *task_parameters
+ postgresql_table:
+ <<: *pg_parameters
+ name: '{{ test_table }}'
+ state: absent
+
+ #
+ # fail_on_user
+ #
+ - name: Create role for test
+ <<: *task_parameters
+ postgresql_user:
+ <<: *pg_parameters
+ name: '{{ test_user2 }}'
+
+ - name: Create test table, set owner as test_user
+ <<: *task_parameters
+ postgresql_table:
+ <<: *pg_parameters
+ name: '{{ test_table }}'
+ owner: '{{ test_user2 }}'
+
+ - name: Test fail_on_user
+ <<: *task_parameters
+ postgresql_user:
+ <<: *pg_parameters
+ name: '{{ test_user2 }}'
+ state: absent
+ ignore_errors: true
+
+ - assert:
+ that:
+ - result is failed
+ - result.msg == 'Unable to remove user'
+
+ - name: Test fail_on_user
+ <<: *task_parameters
+ postgresql_user:
+ <<: *pg_parameters
+ name: '{{ test_user }}'
+ fail_on_user: false
+
+ - assert:
+ that:
+ - result is not changed
+
+ #
+ # Test groups parameter
+ #
+ - name: Create test group
+ <<: *task_parameters
+ postgresql_user:
+ <<: *pg_parameters
+ name: '{{ test_group2 }}'
+ role_attr_flags: NOLOGIN
+
+ - name: Create role test_group1 and grant test_group2 to test_group1 in check_mode
+ <<: *task_parameters
+ postgresql_user:
+ <<: *pg_parameters
+ name: '{{ test_group1 }}'
+ groups: '{{ test_group2 }}'
+ role_attr_flags: NOLOGIN
+ check_mode: true
+
+ - assert:
+ that:
+ - result is changed
+ - result.user == '{{ test_group1 }}'
+ - result.queries == ['CREATE USER "{{ test_group1 }}" NOLOGIN', 'GRANT "{{ test_group2 }}" TO "{{ test_group1 }}"']
+
+ - name: check that the user doesn't exist
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: "SELECT rolname FROM pg_roles WHERE rolname = '{{ test_group1 }}'"
+
+ - assert:
+ that:
+ - result.rowcount == 0
+
+ - name: check membership
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: "SELECT grolist FROM pg_group WHERE groname = '{{ test_group2 }}' AND grolist != '{}'"
+
+ - assert:
+ that:
+ - result.rowcount == 0
+
+ - name: Create role test_group1 and grant test_group2 to test_group1
+ <<: *task_parameters
+ postgresql_user:
+ <<: *pg_parameters
+ name: '{{ test_group1 }}'
+ groups: '{{ test_group2 }}'
+ role_attr_flags: NOLOGIN
+ trust_input: false
+
+ - assert:
+ that:
+ - result is changed
+ - result.user == '{{ test_group1 }}'
+ - result.queries == ['CREATE USER "{{ test_group1 }}" NOLOGIN', 'GRANT "{{ test_group2 }}" TO "{{ test_group1 }}"']
+
+ - name: check that the user exists
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: "SELECT rolname FROM pg_roles WHERE rolname = '{{ test_group1 }}'"
+
+ - assert:
+ that:
+ - result.rowcount == 1
+
+ - name: check membership
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: "SELECT grolist FROM pg_group WHERE groname = '{{ test_group2 }}' AND grolist != '{}'"
+
+ - assert:
+ that:
+ - result.rowcount == 1
+
+ - name: Grant test_group2 to test_group1 again
+ <<: *task_parameters
+ postgresql_user:
+ <<: *pg_parameters
+ name: '{{ test_group1 }}'
+ groups: '{{ test_group2 }}'
+
+ - assert:
+ that:
+ - result is not changed
+ - result.user == '{{ test_group1 }}'
+
+ - name: check membership
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: "SELECT grolist FROM pg_group WHERE groname = '{{ test_group2 }}' AND grolist != '{}'"
+
+ - assert:
+ that:
+ - result.rowcount == 1
+
+ - name: Grant groups to existent role
+ <<: *task_parameters
+ postgresql_user:
+ <<: *pg_parameters
+ name: '{{ test_user }}'
+ groups:
+ - '{{ test_group1 }}'
+ - '{{ test_group2 }}'
+ trust_input: false
+
+ - assert:
+ that:
+ - result is changed
+ - result.user == '{{ test_user }}'
+ - result.queries == ['GRANT "{{ test_group1 }}" TO "{{ test_user }}"', 'GRANT "{{ test_group2 }}" TO "{{ test_user }}"']
+
+ - name: check membership
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: "SELECT * FROM pg_group WHERE groname in ('{{ test_group1 }}', '{{ test_group2 }}') AND grolist != '{}'"
+
+ - assert:
+ that:
+ - result.rowcount == 2
+
+ ########################
+ # Test trust_input param
+
+ - name: Create role with potentially dangerous name, don't trust
+ <<: *task_parameters
+ postgresql_user:
+ <<: *pg_parameters
+ name: '{{ dangerous_name }}'
+ trust_input: false
+ ignore_errors: true
+
+ - assert:
+ that:
+ - result is failed
+ - result.msg == 'Passed input \'{{ dangerous_name }}\' is potentially dangerous'
+
+ - name: Create role with potentially dangerous name, trust
+ <<: *task_parameters
+ postgresql_user:
+ <<: *pg_parameters
+ name: '{{ dangerous_name }}'
+
+ - assert:
+ that:
+ - result is changed
+
+ ########################################################################
+ # https://github.com/ansible-collections/community.postgresql/issues/122
+
+ - name: Create role with SELECT
+ <<: *task_parameters
+ postgresql_user:
+ <<: *pg_parameters
+ name: 'issue122'
+ priv: 'pg_catalog.pg_stat_database:SELECT'
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+
+ - name: Create role with SELECT again
+ <<: *task_parameters
+ postgresql_user:
+ <<: *pg_parameters
+ name: 'issue122'
+ priv: 'pg_catalog.pg_stat_database:SELECT'
+ register: result
+
+ - assert:
+ that:
+ - result is not changed
+
+ always:
+ #
+ # Clean up
+ #
+ - name: Drop test table
+ <<: *task_parameters
+ postgresql_table:
+ <<: *pg_parameters
+ name: '{{ test_table }}'
+ state: absent
+
+ - name: Drop test user
+ <<: *task_parameters
+ postgresql_user:
+ <<: *pg_parameters
+ name: '{{ item }}'
+ state: absent
+ loop:
+ - '{{ test_user }}'
+ - '{{ test_user2 }}'
+ - '{{ test_group1 }}'
+ - '{{ test_group2 }}'
+ - '{{ dangerous_name }}'
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_user/tasks/postgresql_user_initial.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_user/tasks/postgresql_user_initial.yml
new file mode 100644
index 000000000..5d057d2dd
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_user/tasks/postgresql_user_initial.yml
@@ -0,0 +1,156 @@
+#
+# Create and destroy user, test 'password' and 'encrypted' parameters
+#
+# unencrypted values are not supported on newer versions
+# do not run the encrypted: no tests if on 10+
+- ansible.builtin.set_fact:
+ encryption_values:
+ - 'true'
+
+- ansible.builtin.set_fact:
+ encryption_values: '{{ encryption_values + ["false"]}}'
+ when: postgres_version_resp.stdout is version('10', '<=')
+
+- include_tasks: test_password.yml
+ vars:
+ encrypted: '{{ loop_item }}'
+ db_password1: 'secretù' # use UTF-8
+ loop: '{{ encryption_values }}'
+ loop_control:
+ loop_var: loop_item
+
+# BYPASSRLS role attribute was introduced in PostgreSQL 9.5, so
+# we want to test attribute management differently depending
+# on the version.
+- ansible.builtin.set_fact:
+ bypassrls_supported: "{{ postgres_version_resp.stdout is version('9.5.0', '>=') }}"
+
+# test 'no_password_change' and 'role_attr_flags' parameters
+- include_tasks: test_no_password_change.yml
+ vars:
+ no_password_changes: '{{ loop_item }}'
+ loop:
+ - 'true'
+ - 'false'
+ loop_control:
+ loop_var: loop_item
+
+### TODO: fail_on_user
+
+#
+# Test login_user functionality
+#
+- name: Create a user to test login module parameters
+ become: true
+ become_user: "{{ pg_user }}"
+ postgresql_user:
+ name: "{{ db_user1 }}"
+ state: "present"
+ encrypted: 'true'
+ password: "password"
+ role_attr_flags: "CREATEDB,LOGIN,CREATEROLE"
+ login_user: "{{ pg_user }}"
+ trust_input: false
+ db: postgres
+
+- name: Create db
+ postgresql_db:
+ name: "{{ db_name }}"
+ state: "present"
+ login_user: "{{ db_user1 }}"
+ login_password: "password"
+ login_host: "localhost"
+
+- name: Check that database created
+ become: true
+ become_user: "{{ pg_user }}"
+ shell: echo "select datname from pg_database where datname = '{{ db_name }}';" | psql -d postgres
+ register: result
+
+- ansible.builtin.assert:
+ that:
+ - "result.stdout_lines[-1] == '(1 row)'"
+
+- name: Create a user
+ postgresql_user:
+ name: "{{ db_user2 }}"
+ state: "present"
+ encrypted: 'true'
+ password: "md55c8ccfd9d6711fc69a7eae647fc54f51"
+ db: "{{ db_name }}"
+ login_user: "{{ db_user1 }}"
+ login_password: "password"
+ login_host: "localhost"
+ trust_input: false
+
+- name: Check that it was created
+ become: true
+ become_user: "{{ pg_user }}"
+ shell: echo "select * from pg_user where usename='{{ db_user2 }}';" | psql -d postgres
+ register: result
+
+- ansible.builtin.assert:
+ that:
+ - "result.stdout_lines[-1] == '(1 row)'"
+
+- name: Grant database privileges
+ postgresql_privs:
+ type: "database"
+ state: "present"
+ roles: "{{ db_user2 }}"
+ privs: "CREATE,connect"
+ objs: "{{ db_name }}"
+ db: "{{ db_name }}"
+ login_user: "{{ db_user1 }}"
+ login_password: "password"
+ login_host: "localhost"
+
+- name: Check that the user has the requested permissions (database)
+ become: true
+ become_user: "{{ pg_user }}"
+ shell: echo "select datacl from pg_database where datname='{{ db_name }}';" | psql {{ db_name }}
+ register: result_database
+
+- ansible.builtin.assert:
+ that:
+ - "result_database.stdout_lines[-1] == '(1 row)'"
+ - "db_user2 ~ '=Cc' in result_database.stdout"
+
+- name: Remove user
+ postgresql_user:
+ name: "{{ db_user2 }}"
+ state: 'absent'
+ priv: "ALL"
+ db: "{{ db_name }}"
+ login_user: "{{ db_user1 }}"
+ login_password: "password"
+ login_host: "localhost"
+ trust_input: false
+
+- name: Check that they were removed
+ become: true
+ become_user: "{{ pg_user }}"
+ shell: echo "select * from pg_user where usename='{{ db_user2 }}';" | psql -d postgres
+ register: result
+
+- ansible.builtin.assert:
+ that:
+ - "result.stdout_lines[-1] == '(0 rows)'"
+
+- name: Destroy DB
+ postgresql_db:
+ state: absent
+ name: "{{ db_name }}"
+ login_user: "{{ db_user1 }}"
+ login_password: "password"
+ login_host: "localhost"
+
+- name: Check that database was destroyed
+ become: true
+ become_user: "{{ pg_user }}"
+ shell: echo "select datname from pg_database where datname = '{{ db_name }}';" | psql -d postgres
+ register: result
+
+- ansible.builtin.assert:
+ that:
+ - "result.stdout_lines[-1] == '(0 rows)'"
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_user/tasks/test_no_password_change.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_user/tasks/test_no_password_change.yml
new file mode 100644
index 000000000..41ecbe61f
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_user/tasks/test_no_password_change.yml
@@ -0,0 +1,167 @@
+- vars:
+ task_parameters: &task_parameters
+ become_user: "{{ pg_user }}"
+ become: true
+ register: result
+ postgresql_parameters: &parameters
+ db: postgres
+ name: "{{ db_user1 }}"
+ login_user: "{{ pg_user }}"
+
+ block:
+
+ - name: Create a user with all role attributes
+ <<: *task_parameters
+ postgresql_user:
+ <<: *parameters
+ state: "present"
+ role_attr_flags: "SUPERUSER,CREATEROLE,CREATEDB,INHERIT,LOGIN{{ bypassrls_supported | ternary(',BYPASSRLS', '') }}"
+ no_password_changes: '{{ no_password_changes }}' # no_password_changes is ignored when user doesn't already exist
+
+ - name: Check that the user has the requested role attributes
+ <<: *task_parameters
+ shell: "echo \"select 'super:'||rolsuper, 'createrole:'||rolcreaterole, 'create:'||rolcreatedb, 'inherit:'||rolinherit, 'login:'||rolcanlogin {{ bypassrls_supported | ternary(\", 'bypassrls:'||rolbypassrls\", '') }} from pg_roles where rolname='{{ db_user1 }}';\" | psql -d postgres"
+
+ - assert:
+ that:
+ - "result.stdout_lines[-1] == '(1 row)'"
+ - "'super:t' in result.stdout_lines[-2]"
+ - "'createrole:t' in result.stdout_lines[-2]"
+ - "'create:t' in result.stdout_lines[-2]"
+ - "'inherit:t' in result.stdout_lines[-2]"
+ - "'login:t' in result.stdout_lines[-2]"
+
+ - block:
+ - name: Check that the user has the requested role attribute BYPASSRLS
+ <<: *task_parameters
+ shell: "echo \"select 'bypassrls:'||rolbypassrls from pg_roles where rolname='{{ db_user1 }}';\" | psql -d postgres"
+
+ - assert:
+ that:
+ - "not bypassrls_supported or 'bypassrls:t' in result.stdout_lines[-2]"
+ when: bypassrls_supported
+
+ - name: Modify a user to have no role attributes
+ <<: *task_parameters
+ postgresql_user:
+ <<: *parameters
+ state: "present"
+ role_attr_flags: "NOSUPERUSER,NOCREATEROLE,NOCREATEDB,noinherit,NOLOGIN{{ bypassrls_supported | ternary(',NOBYPASSRLS', '') }}"
+ no_password_changes: '{{ no_password_changes }}'
+
+ - name: Check that ansible reports it modified the role
+ assert:
+ that:
+ - result is changed
+
+ - name: "Check that the user doesn't have any attribute"
+ <<: *task_parameters
+ shell: "echo \"select 'super:'||rolsuper, 'createrole:'||rolcreaterole, 'create:'||rolcreatedb, 'inherit:'||rolinherit, 'login:'||rolcanlogin from pg_roles where rolname='{{ db_user1 }}';\" | psql -d postgres"
+
+ - assert:
+ that:
+ - "result.stdout_lines[-1] == '(1 row)'"
+ - "'super:f' in result.stdout_lines[-2]"
+ - "'createrole:f' in result.stdout_lines[-2]"
+ - "'create:f' in result.stdout_lines[-2]"
+ - "'inherit:f' in result.stdout_lines[-2]"
+ - "'login:f' in result.stdout_lines[-2]"
+
+ - block:
+ - name: Check that the user has the requested role attribute BYPASSRLS
+ <<: *task_parameters
+ shell: "echo \"select 'bypassrls:'||rolbypassrls from pg_roles where rolname='{{ db_user1 }}';\" | psql -d postgres"
+
+ - assert:
+ that:
+ - "not bypassrls_supported or 'bypassrls:f' in result.stdout_lines[-2]"
+ when: bypassrls_supported
+
+ - name: Try to add an invalid attribute
+ <<: *task_parameters
+ postgresql_user:
+ <<: *parameters
+ state: "present"
+ role_attr_flags: "NOSUPERUSER,NOCREATEROLE,NOCREATEDB,noinherit,NOLOGIN{{ bypassrls_supported | ternary(',NOBYPASSRLS', '') }},INVALID"
+ no_password_changes: '{{ no_password_changes }}'
+ ignore_errors: true
+
+ - name: Check that ansible reports failure
+ assert:
+ that:
+ - result is not changed
+ - result is failed
+ - "result.msg == 'Invalid role_attr_flags specified: INVALID'"
+
+ - name: Modify a single role attribute on a user
+ <<: *task_parameters
+ postgresql_user:
+ <<: *parameters
+ state: "present"
+ role_attr_flags: "LOGIN"
+ no_password_changes: '{{ no_password_changes }}'
+
+ - name: Check that ansible reports it modified the role
+ assert:
+ that:
+ - result is changed
+
+ - name: Check the role attributes
+ <<: *task_parameters
+ shell: echo "select 'super:'||rolsuper, 'createrole:'||rolcreaterole, 'create:'||rolcreatedb, 'inherit:'||rolinherit, 'login:'||rolcanlogin from pg_roles where rolname='{{ db_user1 }}';" | psql -d postgres
+
+ - assert:
+ that:
+ - "result.stdout_lines[-1] == '(1 row)'"
+ - "'super:f' in result.stdout_lines[-2]"
+ - "'createrole:f' in result.stdout_lines[-2]"
+ - "'create:f' in result.stdout_lines[-2]"
+ - "'inherit:f' in result.stdout_lines[-2]"
+ - "'login:t' in result.stdout_lines[-2]"
+
+ - block:
+ - name: Check the role attribute BYPASSRLS
+ <<: *task_parameters
+ shell: echo "select 'bypassrls:'||rolbypassrls from pg_roles where rolname='{{ db_user1 }}';" | psql -d postgres
+
+ - assert:
+ that:
+ - "( postgres_version_resp.stdout is version('9.5.0', '<')) or 'bypassrls:f' in result.stdout_lines[-2]"
+ when: bypassrls_supported
+
+ - name: Check that using same attribute a second time does nothing
+ <<: *task_parameters
+ postgresql_user:
+ <<: *parameters
+ state: "present"
+ role_attr_flags: "LOGIN"
+ no_password_changes: '{{ no_password_changes }}'
+ environment:
+ PGOPTIONS: '-c default_transaction_read_only=on' # ensure 'alter user' query isn't executed
+
+ - name: Check there isn't any update reported
+ assert:
+ that:
+ - result is not changed
+
+ - name: Cleanup the user
+ <<: *task_parameters
+ postgresql_user:
+ <<: *parameters
+ state: 'absent'
+ no_password_changes: '{{ no_password_changes }}' # user deletion: no_password_changes is ignored
+
+ - name: Check that user was removed
+ <<: *task_parameters
+ shell: echo "select * from pg_user where usename='{{ db_user1 }}';" | psql -d postgres
+
+ - assert:
+ that:
+ - "result.stdout_lines[-1] == '(0 rows)'"
+
+ always:
+ - name: Cleanup the user
+ <<: *task_parameters
+ postgresql_user:
+ <<: *parameters
+ state: 'absent'
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_user/tasks/test_password.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_user/tasks/test_password.yml
new file mode 100644
index 000000000..aece258fd
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_user/tasks/test_password.yml
@@ -0,0 +1,429 @@
+- vars:
+ task_parameters: &task_parameters
+ become_user: "{{ pg_user }}"
+ become: true
+ register: result
+ postgresql_query_parameters: &query_parameters
+ db: postgres
+ login_user: "{{ pg_user }}"
+ postgresql_parameters: &parameters
+ <<: *query_parameters
+ name: "{{ db_user1 }}"
+
+ block:
+ - name: 'Check that PGOPTIONS environment variable is effective (1/2)'
+ <<: *task_parameters
+ postgresql_user:
+ <<: *parameters
+ password: '{{ db_password1 }}'
+ ignore_errors: true
+ environment:
+ PGCLIENTENCODING: 'UTF8'
+ PGOPTIONS: '-c default_transaction_read_only=on' # ensure 'alter user' query isn't executed
+
+ - name: 'Check that PGOPTIONS environment variable is effective (2/2)'
+ assert:
+ that:
+ - "{{ result is failed }}"
+
+ - name: 'Create a user (password encrypted: {{ encrypted }})'
+ <<: *task_parameters
+ postgresql_user:
+ <<: *parameters
+ password: '{{ db_password1 }}'
+ encrypted: '{{ encrypted }}'
+ environment:
+ PGCLIENTENCODING: 'UTF8'
+
+ - block: &changed # block is only used here in order to be able to define YAML anchor
+ - name: Check that ansible reports it was created
+ assert:
+ that:
+ - "{{ result is changed }}"
+
+ - name: Check that it was created
+ <<: *task_parameters
+ shell: echo "select * from pg_user where usename='{{ db_user1 }}';" | psql -d postgres
+
+ - assert:
+ that:
+ - "result.stdout_lines[-1] == '(1 row)'"
+
+ - name: Check that creating user a second time does nothing
+ <<: *task_parameters
+ postgresql_user:
+ <<: *parameters
+ password: '{{ db_password1 }}'
+ encrypted: '{{ encrypted }}'
+ environment:
+ PGCLIENTENCODING: 'UTF8'
+ PGOPTIONS: '-c default_transaction_read_only=on' # ensure 'alter user' query isn't executed
+
+ - block: &not_changed # block is only used here in order to be able to define YAML anchor
+ - name: Check that ansible reports no change
+ assert:
+ that:
+ - "{{ result is not changed }}"
+
+ - name: 'Define an expiration time'
+ <<: *task_parameters
+ postgresql_user:
+ <<: *parameters
+ expires: '2025-01-01'
+ environment:
+ PGCLIENTENCODING: 'UTF8'
+
+ - <<: *changed
+
+ - name: 'Redefine the same expiration time'
+ <<: *task_parameters
+ postgresql_user:
+ expires: '2025-01-01'
+ <<: *parameters
+ environment:
+ PGCLIENTENCODING: 'UTF8'
+ PGOPTIONS: '-c default_transaction_read_only=on' # ensure 'alter user' query isn't executed
+
+ - <<: *not_changed
+
+ - block:
+
+ - name: 'Using MD5-hashed password: check that password not changed when using cleartext password'
+ <<: *task_parameters
+ postgresql_user:
+ <<: *parameters
+ password: '{{ db_password1 }}'
+ encrypted: 'true'
+ environment:
+ PGCLIENTENCODING: 'UTF8'
+ PGOPTIONS: '-c default_transaction_read_only=on' # ensure 'alter user' query isn't executed
+
+ - <<: *not_changed
+
+ - name: "Using MD5-hashed password: check that password not changed when using md5 hash with 'ENCRYPTED'"
+ <<: *task_parameters
+ postgresql_user:
+ <<: *parameters
+ password: "md5{{ (db_password1 ~ db_user1) | hash('md5')}}"
+ encrypted: 'true'
+ environment:
+ PGOPTIONS: '-c default_transaction_read_only=on' # ensure 'alter user' query isn't executed
+
+ - <<: *not_changed
+
+ - name: "Using MD5-hashed password: check that password not changed when using md5 hash with 'UNENCRYPTED'"
+ <<: *task_parameters
+ postgresql_user:
+ <<: *parameters
+ password: "md5{{ (db_password1 ~ db_user1) | hash('md5')}}"
+ encrypted: 'false'
+ environment:
+ PGOPTIONS: '-c default_transaction_read_only=on' # ensure 'alter user' query isn't executed
+
+ - <<: *not_changed
+
+ - name: 'Redefine the same expiration time and password (encrypted)'
+ <<: *task_parameters
+ postgresql_user:
+ <<: *parameters
+ encrypted: 'true'
+ password: "md5{{ (db_password1 ~ db_user1) | hash('md5')}}"
+ expires: '2025-01-01'
+ environment:
+ PGOPTIONS: '-c default_transaction_read_only=on' # ensure 'alter user' query isn't executed
+
+ - <<: *not_changed
+
+ - name: 'Using MD5-hashed password: check that password changed when using another cleartext password'
+ <<: *task_parameters
+ postgresql_user:
+ <<: *parameters
+ password: 'prefix{{ db_password1 }}'
+ encrypted: 'true'
+ environment:
+ PGCLIENTENCODING: 'UTF8'
+
+ - <<: *changed
+
+ - name: "Using MD5-hashed password: check that password changed when using another md5 hash with 'ENCRYPTED'"
+ <<: *task_parameters
+ postgresql_user:
+ <<: *parameters
+ password: "md5{{ ('prefix1' ~ db_password1 ~ db_user1) | hash('md5')}}"
+ encrypted: 'true'
+
+ - <<: *changed
+
+ - name: "Using MD5-hashed password: check that password changed when using md5 hash with 'UNENCRYPTED'"
+ <<: *task_parameters
+ postgresql_user:
+ <<: *parameters
+ password: "md5{{ ('prefix2' ~ db_password1 ~ db_user1) | hash('md5')}}"
+ encrypted: 'false'
+ register: change_pass_unencrypted
+ failed_when:
+ - change_pass_unencrypted is failed
+ # newer version of psycopg2 no longer supported unencrypted password, we ignore the error
+ - '"UNENCRYPTED PASSWORD is no longer supported" not in change_pass_unencrypted.msg'
+
+ - <<: *changed
+
+ - name: 'Using MD5-hashed password: check that password changed when clearing the password'
+ <<: *task_parameters
+ postgresql_user:
+ <<: *parameters
+ password: ''
+ encrypted: 'true'
+ environment:
+ PGCLIENTENCODING: 'UTF8'
+
+ - <<: *changed
+
+ - name: 'Using MD5-hashed password: check that password not changed when clearing the password again'
+ <<: *task_parameters
+ postgresql_user:
+ <<: *parameters
+ password: ''
+ encrypted: 'true'
+ environment:
+ PGCLIENTENCODING: 'UTF8'
+ PGOPTIONS: '-c default_transaction_read_only=on' # ensure 'alter user' query isn't executed
+
+ - <<: *not_changed
+
+ - name: 'Using cleartext password: check that password not changed when clearing the password again'
+ <<: *task_parameters
+ postgresql_user:
+ <<: *parameters
+ password: ''
+ encrypted: 'false'
+ environment:
+ PGCLIENTENCODING: 'UTF8'
+ PGOPTIONS: '-c default_transaction_read_only=on' # ensure 'alter user' query isn't executed
+
+ - <<: *not_changed
+
+ - name: 'Using MD5-hashed password: check that password changed when using a cleartext password'
+ <<: *task_parameters
+ postgresql_user:
+ <<: *parameters
+ password: '{{ db_password1 }}'
+ encrypted: 'true'
+ environment:
+ PGCLIENTENCODING: 'UTF8'
+
+ - <<: *changed
+
+ when: encrypted == 'true' and postgres_version_resp.stdout is version('14', '<')
+
+ - block:
+
+ - name: 'Using cleartext password: check that password not changed when using cleartext password'
+ <<: *task_parameters
+ postgresql_user:
+ <<: *parameters
+ password: "{{ db_password1 }}"
+ encrypted: 'false'
+ environment:
+ PGCLIENTENCODING: 'UTF8'
+ PGOPTIONS: '-c default_transaction_read_only=on' # ensure 'alter user' query isn't executed
+
+ - <<: *not_changed
+
+ - name: 'Redefine the same expiration time and password (not encrypted)'
+ <<: *task_parameters
+ postgresql_user:
+ <<: *parameters
+ password: "{{ db_password1 }}"
+ encrypted: 'false'
+ expires: '2025-01-01'
+ environment:
+ PGCLIENTENCODING: 'UTF8'
+ PGOPTIONS: '-c default_transaction_read_only=on' # ensure 'alter user' query isn't executed
+
+ - <<: *not_changed
+
+ - name: 'Using cleartext password: check that password changed when using another cleartext password'
+ <<: *task_parameters
+ postgresql_user:
+ <<: *parameters
+ password: "changed{{ db_password1 }}"
+ encrypted: 'false'
+ environment:
+ PGCLIENTENCODING: 'UTF8'
+
+ - <<: *changed
+
+ - name: 'Using cleartext password: check that password changed when clearing the password'
+ <<: *task_parameters
+ postgresql_user:
+ <<: *parameters
+ password: ''
+ encrypted: 'false'
+ environment:
+ PGCLIENTENCODING: 'UTF8'
+
+ - <<: *changed
+
+ - name: 'Using cleartext password: check that password not changed when clearing the password again'
+ <<: *task_parameters
+ postgresql_user:
+ <<: *parameters
+ password: ''
+ encrypted: 'false'
+ environment:
+ PGCLIENTENCODING: 'UTF8'
+ PGOPTIONS: '-c default_transaction_read_only=on' # ensure 'alter user' query isn't executed
+
+ - <<: *not_changed
+
+ - name: 'Using MD5-hashed password: check that password not changed when clearing the password again'
+ <<: *task_parameters
+ postgresql_user:
+ <<: *parameters
+ password: ''
+ encrypted: 'true'
+ environment:
+ PGCLIENTENCODING: 'UTF8'
+ PGOPTIONS: '-c default_transaction_read_only=on' # ensure 'alter user' query isn't executed
+
+ - <<: *not_changed
+
+ - name: 'Using cleartext password: check that password changed when using cleartext password'
+ <<: *task_parameters
+ postgresql_user:
+ <<: *parameters
+ password: "{{ db_password1 }}"
+ encrypted: 'false'
+ environment:
+ PGCLIENTENCODING: 'UTF8'
+
+ - <<: *changed
+
+ when: encrypted == 'false'
+
+ # start of block scram-sha-256
+ # scram-sha-256 password encryption type is supported since PostgreSQL 10
+ - when: postgres_version_resp.stdout is version('10', '>=')
+ block:
+
+ - name: 'Using cleartext password with scram-sha-256: resetting password'
+ <<: *task_parameters
+ postgresql_user:
+ <<: *parameters
+ password: ""
+ encrypted: "{{ encrypted }}"
+ environment:
+ PGCLIENTENCODING: 'UTF8'
+
+ - name: 'Using cleartext password with scram-sha-256: check that password is changed when using cleartext password'
+ <<: *task_parameters
+ postgresql_user:
+ <<: *parameters
+ password: "{{ db_password1 }}"
+ encrypted: "{{ encrypted }}"
+ environment:
+ PGCLIENTENCODING: 'UTF8'
+ # ansible postgresql_user module interface does not (yet) support forcing password_encryption
+ # type value, we'll have to hack it in env variable to force correct encryption
+ PGOPTIONS: "-c password_encryption=scram-sha-256"
+
+ - <<: *changed
+
+ - name: 'Using cleartext password with scram-sha-256: ensure password is properly encrypted'
+ <<: *task_parameters
+ postgresql_query:
+ <<: *query_parameters
+ query: select * from pg_authid where rolname=%s and rolpassword like %s
+ positional_args:
+ - '{{ db_user1 }}'
+ - 'SCRAM-SHA-256$%'
+
+ - assert:
+ that:
+ - result.rowcount == 1
+
+ - name: 'Using cleartext password with scram-sha-256: check that password is not changed when using the same password'
+ <<: *task_parameters
+ postgresql_user:
+ <<: *parameters
+ password: "{{ db_password1 }}"
+ encrypted: "{{ encrypted }}"
+ environment:
+ PGCLIENTENCODING: 'UTF8'
+ PGOPTIONS: "-c password_encryption=scram-sha-256"
+
+ - <<: *not_changed
+
+ - name: 'Using cleartext password with scram-sha-256: check that password is changed when using another cleartext password'
+ <<: *task_parameters
+ postgresql_user:
+ <<: *parameters
+ password: "changed{{ db_password1 }}"
+ encrypted: "{{ encrypted }}"
+ environment:
+ PGCLIENTENCODING: 'UTF8'
+ PGOPTIONS: "-c password_encryption=scram-sha-256"
+
+ - <<: *changed
+
+ - name: 'Using cleartext password with scram-sha-256: check that password is changed when clearing the password'
+ <<: *task_parameters
+ postgresql_user:
+ <<: *parameters
+ password: ''
+ encrypted: "{{ encrypted }}"
+ environment:
+ PGCLIENTENCODING: 'UTF8'
+ PGOPTIONS: "-c password_encryption=scram-sha-256"
+
+ - <<: *changed
+
+ - name: 'Using cleartext password with scram-sha-256: check that password is not changed when clearing the password again'
+ <<: *task_parameters
+ postgresql_user:
+ <<: *parameters
+ password: ''
+ encrypted: "{{ encrypted }}"
+ environment:
+ PGCLIENTENCODING: 'UTF8'
+ PGOPTIONS: "-c password_encryption=scram-sha-256"
+
+ - <<: *not_changed
+
+ # end of block scram-sha-256
+
+ - name: Remove user
+ <<: *task_parameters
+ postgresql_user:
+ state: 'absent'
+ <<: *parameters
+
+ - <<: *changed
+
+ - name: Check that they were removed
+ <<: *task_parameters
+ shell: echo "select * from pg_user where usename='{{ db_user1 }}';" | psql -d postgres
+ environment:
+ PGOPTIONS: '-c default_transaction_read_only=on' # ensure 'alter user' query isn't executed
+
+ - assert:
+ that:
+ - "result.stdout_lines[-1] == '(0 rows)'"
+
+ - name: Check that removing user a second time does nothing
+ <<: *task_parameters
+ postgresql_user:
+ state: 'absent'
+ <<: *parameters
+ environment:
+ PGOPTIONS: '-c default_transaction_read_only=on' # ensure 'alter user' query isn't executed
+
+ - <<: *not_changed
+
+ always:
+ - name: Remove user
+ <<: *task_parameters
+ postgresql_user:
+ state: 'absent'
+ <<: *parameters
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_user_obj_stat_info/aliases b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_user_obj_stat_info/aliases
new file mode 100644
index 000000000..786e05315
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_user_obj_stat_info/aliases
@@ -0,0 +1,4 @@
+destructive
+shippable/posix/group1
+skip/freebsd
+skip/rhel
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_user_obj_stat_info/defaults/main.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_user_obj_stat_info/defaults/main.yml
new file mode 100644
index 000000000..f697cefd3
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_user_obj_stat_info/defaults/main.yml
@@ -0,0 +1,12 @@
+pg_user: postgres
+db_default: postgres
+
+test_table1: acme1
+test_table2: acme2
+test_table3: acme3
+test_idx1: idx1
+test_idx2: idx2
+test_func1: func1
+test_func2: func2
+test_func3: func3
+test_schema1: schema1
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_user_obj_stat_info/meta/main.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_user_obj_stat_info/meta/main.yml
new file mode 100644
index 000000000..4ce5a5837
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_user_obj_stat_info/meta/main.yml
@@ -0,0 +1,2 @@
+dependencies:
+ - setup_postgresql_db
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_user_obj_stat_info/tasks/main.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_user_obj_stat_info/tasks/main.yml
new file mode 100644
index 000000000..fa47fdc58
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_user_obj_stat_info/tasks/main.yml
@@ -0,0 +1,8 @@
+####################################################################
+# WARNING: These are designed specifically for Ansible tests #
+# and should not be used as examples of how to write Ansible roles #
+####################################################################
+
+# Initial tests of postgresql_user_obj_stat_info module:
+- import_tasks: postgresql_user_obj_stat_info.yml
+ when: postgres_version_resp.stdout is version('9.4', '>=')
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/postgresql_user_obj_stat_info/tasks/postgresql_user_obj_stat_info.yml b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_user_obj_stat_info/tasks/postgresql_user_obj_stat_info.yml
new file mode 100644
index 000000000..62f72d9ec
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/postgresql_user_obj_stat_info/tasks/postgresql_user_obj_stat_info.yml
@@ -0,0 +1,222 @@
+---
+# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) <aaklychkov@mail.ru>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+- vars:
+ task_parameters: &task_parameters
+ become_user: '{{ pg_user }}'
+ become: true
+ register: result
+ pg_parameters: &pg_parameters
+ login_user: '{{ pg_user }}'
+ login_db: '{{ db_default }}'
+
+ block:
+ # Preparation:
+ # 0. create test schema
+ # 1. create test tables
+ # 2. create test indexes
+ # 3. create test functions
+ # 4. enable track_functions and restart
+
+ - name: Create schema
+ <<: *task_parameters
+ postgresql_schema:
+ <<: *pg_parameters
+ name: '{{ test_schema1 }}'
+
+ - name: Create test tables
+ <<: *task_parameters
+ postgresql_table:
+ <<: *pg_parameters
+ name: '{{ item }}'
+ columns:
+ - id int
+ loop:
+ - '{{ test_table1 }}'
+ - '{{ test_table2 }}'
+
+ - name: Create test table in another schema
+ <<: *task_parameters
+ postgresql_table:
+ <<: *pg_parameters
+ name: '{{ test_schema1 }}.{{ test_table3 }}'
+
+ - name: Create test indexes
+ <<: *task_parameters
+ postgresql_idx:
+ <<: *pg_parameters
+ name: '{{ item }}'
+ table: '{{ test_table1 }}'
+ columns:
+ - id
+ loop:
+ - '{{ test_idx1 }}'
+ - '{{ test_idx2 }}'
+
+ - name: Set track_function (restart is required)
+ <<: *task_parameters
+ postgresql_set:
+ <<: *pg_parameters
+ name: track_functions
+ value: all
+
+ # To avoid CI timeouts
+ - name: Kill all postgres processes
+ shell: 'pkill -u {{ pg_user }}'
+ become: true
+ when: ansible_facts.distribution == 'CentOS' and ansible_facts.distribution_major_version == '8'
+ ignore_errors: true
+
+ - name: Stop PostgreSQL
+ become: true
+ service:
+ name: "{{ postgresql_service }}"
+ state: stopped
+ when: (ansible_facts.distribution_major_version != '8' and ansible_facts.distribution == 'CentOS') or ansible_facts.distribution != 'CentOS'
+
+ - name: Pause between stop and start PosgreSQL
+ pause:
+ seconds: 5
+
+ - name: Start PostgreSQL
+ become: true
+ service:
+ name: "{{ postgresql_service }}"
+ state: started
+
+ - name: Create test functions
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: 'CREATE FUNCTION {{ item }}() RETURNS boolean AS $$ BEGIN RETURN 1; END; $$ LANGUAGE PLPGSQL'
+ loop:
+ - '{{ test_func1 }}'
+ - '{{ test_func2 }}'
+ - '{{ test_schema1 }}.{{ test_func3 }}'
+
+ - name: Touch test functions
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: 'SELECT {{ item }}()'
+ loop:
+ - '{{ test_func1 }}'
+ - '{{ test_func2 }}'
+ - '{{ test_schema1 }}.{{ test_func3 }}'
+
+ #######
+ # Tests
+ #######
+ # 0. Without filter
+ - name: Collect all stats
+ <<: *task_parameters
+ postgresql_user_obj_stat_info:
+ <<: *pg_parameters
+
+ - assert:
+ that:
+ - result is not changed
+ - result.tables.public.{{ test_table1 }}.size == 0
+ - result.tables.public.{{ test_table1 }}.size == 0
+ - result.tables.{{ test_schema1 }}.{{ test_table3 }}.size == 0
+ - result.functions.public.{{ test_func1 }}.calls == 1
+ - result.functions.public.{{ test_func2 }}.calls == 1
+ - result.functions.{{ test_schema1 }}.{{ test_func3 }}.calls == 1
+ - result.indexes.public.{{ test_idx1 }}.idx_scan == 0
+ - result.indexes.public.{{ test_idx2 }}.idx_scan == 0
+
+ # 1. With filter
+ - name: Collect stats with filter
+ <<: *task_parameters
+ postgresql_user_obj_stat_info:
+ <<: *pg_parameters
+ filter: tables, indexes
+
+ - assert:
+ that:
+ - result is not changed
+ - result.tables.public.{{ test_table1 }}.size == 0
+ - result.tables.public.{{ test_table1 }}.size == 0
+ - result.tables.{{ test_schema1 }}.{{ test_table3 }}.size == 0
+ - result.functions == {}
+ - result.indexes.public.{{ test_idx1 }}.idx_scan == 0
+ - result.indexes.public.{{ test_idx2 }}.idx_scan == 0
+
+ # 2. With schema
+ - name: Collect stats for objects in certain schema
+ <<: *task_parameters
+ postgresql_user_obj_stat_info:
+ <<: *pg_parameters
+ schema: public
+
+ - assert:
+ that:
+ - result is not changed
+ - result.tables.public.{{ test_table1 }}.size == 0
+ - result.tables.public.{{ test_table1 }}.size == 0
+ - result.indexes.public.{{ test_idx1 }}.idx_scan == 0
+ - result.indexes.public.{{ test_idx2 }}.idx_scan == 0
+ - result.functions.public.{{ test_func1 }}.calls == 1
+ - result.functions.public.{{ test_func2 }}.calls == 1
+ - result.tables.{{ test_schema1 }} is not defined
+
+
+ # 3. With wrong schema
+ - name: Try to collect data in nonexistent schema
+ <<: *task_parameters
+ postgresql_user_obj_stat_info:
+ <<: *pg_parameters
+ schema: nonexistent
+ ignore_errors: true
+
+ - assert:
+ that:
+ - result is failed
+ - result.msg == "Schema 'nonexistent' does not exist"
+
+ # 4. Test Trust Input
+ - name: Try running with SQL injection
+ <<: *task_parameters
+ postgresql_user_obj_stat_info:
+ <<: *pg_parameters
+ session_role: 'curious.anonymous"; SELECT * FROM information_schema.tables; --'
+ trust_input: false
+ ignore_errors: true
+
+ - assert:
+ that:
+ - result is failed
+ - result.msg is search('is potentially dangerous')
+
+ ##########
+ # Clean up
+ ##########
+ - name: Drop schema
+ <<: *task_parameters
+ postgresql_schema:
+ <<: *pg_parameters
+ name: '{{ test_schema1 }}'
+ state: absent
+ cascade_drop: true
+
+ - name: Drop test tables
+ <<: *task_parameters
+ postgresql_table:
+ <<: *pg_parameters
+ name: '{{ item }}'
+ state: absent
+ loop:
+ - '{{ test_table1 }}'
+ - '{{ test_table2 }}'
+
+ - name: Drop test functions
+ <<: *task_parameters
+ postgresql_query:
+ <<: *pg_parameters
+ query: 'DROP FUNCTION {{ item }}()'
+ loop:
+ - '{{ test_func1 }}'
+ - '{{ test_func2 }}'
+ - '{{ test_schema1 }}.{{ test_func3 }}'
+ ignore_errors: true
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/setup_pkg_mgr/tasks/main.yml b/ansible_collections/community/postgresql/tests/integration/targets/setup_pkg_mgr/tasks/main.yml
new file mode 100644
index 000000000..cc4e3b0dd
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/setup_pkg_mgr/tasks/main.yml
@@ -0,0 +1,17 @@
+---
+####################################################################
+# WARNING: These are designed specifically for Ansible tests #
+# and should not be used as examples of how to write Ansible roles #
+####################################################################
+
+- set_fact:
+ pkg_mgr: community.general.pkgng
+ ansible_pkg_mgr: community.general.pkgng
+ cacheable: true
+ when: ansible_os_family == "FreeBSD"
+
+- set_fact:
+ pkg_mgr: community.general.zypper
+ ansible_pkg_mgr: community.general.zypper
+ cacheable: true
+ when: ansible_os_family == "Suse"
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/defaults/main.yml b/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/defaults/main.yml
new file mode 100644
index 000000000..973d41591
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/defaults/main.yml
@@ -0,0 +1,21 @@
+postgresql_service: postgresql
+
+postgresql_packages:
+ - postgresql-server
+ - python-psycopg2
+
+pg_user: postgres
+pg_group: root
+
+locale_latin_suffix:
+locale_utf8_suffix:
+
+postgis: postgis
+
+# defaults for test SSL
+ssl_db: 'ssl_db'
+ssl_user: 'ssl_user'
+ssl_pass: 'ssl_pass'
+ssl_rootcert: '/etc/server-ca.crt'
+ssl_cert: '/etc/client.crt'
+ssl_key: '/etc/client.key' \ No newline at end of file
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/files/dummy--0.sql b/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/files/dummy--0.sql
new file mode 100644
index 000000000..626b0fb48
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/files/dummy--0.sql
@@ -0,0 +1,2 @@
+CREATE OR REPLACE FUNCTION dummy_display_ext_version()
+RETURNS text LANGUAGE SQL AS 'SELECT (''0'')::text';
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/files/dummy--1.0.sql b/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/files/dummy--1.0.sql
new file mode 100644
index 000000000..53c79666b
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/files/dummy--1.0.sql
@@ -0,0 +1,2 @@
+CREATE OR REPLACE FUNCTION dummy_display_ext_version()
+RETURNS text LANGUAGE SQL AS 'SELECT (''1.0'')::text';
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/files/dummy--2.0.sql b/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/files/dummy--2.0.sql
new file mode 100644
index 000000000..227ba1b4c
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/files/dummy--2.0.sql
@@ -0,0 +1,2 @@
+CREATE OR REPLACE FUNCTION dummy_display_ext_version()
+RETURNS text LANGUAGE SQL AS 'SELECT (''2.0'')::text';
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/files/dummy--3-1.0-1.sql b/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/files/dummy--3-1.0-1.sql
new file mode 100644
index 000000000..3c23cb5d1
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/files/dummy--3-1.0-1.sql
@@ -0,0 +1,2 @@
+CREATE OR REPLACE FUNCTION dummy_display_ext_version()
+RETURNS text LANGUAGE SQL AS 'SELECT (''3-1.0-1'')::text';
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/files/dummy--3-1.0.sql b/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/files/dummy--3-1.0.sql
new file mode 100644
index 000000000..252680ad6
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/files/dummy--3-1.0.sql
@@ -0,0 +1,2 @@
+CREATE OR REPLACE FUNCTION dummy_display_ext_version()
+RETURNS text LANGUAGE SQL AS 'SELECT (''3-1.0'')::text';
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/files/dummy--3-1.foo.sql b/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/files/dummy--3-1.foo.sql
new file mode 100644
index 000000000..6d706ede9
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/files/dummy--3-1.foo.sql
@@ -0,0 +1,2 @@
+CREATE OR REPLACE FUNCTION dummy_display_ext_version()
+RETURNS text LANGUAGE SQL AS 'SELECT (''3-1.foo'')::text';
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/files/dummy--3.0-1.sql b/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/files/dummy--3.0-1.sql
new file mode 100644
index 000000000..b366bd17d
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/files/dummy--3.0-1.sql
@@ -0,0 +1,2 @@
+CREATE OR REPLACE FUNCTION dummy_display_ext_version()
+RETURNS text LANGUAGE SQL AS 'SELECT (''3.0-1'')::text';
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/files/dummy--3.0-foo.sql b/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/files/dummy--3.0-foo.sql
new file mode 100644
index 000000000..659e2113b
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/files/dummy--3.0-foo.sql
@@ -0,0 +1,2 @@
+CREATE OR REPLACE FUNCTION dummy_display_ext_version()
+RETURNS text LANGUAGE SQL AS 'SELECT (''3.0-foo'')::text';
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/files/dummy--3.0.sql b/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/files/dummy--3.0.sql
new file mode 100644
index 000000000..7d6a60e54
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/files/dummy--3.0.sql
@@ -0,0 +1,2 @@
+CREATE OR REPLACE FUNCTION dummy_display_ext_version()
+RETURNS text LANGUAGE SQL AS 'SELECT (''3.0'')::text';
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/files/dummy--3.beta.sql b/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/files/dummy--3.beta.sql
new file mode 100644
index 000000000..0e945a1d0
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/files/dummy--3.beta.sql
@@ -0,0 +1,2 @@
+CREATE OR REPLACE FUNCTION dummy_display_ext_version()
+RETURNS text LANGUAGE SQL AS 'SELECT (''3.beta'')::text';
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/files/dummy--4.0.sql b/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/files/dummy--4.0.sql
new file mode 100644
index 000000000..33aaef568
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/files/dummy--4.0.sql
@@ -0,0 +1,2 @@
+CREATE OR REPLACE FUNCTION dummy_display_ext_version()
+RETURNS text LANGUAGE SQL AS 'SELECT (''4.0'')::text';
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/files/dummy--v4.sql b/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/files/dummy--v4.sql
new file mode 100644
index 000000000..bb966db98
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/files/dummy--v4.sql
@@ -0,0 +1,2 @@
+CREATE OR REPLACE FUNCTION dummy_display_ext_version()
+RETURNS text LANGUAGE SQL AS 'SELECT (''v4'')::text';
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/files/dummy.control b/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/files/dummy.control
new file mode 100644
index 000000000..fda97bc58
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/files/dummy.control
@@ -0,0 +1,3 @@
+comment = 'dummy extension used to test postgresql_ext Ansible module'
+default_version = '4.0'
+relocatable = true
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/files/pg_hba.conf b/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/files/pg_hba.conf
new file mode 100644
index 000000000..58de3607f
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/files/pg_hba.conf
@@ -0,0 +1,10 @@
+# !!! This file managed by Ansible. Any local changes may be overwritten. !!!
+
+# Database administrative login by UNIX sockets
+# note: you may wish to restrict this further later
+local all {{ pg_user }} trust
+
+# TYPE DATABASE USER CIDR-ADDRESS METHOD
+local all all md5
+host all all 127.0.0.1/32 md5
+host all all ::1/128 md5
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/meta/main.yml b/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/meta/main.yml
new file mode 100644
index 000000000..5438ced5c
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/meta/main.yml
@@ -0,0 +1,2 @@
+dependencies:
+ - setup_pkg_mgr
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/tasks/main.yml b/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/tasks/main.yml
new file mode 100644
index 000000000..80bb3c4d4
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/tasks/main.yml
@@ -0,0 +1,279 @@
+####################################################################
+# WARNING: These are designed specifically for Ansible tests #
+# and should not be used as examples of how to write Ansible roles #
+####################################################################
+
+- name: python 2
+ set_fact:
+ python_suffix: ''
+ when: ansible_python_version is version('3', '<')
+
+- name: python 3
+ set_fact:
+ python_suffix: -py3
+ when: ansible_python_version is version('3', '>=')
+
+- name: Include distribution and Python version specific variables
+ include_vars: '{{ lookup(''first_found'', params) }}'
+ vars:
+ params:
+ files:
+ - '{{ ansible_distribution }}-{{ ansible_distribution_major_version }}{{ python_suffix }}.yml'
+ - '{{ ansible_distribution }}-{{ ansible_distribution_version }}{{ python_suffix }}.yml'
+ - '{{ ansible_os_family }}{{ python_suffix }}.yml'
+ - default{{ python_suffix }}.yml
+ paths:
+ - '{{ role_path }}/vars'
+
+- name: Make sure the dbus service is enabled under systemd
+ shell: systemctl enable dbus || systemctl enable dbus-broker
+ ignore_errors: true
+ when: ansible_service_mgr == 'systemd' and ansible_distribution == 'Fedora'
+
+- name: Make sure the dbus service is started under systemd
+ systemd:
+ name: dbus
+ state: started
+ when: ansible_service_mgr == 'systemd' and ansible_distribution == 'Fedora'
+
+- name: Kill all postgres processes
+ shell: 'pkill -u {{ pg_user }}'
+ become: true
+ when: ansible_facts.distribution == 'CentOS' and ansible_facts.distribution_major_version == '8'
+ ignore_errors: true
+
+- name: stop postgresql service
+ service: name={{ postgresql_service }} state=stopped
+ ignore_errors: true
+
+- name: remove old db (RedHat)
+ file:
+ path: '{{ pg_dir }}'
+ state: absent
+ ignore_errors: true
+ when: ansible_os_family == "RedHat"
+
+- name: remove old db config and files (debian)
+ file:
+ path: '{{ loop_item }}'
+ state: absent
+ ignore_errors: true
+ when: ansible_os_family == "Debian"
+ loop:
+ - /etc/postgresql
+ - /var/lib/postgresql
+ loop_control:
+ loop_var: loop_item
+
+#
+# Install PostgreSQL 15 on Ubuntu 20.04
+- name: Install PostgreSQL 15 on Ubuntu 20.04
+ when:
+ - ansible_facts.distribution == 'Ubuntu'
+ - ansible_facts.distribution_major_version == '20'
+ block:
+ - name: Run autoremove
+ become: true
+ apt:
+ autoremove: true
+
+ - name: Install wget
+ package:
+ name: wget
+
+ - name: Create the file repository configuration
+ lineinfile:
+ create: true
+ line: "deb http://apt.postgresql.org/pub/repos/apt {{ ansible_facts['distribution_release'] }}-pgdg main"
+ path: '/etc/apt/sources.list.d/pgdg.list'
+ state: 'present'
+
+ - name: Import the repository signing key
+ ansible.builtin.apt_key:
+ state: present
+ url: https://www.postgresql.org/media/keys/ACCC4CF8.asc
+
+ - name: Update the package lists
+ apt:
+ update_cache: true
+
+ - name: Install locale needed
+ shell: 'locale-gen {{ item }}'
+ loop:
+ - es_ES
+ - pt_BR
+
+ - name: Update locale
+ shell: 'update-locale'
+##
+#
+
+- name: install dependencies for postgresql test
+ package:
+ name: '{{ postgresql_package_item }}'
+ state: present
+ with_items: '{{ postgresql_packages }}'
+ loop_control:
+ loop_var: postgresql_package_item
+
+- name: Initialize postgres (RedHat systemd)
+ command: postgresql-setup initdb
+ when: ansible_os_family == "RedHat" and ansible_service_mgr == "systemd"
+
+- name: Initialize postgres (RedHat sysv)
+ command: /sbin/service postgresql initdb
+ when: ansible_os_family == "RedHat" and ansible_service_mgr != "systemd"
+
+- name: Initialize postgres (Debian)
+ shell: . /usr/share/postgresql-common/maintscripts-functions && set_system_locale && /usr/bin/pg_createcluster -u postgres {{ pg_ver }} main
+ args:
+ creates: /etc/postgresql/{{ pg_ver }}/
+ when: ansible_os_family == 'Debian'
+
+- name: Copy pg_hba into place
+ template:
+ src: files/pg_hba.conf
+ dest: '{{ pg_hba_location }}'
+ owner: '{{ pg_user }}'
+ group: '{{ pg_group }}'
+ mode: '0644'
+
+- name: Generate locales (Debian)
+ locale_gen:
+ name: '{{ item }}'
+ state: present
+ with_items:
+ - pt_BR
+ - es_ES
+ when: ansible_os_family == 'Debian'
+
+- block:
+ - name: Install langpacks (RHEL8)
+ yum:
+ name:
+ - glibc-langpack-es
+ - glibc-langpack-pt
+ - glibc-all-langpacks
+ state: present
+ when: ansible_distribution_major_version is version('8', '>=')
+
+ - name: Check if locales need to be generated (RedHat)
+ shell: localedef --list-archive | grep -a -q '^{{ locale }}$'
+ register: locale_present
+ ignore_errors: true
+ with_items:
+ - es_ES
+ - pt_BR
+ loop_control:
+ loop_var: locale
+
+ - block:
+ - name: Reinstall internationalization files
+ command: yum -y reinstall glibc-common
+ rescue:
+ - name: Install internationalization files
+ yum:
+ name: glibc-common
+ state: present
+ when: locale_present is failed
+
+ - name: Generate locale (RedHat)
+ command: localedef -f ISO-8859-1 -i {{ item.locale }} {{ item.locale }}
+ when: item is failed
+ with_items: '{{ locale_present.results }}'
+ when: ansible_os_family == 'RedHat' and ansible_distribution != 'Fedora'
+
+- name: Install glibc langpacks (Fedora >= 24)
+ package:
+ name: '{{ item }}'
+ state: latest
+ with_items:
+ - glibc-langpack-es
+ - glibc-langpack-pt
+ when: ansible_distribution == 'Fedora' and ansible_distribution_major_version is version('24', '>=')
+
+- name: start postgresql service
+ service: name={{ postgresql_service }} state=started
+
+- name: Pause between start and stop
+ pause:
+ seconds: 5
+
+- name: Kill all postgres processes
+ shell: 'pkill -u {{ pg_user }}'
+ become: true
+ when: ansible_facts.distribution == 'CentOS' and ansible_facts.distribution_major_version == '8'
+ ignore_errors: true
+ register: terminate
+
+- name: Stop postgresql service
+ service: name={{ postgresql_service }} state=stopped
+ when: terminate is not succeeded
+
+- name: Pause between stop and start
+ pause:
+ seconds: 5
+
+- name: Start postgresql service
+ service: name={{ postgresql_service }} state=started
+
+- name: copy control file for dummy ext
+ copy:
+ src: dummy.control
+ dest: /usr/share/postgresql/{{ pg_ver }}/extension/dummy.control
+ mode: '0444'
+ when: ansible_os_family == 'Debian'
+
+- name: copy version files for dummy ext
+ copy:
+ src: '{{ item }}'
+ dest: /usr/share/postgresql/{{ pg_ver }}/extension/{{ item }}
+ mode: '0444'
+ with_items:
+ - dummy--0.sql
+ - dummy--1.0.sql
+ - dummy--2.0.sql
+ - dummy--3.0.sql
+ - dummy--3.0-1.sql
+ - dummy--3.0-foo.sql
+ - dummy--3.beta.sql
+ - dummy--3-1.0.sql
+ - dummy--3-1.0-1.sql
+ - dummy--3-1.foo.sql
+ - dummy--v4.sql
+ - dummy--4.0.sql
+ when: ansible_os_family == 'Debian'
+
+- name: add update paths
+ file:
+ path: /usr/share/postgresql/{{ pg_ver }}/extension/{{ item }}
+ mode: '0444'
+ state: touch
+ with_items:
+ - dummy--0--1.0.sql
+ - dummy--1.0--2.0.sql
+ - dummy--2.0--3.0.sql
+ - dummy--3.0--3.0-1.sql
+ - dummy--3.0-1--3.0-foo.sql
+ - dummy--3.0-foo--3.beta.sql
+ - dummy--3.beta--3-1.0.sql
+ - dummy--3-1.0--3-1.0-1.sql
+ - dummy--3-1.0-1--3-1.foo.sql
+ - dummy--3-1.foo--v4.sql
+ - dummy--v4--4.0.sql
+ when: ansible_os_family == 'Debian'
+
+- name: Get PostgreSQL version
+ become_user: '{{ pg_user }}'
+ become: true
+ shell: echo 'SHOW SERVER_VERSION' | psql --tuples-only --no-align --dbname postgres
+ register: postgres_version_resp
+
+- name: Print PostgreSQL server version
+ debug:
+ msg: '{{ postgres_version_resp.stdout }}'
+
+- import_tasks: ssl.yml
+ when:
+ - ansible_os_family == 'Debian'
+ - postgres_version_resp.stdout is version('9.4', '>=')
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/tasks/ssl.yml b/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/tasks/ssl.yml
new file mode 100644
index 000000000..9f53e80ea
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/tasks/ssl.yml
@@ -0,0 +1,108 @@
+- name: postgresql SSL - create database
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_db:
+ name: '{{ ssl_db }}'
+
+- name: postgresql SSL - create role
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_user:
+ name: '{{ ssl_user }}'
+ role_attr_flags: SUPERUSER
+ password: '{{ ssl_pass }}'
+
+- name: postgresql SSL - install openssl
+ become: true
+ package: name=openssl state=present
+
+- name: postgresql SSL - create certs 1 (Make a self-signed server CA)
+ become_user: root
+ become: true
+ shell: openssl req -sha256 -new -x509 -days 365 -nodes -out /etc/server-ca.crt -keyout /etc/server-ca.key -subj "/CN="
+
+- name: postgresql SSL - create certs 2 (Generate server CSR)
+ become_user: root
+ become: true
+ shell: openssl req -sha256 -new -nodes -subj "/CN=127.0.0.1" -out /etc/server.csr -keyout /etc/server.key
+
+- name: postgresql SSL - create certs 3 (Sign a server certificate)
+ become_user: root
+ become: true
+ shell: openssl x509 -req -sha256 -days 365 -in /etc/server.csr -CA /etc/server-ca.crt -CAkey /etc/server-ca.key -CAcreateserial -out /etc/server.crt
+
+- name: postgresql SSL - create certs 4 (Make a self-signed client CA)
+ become_user: root
+ become: true
+ shell: openssl req -sha256 -new -x509 -days 365 -nodes -out /etc/client-ca.crt -keyout /etc/client-ca.key -subj "/CN="
+
+- name: postgresql SSL - create certs 5 (Generate client CSR)
+ become_user: root
+ become: true
+ shell: openssl req -sha256 -new -nodes -subj "/CN={{ ssl_user }}" -out /etc/client.csr -keyout /etc/client.key
+
+- name: postgresql SSL - create certs 6 (Sign a client certificate)
+ become_user: root
+ become: true
+ shell: openssl x509 -req -sha256 -days 365 -in /etc/client.csr -CA /etc/client-ca.crt -CAkey /etc/client-ca.key -CAcreateserial -out /etc/client.crt
+
+- name: postgresql SSL - set right permissions to files
+ become_user: root
+ become: true
+ file:
+ path: '{{ item }}'
+ mode: '0600'
+ owner: '{{ pg_user }}'
+ group: '{{ pg_user }}'
+ with_items:
+ - /etc/server.key
+ - /etc/server.crt
+ - /etc/server.csr
+ - /etc/client.csr
+ - /etc/client.key
+ - /etc/client-ca.crt
+ - /etc/client-ca.key
+ - /etc/server-ca.key
+ - /etc/server-ca.crt
+
+- name: postgresql SSL - enable SSL
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_set:
+ login_user: '{{ pg_user }}'
+ db: postgres
+ name: ssl
+ value: true
+
+- name: postgresql SSL - add ssl_cert_file
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_set:
+ login_user: '{{ pg_user }}'
+ db: postgres
+ name: ssl_cert_file
+ value: /etc/server.crt
+
+- name: postgresql SSL - add ssl_key_file
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_set:
+ login_user: '{{ pg_user }}'
+ db: postgres
+ name: ssl_key_file
+ value: /etc/server.key
+
+- name: postgresql SSL - add ssl_ca_file
+ become_user: '{{ pg_user }}'
+ become: true
+ postgresql_set:
+ login_user: '{{ pg_user }}'
+ db: postgres
+ name: ssl_ca_file
+ value: /etc/client-ca.crt
+
+- name: postgresql SSL - reload PostgreSQL to enable ssl on
+ become: true
+ service:
+ name: '{{ postgresql_service }}'
+ state: reloaded
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/vars/Debian-8.yml b/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/vars/Debian-8.yml
new file mode 100644
index 000000000..932738d39
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/vars/Debian-8.yml
@@ -0,0 +1,9 @@
+postgresql_packages:
+ - "postgresql"
+ - "postgresql-common"
+ - "python-psycopg2"
+
+pg_hba_location: "/etc/postgresql/9.4/main/pg_hba.conf"
+pg_dir: "/var/lib/postgresql/9.4/main"
+pg_auto_conf: "{{ pg_dir }}/postgresql.auto.conf"
+pg_ver: 9.4
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/vars/RedHat-py3.yml b/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/vars/RedHat-py3.yml
new file mode 100644
index 000000000..72041a3d7
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/vars/RedHat-py3.yml
@@ -0,0 +1,9 @@
+postgresql_packages:
+ - "postgresql-server"
+ - "python3-psycopg2"
+ - "bzip2"
+ - "xz"
+
+pg_hba_location: "/var/lib/pgsql/data/pg_hba.conf"
+pg_dir: "/var/lib/pgsql/data"
+pg_auto_conf: "{{ pg_dir }}/postgresql.auto.conf"
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/vars/RedHat.yml b/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/vars/RedHat.yml
new file mode 100644
index 000000000..30720f8fe
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/vars/RedHat.yml
@@ -0,0 +1,8 @@
+postgresql_packages:
+ - "postgresql-server"
+ - "python-psycopg2"
+ - "bzip2"
+
+pg_hba_location: "/var/lib/pgsql/data/pg_hba.conf"
+pg_dir: "/var/lib/pgsql/data"
+pg_auto_conf: "{{ pg_dir }}/postgresql.auto.conf"
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/vars/Ubuntu-20-py3.yml b/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/vars/Ubuntu-20-py3.yml
new file mode 100644
index 000000000..ff543a385
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/vars/Ubuntu-20-py3.yml
@@ -0,0 +1,13 @@
+postgresql_packages:
+ - "apt-utils"
+ - "postgresql"
+ - "postgresql-common"
+ - "python3-psycopg2"
+ - "postgresql-client"
+
+pg_hba_location: "/etc/postgresql/15/main/pg_hba.conf"
+pg_dir: "/var/lib/postgresql/15/main"
+pg_auto_conf: "{{ pg_dir }}/postgresql.auto.conf"
+pg_ver: 15
+
+postgis: postgresql-15-postgis-3
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/vars/default-py3.yml b/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/vars/default-py3.yml
new file mode 100644
index 000000000..3ff3e0de5
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/vars/default-py3.yml
@@ -0,0 +1,7 @@
+postgresql_packages:
+ - "postgresql-server"
+ - "python3-psycopg2"
+
+pg_hba_location: "/var/lib/pgsql/data/pg_hba.conf"
+pg_dir: "/var/lib/pgsql/data"
+pg_auto_conf: "{{ pg_dir }}/postgresql.auto.conf"
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/vars/default.yml b/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/vars/default.yml
new file mode 100644
index 000000000..71f1cd46e
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_db/vars/default.yml
@@ -0,0 +1,7 @@
+postgresql_packages:
+ - "postgresql-server"
+ - "python-psycopg2"
+
+pg_hba_location: "/var/lib/pgsql/data/pg_hba.conf"
+pg_dir: "/var/lib/pgsql/data"
+pg_auto_conf: "{{ pg_dir }}/postgresql.auto.conf"
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_replication/defaults/main.yml b/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_replication/defaults/main.yml
new file mode 100644
index 000000000..5ac314c4b
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_replication/defaults/main.yml
@@ -0,0 +1,26 @@
+# General:
+pg_user: postgres
+db_default: postgres
+
+pg_package_list:
+- postgresql
+- postgresql-client
+- python3-psycopg2
+
+packages_to_remove:
+- postgresql
+- postgresql-client
+
+# Master specific defaults:
+primary_root_dir: '/var/lib/pgsql/primary'
+primary_data_dir: '{{ primary_root_dir }}/data'
+primary_postgresql_conf: '{{ primary_data_dir }}/postgresql.conf'
+primary_pg_hba_conf: '{{ primary_data_dir }}/pg_hba.conf'
+primary_port: 5431
+
+# Replica specific defaults:
+replica_root_dir: '/var/lib/pgsql/replica'
+replica_data_dir: '{{ replica_root_dir }}/data'
+replica_postgresql_conf: '{{ replica_data_dir }}/postgresql.conf'
+replica_pg_hba_conf: '{{ replica_data_dir }}/pg_hba.conf'
+replica_port: 5434
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_replication/handlers/main.yml b/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_replication/handlers/main.yml
new file mode 100644
index 000000000..ea230c778
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_replication/handlers/main.yml
@@ -0,0 +1,24 @@
+- name: Stop services
+ become: true
+ become_user: '{{ pg_user }}'
+ shell: '{{ pg_ctl }} -D {{ item.datadir }} -o "-p {{ item.port }}" -m immediate stop'
+ loop:
+ - { datadir: '{{ primary_data_dir }}', port: '{{ primary_port }}' }
+ - { datadir: '{{ replica_data_dir }}', port: '{{ replica_port }}' }
+ listen: stop postgresql
+
+- name: Remove packages
+ apt:
+ name: '{{ packages_to_remove }}'
+ state: absent
+ listen: cleanup postgresql
+
+- name: Remove FS objects
+ file:
+ state: absent
+ path: "{{ item }}"
+ force: true
+ loop:
+ - "{{ primary_root_dir }}"
+ - "{{ replica_root_dir }}"
+ listen: cleanup postgresql
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_replication/tasks/main.yml b/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_replication/tasks/main.yml
new file mode 100644
index 000000000..4c6421a18
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_replication/tasks/main.yml
@@ -0,0 +1,13 @@
+####################################################################
+# WARNING: These are designed specifically for Ansible tests #
+# and should not be used as examples of how to write Ansible roles #
+####################################################################
+
+# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) <aaklychkov@mail.ru>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+# Setup PostgreSQL primary-standby replication into one container:
+- import_tasks: setup_postgresql_cluster.yml
+ when:
+ - ansible_distribution == 'Ubuntu'
+ - ansible_distribution_major_version >= '18'
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_replication/tasks/setup_postgresql_cluster.yml b/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_replication/tasks/setup_postgresql_cluster.yml
new file mode 100644
index 000000000..2bff42e78
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_replication/tasks/setup_postgresql_cluster.yml
@@ -0,0 +1,149 @@
+- name: Remove preinstalled packages
+ apt:
+ name: '{{ packages_to_remove }}'
+ state: absent
+ become: true
+
+- name: Run autoremove
+ become: true
+ apt:
+ autoremove: true
+
+- name: Configure Ubuntu 20 for PostgreSQL
+ when:
+ - ansible_facts['distribution'] == 'Ubuntu'
+ - ansible_facts['distribution_major_version'] is version('20', 'ge')
+ block:
+ - name: Install wget
+ package:
+ name: wget
+
+ - name: Add PGDG repository
+ lineinfile:
+ create: true
+ line: "deb http://apt.postgresql.org/pub/repos/apt {{ ansible_facts['distribution_release'] }}-pgdg main"
+ path: '/etc/apt/sources.list.d/pgdg.list'
+ state: 'present'
+
+ - name: Add PGDG GPG key
+ ansible.builtin.apt_key:
+ state: present
+ url: https://www.postgresql.org/media/keys/ACCC4CF8.asc
+
+ - name: Update apt cache
+ apt:
+ update_cache: true
+
+- name: Install apt-utils
+ apt:
+ name: apt-utils
+
+- name: Install packages
+ apt:
+ name: '{{ pg_package_list }}'
+ policy_rc_d: 101 # prevent the service from starting
+ notify: cleanup postgresql
+
+- name: Delete postgresql related files
+ file:
+ state: absent
+ path: '{{ item }}'
+ force: true
+ loop:
+ - '{{ primary_root_dir }}'
+ - '{{ replica_root_dir }}'
+ - /etc/postgresql
+ - /var/lib/postgresql
+
+- name: Create dirs needed
+ file:
+ state: directory
+ recurse: true
+ path: '{{ item }}'
+ owner: postgres
+ group: postgres
+ mode: '0700'
+ loop:
+ - '{{ primary_data_dir }}'
+ - '{{ replica_data_dir }}'
+ - /var/lib/postgresql
+ notify: cleanup postgresql
+
+- name: Find initdb
+ shell: find /usr/lib -type f -name "initdb"
+ register: result
+
+- name: Set path to initdb
+ set_fact:
+ initdb: '{{ result.stdout }}'
+
+- name: Initialize databases
+ become: true
+ become_user: '{{ pg_user }}'
+ shell: '{{ initdb }} --pgdata {{ item }}'
+ loop:
+ - '{{ primary_data_dir }}'
+ - '{{ replica_data_dir }}'
+
+- name: Copy config templates
+ template:
+ src: '{{ item.conf_templ }}'
+ dest: '{{ item.conf_dest }}'
+ owner: postgres
+ group: postgres
+ force: true
+ loop:
+ - conf_templ: primary_postgresql.conf.j2
+ conf_dest: '{{ primary_postgresql_conf }}'
+ - conf_templ: replica_postgresql.conf.j2
+ conf_dest: '{{ replica_postgresql_conf }}'
+ - conf_templ: pg_hba.conf.j2
+ conf_dest: '{{ primary_pg_hba_conf }}'
+ - conf_templ: pg_hba.conf.j2
+ conf_dest: '{{ replica_pg_hba_conf }}'
+
+- name: Find pg_ctl
+ shell: find /usr/lib -type f -name "pg_ctl"
+ register: result
+
+- name: Set path to initdb
+ set_fact:
+ pg_ctl: '{{ result.stdout }}'
+
+- name: Start primary
+ become: true
+ become_user: '{{ pg_user }}'
+ shell: '{{ pg_ctl }} -D {{ primary_data_dir }} -o "-p {{ primary_port }}" -l {{ primary_data_dir }}/primary.log start'
+ notify:
+ - stop postgresql
+
+- name: Start replica
+ become: true
+ become_user: '{{ pg_user }}'
+ shell: '{{ pg_ctl }} -D {{ replica_data_dir }} -o "-p {{ replica_port }}" -l {{ replica_data_dir }}/replica.log start'
+
+- name: Check connectivity to the primary and get PostgreSQL version
+ become: true
+ become_user: '{{ pg_user }}'
+ postgresql_ping:
+ db: '{{ db_default }}'
+ login_user: '{{ pg_user }}'
+ login_port: '{{ primary_port }}'
+ register: result
+
+- name: Check connectivity to the replica and get PostgreSQL version
+ become: true
+ become_user: '{{ pg_user }}'
+ postgresql_ping:
+ db: '{{ db_default }}'
+ login_user: '{{ pg_user }}'
+ login_port: '{{ replica_port }}'
+
+- name: Define server version
+ set_fact:
+ pg_major_version: '{{ result.server_version.major }}'
+ pg_minor_version: '{{ result.server_version.minor }}'
+
+- name: Print PostgreSQL version
+ debug:
+ msg: PostgreSQL version is {{ pg_major_version }}.{{ pg_minor_version }}
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_replication/templates/pg_hba.conf.j2 b/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_replication/templates/pg_hba.conf.j2
new file mode 100644
index 000000000..62e05ffc8
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_replication/templates/pg_hba.conf.j2
@@ -0,0 +1,7 @@
+local all all trust
+local replication logical_replication trust
+host replication logical_replication 127.0.0.1/32 trust
+host replication logical_replication 0.0.0.0/0 trust
+local all logical_replication trust
+host all logical_replication 127.0.0.1/32 trust
+host all logical_replication 0.0.0.0/0 trust
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_replication/templates/primary_postgresql.conf.j2 b/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_replication/templates/primary_postgresql.conf.j2
new file mode 100644
index 000000000..545769f35
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_replication/templates/primary_postgresql.conf.j2
@@ -0,0 +1,28 @@
+# Important parameters:
+listen_addresses='*'
+port = {{ primary_port }}
+wal_level = logical
+max_wal_senders = 8
+track_commit_timestamp = on
+max_replication_slots = 10
+
+# Unimportant parameters:
+max_connections=10
+shared_buffers=8MB
+dynamic_shared_memory_type=posix
+log_destination='stderr'
+logging_collector=on
+log_directory='log'
+log_filename='postgresql-%a.log'
+log_truncate_on_rotation=on
+log_rotation_age=1d
+log_rotation_size=0
+log_line_prefix='%m[%p]'
+log_timezone='W-SU'
+datestyle='iso,mdy'
+timezone='W-SU'
+lc_messages='en_US.UTF-8'
+lc_monetary='en_US.UTF-8'
+lc_numeric='en_US.UTF-8'
+lc_time='en_US.UTF-8'
+default_text_search_config='pg_catalog.english'
diff --git a/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_replication/templates/replica_postgresql.conf.j2 b/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_replication/templates/replica_postgresql.conf.j2
new file mode 100644
index 000000000..206ab2eb3
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/integration/targets/setup_postgresql_replication/templates/replica_postgresql.conf.j2
@@ -0,0 +1,28 @@
+# Important parameters:
+listen_addresses='*'
+port = {{ replica_port }}
+wal_level = logical
+max_wal_senders = 8
+track_commit_timestamp = on
+max_replication_slots = 10
+
+# Unimportant parameters:
+max_connections=10
+shared_buffers=8MB
+dynamic_shared_memory_type=posix
+log_destination='stderr'
+logging_collector=on
+log_directory='log'
+log_filename='postgresql-%a.log'
+log_truncate_on_rotation=on
+log_rotation_age=1d
+log_rotation_size=0
+log_line_prefix='%m[%p]'
+log_timezone='W-SU'
+datestyle='iso,mdy'
+timezone='W-SU'
+lc_messages='en_US.UTF-8'
+lc_monetary='en_US.UTF-8'
+lc_numeric='en_US.UTF-8'
+lc_time='en_US.UTF-8'
+default_text_search_config='pg_catalog.english'
diff --git a/ansible_collections/community/postgresql/tests/requirements.yml b/ansible_collections/community/postgresql/tests/requirements.yml
new file mode 100644
index 000000000..5a2c9c805
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/requirements.yml
@@ -0,0 +1,3 @@
+integration_tests_dependencies:
+- community.general
+unit_tests_dependencies: []
diff --git a/ansible_collections/community/postgresql/tests/sanity/extra/no-unwanted-files.json b/ansible_collections/community/postgresql/tests/sanity/extra/no-unwanted-files.json
new file mode 100644
index 000000000..c789a7fd3
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/sanity/extra/no-unwanted-files.json
@@ -0,0 +1,7 @@
+{
+ "include_symlinks": true,
+ "prefixes": [
+ "plugins/"
+ ],
+ "output": "path-message"
+}
diff --git a/ansible_collections/community/postgresql/tests/sanity/extra/no-unwanted-files.py b/ansible_collections/community/postgresql/tests/sanity/extra/no-unwanted-files.py
new file mode 100755
index 000000000..49806f2e2
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/sanity/extra/no-unwanted-files.py
@@ -0,0 +1,43 @@
+#!/usr/bin/env python
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+"""Prevent unwanted files from being added to the source tree."""
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+import os
+import sys
+
+
+def main():
+ """Main entry point."""
+ paths = sys.argv[1:] or sys.stdin.read().splitlines()
+
+ allowed_extensions = (
+ '.cs',
+ '.ps1',
+ '.psm1',
+ '.py',
+ )
+
+ skip_paths = set([
+ ])
+
+ skip_directories = (
+ )
+
+ for path in paths:
+ if path in skip_paths:
+ continue
+
+ if any(path.startswith(skip_directory) for skip_directory in skip_directories):
+ continue
+
+ ext = os.path.splitext(path)[1]
+
+ if ext not in allowed_extensions:
+ print('%s: extension must be one of: %s' % (path, ', '.join(allowed_extensions)))
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/community/postgresql/tests/sanity/ignore-2.12.txt b/ansible_collections/community/postgresql/tests/sanity/ignore-2.12.txt
new file mode 100644
index 000000000..b9cd1303f
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/sanity/ignore-2.12.txt
@@ -0,0 +1,5 @@
+tests/utils/shippable/check_matrix.py replace-urlopen
+tests/utils/shippable/timing.py shebang
+plugins/modules/postgresql_db.py use-argspec-type-path
+plugins/modules/postgresql_db.py validate-modules:use-run-command-not-popen
+plugins/modules/postgresql_tablespace.py validate-modules:mutually_exclusive-unknown
diff --git a/ansible_collections/community/postgresql/tests/sanity/ignore-2.13.txt b/ansible_collections/community/postgresql/tests/sanity/ignore-2.13.txt
new file mode 100644
index 000000000..b9cd1303f
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/sanity/ignore-2.13.txt
@@ -0,0 +1,5 @@
+tests/utils/shippable/check_matrix.py replace-urlopen
+tests/utils/shippable/timing.py shebang
+plugins/modules/postgresql_db.py use-argspec-type-path
+plugins/modules/postgresql_db.py validate-modules:use-run-command-not-popen
+plugins/modules/postgresql_tablespace.py validate-modules:mutually_exclusive-unknown
diff --git a/ansible_collections/community/postgresql/tests/sanity/ignore-2.14.txt b/ansible_collections/community/postgresql/tests/sanity/ignore-2.14.txt
new file mode 100644
index 000000000..b9cd1303f
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/sanity/ignore-2.14.txt
@@ -0,0 +1,5 @@
+tests/utils/shippable/check_matrix.py replace-urlopen
+tests/utils/shippable/timing.py shebang
+plugins/modules/postgresql_db.py use-argspec-type-path
+plugins/modules/postgresql_db.py validate-modules:use-run-command-not-popen
+plugins/modules/postgresql_tablespace.py validate-modules:mutually_exclusive-unknown
diff --git a/ansible_collections/community/postgresql/tests/sanity/ignore-2.15.txt b/ansible_collections/community/postgresql/tests/sanity/ignore-2.15.txt
new file mode 100644
index 000000000..58b57c247
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/sanity/ignore-2.15.txt
@@ -0,0 +1,6 @@
+tests/utils/shippable/check_matrix.py replace-urlopen
+tests/utils/shippable/timing.py shebang
+plugins/modules/postgresql_db.py use-argspec-type-path
+plugins/modules/postgresql_db.py validate-modules:use-run-command-not-popen
+plugins/modules/postgresql_tablespace.py validate-modules:mutually_exclusive-unknown
+plugins/module_utils/version.py pylint:unused-import
diff --git a/ansible_collections/community/postgresql/tests/sanity/ignore-2.16.txt b/ansible_collections/community/postgresql/tests/sanity/ignore-2.16.txt
new file mode 100644
index 000000000..58b57c247
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/sanity/ignore-2.16.txt
@@ -0,0 +1,6 @@
+tests/utils/shippable/check_matrix.py replace-urlopen
+tests/utils/shippable/timing.py shebang
+plugins/modules/postgresql_db.py use-argspec-type-path
+plugins/modules/postgresql_db.py validate-modules:use-run-command-not-popen
+plugins/modules/postgresql_tablespace.py validate-modules:mutually_exclusive-unknown
+plugins/module_utils/version.py pylint:unused-import
diff --git a/ansible_collections/community/postgresql/tests/unit/plugins/module_utils/__init__.py b/ansible_collections/community/postgresql/tests/unit/plugins/module_utils/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/unit/plugins/module_utils/__init__.py
diff --git a/ansible_collections/community/postgresql/tests/unit/plugins/module_utils/test_postgres.py b/ansible_collections/community/postgresql/tests/unit/plugins/module_utils/test_postgres.py
new file mode 100644
index 000000000..975542446
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/unit/plugins/module_utils/test_postgres.py
@@ -0,0 +1,338 @@
+# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) <aaklychkov@mail.ru>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+from os import environ
+
+import pytest
+
+import ansible_collections.community.postgresql.plugins.module_utils.postgres as pg
+
+
+INPUT_DICT = dict(
+ session_role=dict(default=''),
+ login_user=dict(default='postgres'),
+ login_password=dict(default='test', no_log=True),
+ login_host=dict(default='test'),
+ login_unix_socket=dict(default=''),
+ port=dict(type='int', default=5432, aliases=['login_port']),
+ ssl_mode=dict(
+ default='prefer',
+ choices=['allow', 'disable', 'prefer', 'require', 'verify-ca', 'verify-full']
+ ),
+ ca_cert=dict(aliases=['ssl_rootcert']),
+ ssl_cert=dict(type='path'),
+ ssl_key=dict(type='path'),
+)
+
+EXPECTED_DICT = dict(
+ user=dict(default='postgres'),
+ password=dict(default='test', no_log=True),
+ host=dict(default='test'),
+ port=dict(type='int', default=5432, aliases=['login_port']),
+ sslmode=dict(
+ default='prefer',
+ choices=['allow', 'disable', 'prefer', 'require', 'verify-ca', 'verify-full']
+ ),
+ sslrootcert=dict(aliases=['ssl_rootcert']),
+ sslcert=dict(type='path'),
+ sslkey=dict(type='path'),
+)
+
+
+class TestPostgresCommonArgSpec():
+
+ """
+ Namespace for testing postgresql_common_arg_spec() function.
+ """
+
+ def test_postgres_common_argument_spec(self):
+ """
+ Test for postgresql_common_arg_spec() function.
+
+ The tested function just returns a dictionary with the default
+ parameters and their values for PostgreSQL modules.
+ The return and expected dictionaries must be compared.
+ """
+ expected_dict = dict(
+ login_user=dict(default='postgres', aliases=['login']),
+ login_password=dict(default='', no_log=True),
+ login_host=dict(default='', aliases=['host']),
+ login_unix_socket=dict(default='', aliases=['unix_socket']),
+ port=dict(type='int', default=5432, aliases=['login_port']),
+ ssl_mode=dict(
+ default='prefer',
+ choices=['allow', 'disable', 'prefer', 'require', 'verify-ca', 'verify-full']
+ ),
+ ca_cert=dict(aliases=['ssl_rootcert']),
+ ssl_cert=dict(type='path'),
+ ssl_key=dict(type='path'),
+ connect_params=dict(default={}, type='dict'),
+ )
+ assert pg.postgres_common_argument_spec() == expected_dict
+
+ # Setting new values for checking environment variables
+ expected_dict['port']['default'] = 5435
+ expected_dict['login_user']['default'] = 'test_user'
+
+ # Setting environment variables
+ environ['PGUSER'] = 'test_user'
+ environ['PGPORT'] = '5435'
+ assert pg.postgres_common_argument_spec() == expected_dict
+
+
+@pytest.fixture
+def m_psycopg2():
+ """Return mock object for psycopg2 emulation."""
+ global Cursor
+ Cursor = None
+
+ class Cursor():
+ def __init__(self):
+ self.passed_query = None
+
+ def execute(self, query):
+ self.passed_query = query
+
+ def close(self):
+ pass
+
+ global DbConnection
+ DbConnection = None
+
+ class DbConnection():
+ def __init__(self):
+ pass
+
+ def cursor(self, cursor_factory=None):
+ return Cursor()
+
+ def set_session(self, autocommit=None):
+ pass
+
+ def set_isolation_level(self, isolevel):
+ pass
+
+ class Extras():
+ def __init__(self):
+ self.DictCursor = True
+
+ class Extensions():
+ def __init__(self):
+ self.ISOLATION_LEVEL_AUTOCOMMIT = True
+
+ class DummyPsycopg2():
+ def __init__(self):
+ self.__version__ = '2.4.3'
+ self.extras = Extras()
+ self.extensions = Extensions()
+
+ def connect(self, host=None, port=None, user=None,
+ password=None, sslmode=None, sslrootcert=None, connect_params=None):
+ if user == 'Exception':
+ raise Exception()
+
+ return DbConnection()
+
+ return DummyPsycopg2()
+
+
+class TestEnsureReqLibs():
+
+ """
+ Namespace for testing ensure_required_libs() function.
+
+ If there is something wrong with libs, the function invokes fail_json()
+ method of AnsibleModule object passed as an argument called 'module'.
+ Therefore we must check:
+ 1. value of err_msg attribute of m_ansible_module mock object.
+ """
+
+ @pytest.fixture(scope='class')
+ def m_ansible_module(self):
+ """Return an object of dummy AnsibleModule class."""
+ class Dummym_ansible_module():
+ def __init__(self):
+ self.params = {'ca_cert': False}
+ self.err_msg = ''
+
+ def fail_json(self, msg):
+ self.err_msg = msg
+
+ return Dummym_ansible_module()
+
+ def test_ensure_req_libs_has_not_psycopg2(self, m_ansible_module):
+ """Test ensure_required_libs() with psycopg2 is None."""
+ # HAS_PSYCOPG2 is False by default
+ pg.ensure_required_libs(m_ansible_module)
+ assert 'Failed to import the required Python library (psycopg2)' in m_ansible_module.err_msg
+
+ def test_ensure_req_libs_has_psycopg2(self, m_ansible_module, monkeypatch):
+ """Test ensure_required_libs() with psycopg2 is not None."""
+ monkeypatch.setattr(pg, 'HAS_PSYCOPG2', True)
+
+ pg.ensure_required_libs(m_ansible_module)
+ assert m_ansible_module.err_msg == ''
+
+ def test_ensure_req_libs_ca_cert(self, m_ansible_module, m_psycopg2, monkeypatch):
+ """
+ Test with module.params['ca_cert'], psycopg2 version is suitable.
+ """
+ m_ansible_module.params['ca_cert'] = True
+ monkeypatch.setattr(pg, 'HAS_PSYCOPG2', True)
+ monkeypatch.setattr(pg, 'psycopg2', m_psycopg2)
+
+ pg.ensure_required_libs(m_ansible_module)
+ assert m_ansible_module.err_msg == ''
+
+ def test_ensure_req_libs_ca_cert_low_psycopg2_ver(self, m_ansible_module, m_psycopg2, monkeypatch):
+ """
+ Test with module.params['ca_cert'], psycopg2 version is wrong.
+ """
+ m_ansible_module.params['ca_cert'] = True
+ monkeypatch.setattr(pg, 'HAS_PSYCOPG2', True)
+ # Set wrong psycopg2 version number:
+ psycopg2 = m_psycopg2
+ psycopg2.__version__ = '2.4.2'
+ monkeypatch.setattr(pg, 'psycopg2', psycopg2)
+
+ pg.ensure_required_libs(m_ansible_module)
+ assert 'psycopg2 must be at least 2.4.3' in m_ansible_module.err_msg
+
+
+@pytest.fixture(scope='class')
+def m_ansible_module():
+ """Return an object of dummy AnsibleModule class."""
+ class DummyAnsibleModule():
+ def __init__(self):
+
+ # take default params from argument spec
+ spec = pg.postgres_common_argument_spec()
+ params = dict()
+ for k in spec.keys():
+ params[k] = spec[k].get('default')
+
+ self.params = params
+ self.err_msg = ''
+ self.warn_msg = ''
+
+ def fail_json(self, msg):
+ self.err_msg = msg
+
+ def warn(self, msg):
+ self.warn_msg = msg
+
+ return DummyAnsibleModule()
+
+
+class TestConnectToDb():
+
+ """
+ Namespace for testing connect_to_db() function.
+
+ When some connection errors occure connect_to_db() caught any of them
+ and invoke fail_json() or warn() methods of AnsibleModule object
+ depending on the passed parameters.
+ connect_to_db may return db_connection object or None if errors occured.
+ Therefore we must check:
+ 1. Values of err_msg and warn_msg attributes of m_ansible_module mock object.
+ 2. Types of return objects (db_connection and cursor).
+ """
+
+ def test_connect_to_db(self, m_ansible_module, monkeypatch, m_psycopg2):
+ """Test connect_to_db(), common test."""
+ monkeypatch.setattr(pg, 'HAS_PSYCOPG2', True)
+ monkeypatch.setattr(pg, 'psycopg2', m_psycopg2)
+
+ conn_params = pg.get_conn_params(m_ansible_module, m_ansible_module.params)
+ db_connection, dummy = pg.connect_to_db(m_ansible_module, conn_params)
+ cursor = db_connection.cursor()
+ # if errors, db_connection returned as None:
+ assert type(db_connection) == DbConnection
+ assert type(cursor) == Cursor
+ assert m_ansible_module.err_msg == ''
+ # The default behaviour, normal in this case:
+ assert 'Database name has not been passed' in m_ansible_module.warn_msg
+
+ def test_session_role(self, m_ansible_module, monkeypatch, m_psycopg2):
+ """Test connect_to_db(), switch on session_role."""
+ monkeypatch.setattr(pg, 'HAS_PSYCOPG2', True)
+ monkeypatch.setattr(pg, 'psycopg2', m_psycopg2)
+
+ m_ansible_module.params['session_role'] = 'test_role'
+ conn_params = pg.get_conn_params(m_ansible_module, m_ansible_module.params)
+ db_connection, dummy = pg.connect_to_db(m_ansible_module, conn_params)
+ cursor = db_connection.cursor()
+ # if errors, db_connection returned as None:
+ assert type(db_connection) == DbConnection
+ assert type(cursor) == Cursor
+ assert m_ansible_module.err_msg == ''
+ # The default behaviour, normal in this case:
+ assert 'Database name has not been passed' in m_ansible_module.warn_msg
+
+ def test_fail_on_conn_true(self, m_ansible_module, monkeypatch, m_psycopg2):
+ """
+ Test connect_to_db(), fail_on_conn arg passed as True (the default behavior).
+ """
+ monkeypatch.setattr(pg, 'HAS_PSYCOPG2', True)
+ monkeypatch.setattr(pg, 'psycopg2', m_psycopg2)
+
+ m_ansible_module.params['login_user'] = 'Exception' # causes Exception
+
+ conn_params = pg.get_conn_params(m_ansible_module, m_ansible_module.params)
+ db_connection, dummy = pg.connect_to_db(m_ansible_module, conn_params, fail_on_conn=True)
+
+ assert 'unable to connect to database' in m_ansible_module.err_msg
+ assert db_connection is None
+
+ def test_fail_on_conn_false(self, m_ansible_module, monkeypatch, m_psycopg2):
+ """
+ Test connect_to_db(), fail_on_conn arg passed as False.
+ """
+ monkeypatch.setattr(pg, 'HAS_PSYCOPG2', True)
+ monkeypatch.setattr(pg, 'psycopg2', m_psycopg2)
+
+ m_ansible_module.params['login_user'] = 'Exception' # causes Exception
+
+ conn_params = pg.get_conn_params(m_ansible_module, m_ansible_module.params)
+ db_connection, dummy = pg.connect_to_db(m_ansible_module, conn_params, fail_on_conn=False)
+
+ assert m_ansible_module.err_msg == ''
+ assert 'PostgreSQL server is unavailable' in m_ansible_module.warn_msg
+ assert db_connection is None
+
+ def test_autocommit_true(self, m_ansible_module, monkeypatch, m_psycopg2):
+ """
+ Test connect_to_db(), autocommit arg passed as True (the default is False).
+ """
+ monkeypatch.setattr(pg, 'HAS_PSYCOPG2', True)
+
+ # case 1: psycopg2.__version >= 2.4.2 (the default in m_psycopg2)
+ monkeypatch.setattr(pg, 'psycopg2', m_psycopg2)
+
+ conn_params = pg.get_conn_params(m_ansible_module, m_ansible_module.params)
+ db_connection, dummy = pg.connect_to_db(m_ansible_module, conn_params, autocommit=True)
+ cursor = db_connection.cursor()
+
+ # if errors, db_connection returned as None:
+ assert type(db_connection) == DbConnection
+ assert type(cursor) == Cursor
+ assert m_ansible_module.err_msg == ''
+
+
+class TestGetConnParams():
+
+ """Namespace for testing get_conn_params() function."""
+
+ def test_get_conn_params_def(self, m_ansible_module, m_psycopg2, monkeypatch):
+ """Test get_conn_params(), warn_db_default kwarg is default."""
+ monkeypatch.setattr(pg, 'psycopg2', m_psycopg2)
+ assert pg.get_conn_params(m_ansible_module, INPUT_DICT) == EXPECTED_DICT
+ assert m_ansible_module.warn_msg == 'Database name has not been passed, used default database to connect to.'
+
+ def test_get_conn_params_warn_db_def_false(self, m_ansible_module, m_psycopg2, monkeypatch):
+ """Test get_conn_params(), warn_db_default kwarg is False."""
+ monkeypatch.setattr(pg, 'psycopg2', m_psycopg2)
+ assert pg.get_conn_params(m_ansible_module, INPUT_DICT, warn_db_default=False) == EXPECTED_DICT
+ assert m_ansible_module.warn_msg == ''
diff --git a/ansible_collections/community/postgresql/tests/unit/plugins/module_utils/test_saslprep.py b/ansible_collections/community/postgresql/tests/unit/plugins/module_utils/test_saslprep.py
new file mode 100644
index 000000000..62a1704ad
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/unit/plugins/module_utils/test_saslprep.py
@@ -0,0 +1,55 @@
+# -*- coding: utf-8 -*-
+# Copyright: (c) 2019, Andrey Tuzhilin <andrei.tuzhilin@gmail.com>
+# Copyright: (c) 2020, Andrew Klychkov (@Andersson007) <aaklychkov@mail.ru>
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+import pytest
+
+from ansible_collections.community.postgresql.plugins.module_utils.saslprep import saslprep
+
+
+VALID = [
+ (u'', u''),
+ (u'\u00A0', u' '),
+ (u'a', u'a'),
+ (u'й', u'й'),
+ (u'\u30DE\u30C8\u30EA\u30C3\u30AF\u30B9', u'\u30DE\u30C8\u30EA\u30C3\u30AF\u30B9'),
+ (u'The\u00ADM\u00AAtr\u2168', u'TheMatrIX'),
+ (u'I\u00ADX', u'IX'),
+ (u'user', u'user'),
+ (u'USER', u'USER'),
+ (u'\u00AA', u'a'),
+ (u'\u2168', u'IX'),
+ (u'\u05BE\u00A0\u05BE', u'\u05BE\u0020\u05BE'),
+]
+
+INVALID = [
+ (None, TypeError),
+ (b'', TypeError),
+ (u'\u0221', ValueError),
+ (u'\u0007', ValueError),
+ (u'\u0627\u0031', ValueError),
+ (u'\uE0001', ValueError),
+ (u'\uE0020', ValueError),
+ (u'\uFFF9', ValueError),
+ (u'\uFDD0', ValueError),
+ (u'\u0000', ValueError),
+ (u'\u06DD', ValueError),
+ (u'\uFFFFD', ValueError),
+ (u'\uD800', ValueError),
+ (u'\u200E', ValueError),
+ (u'\u05BE\u00AA\u05BE', ValueError),
+]
+
+
+@pytest.mark.parametrize('source,target', VALID)
+def test_saslprep_conversions(source, target):
+ assert saslprep(source) == target
+
+
+@pytest.mark.parametrize('source,exception', INVALID)
+def test_saslprep_exceptions(source, exception):
+ with pytest.raises(exception) as ex:
+ saslprep(source)
diff --git a/ansible_collections/community/postgresql/tests/unit/plugins/modules/__init__.py b/ansible_collections/community/postgresql/tests/unit/plugins/modules/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/unit/plugins/modules/__init__.py
diff --git a/ansible_collections/community/postgresql/tests/unit/plugins/modules/test_postgresql_set.py b/ansible_collections/community/postgresql/tests/unit/plugins/modules/test_postgresql_set.py
new file mode 100644
index 000000000..a10678202
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/unit/plugins/modules/test_postgresql_set.py
@@ -0,0 +1,28 @@
+# -*- coding: utf-8 -*-
+# Copyright: (c) 2021, Andrew Klychkov (@Andersson007) <aaklychkov@mail.ru>
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+import pytest
+
+from ansible_collections.community.postgresql.plugins.modules.postgresql_set import pretty_to_bytes
+
+
+@pytest.mark.parametrize('input_,expected', [
+ ('', ''),
+ ('test', 'test'),
+ ('0.1', 0.1),
+ ('1024', 1024),
+ ('1024B', 1024),
+ ('1kB', 1024),
+ ('100kB', 102400),
+ ('1MB', 1048576),
+ ('100MB', 104857600),
+ ('1GB', 1073741824),
+ ('10GB', 10737418240),
+ ('127.0.0.1', '127.0.0.1')
+]
+)
+def test_pretty_to_bytes(input_, expected):
+ assert pretty_to_bytes(input_) == expected
diff --git a/ansible_collections/community/postgresql/tests/utils/constraints.txt b/ansible_collections/community/postgresql/tests/utils/constraints.txt
new file mode 100644
index 000000000..ae6000ae1
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/utils/constraints.txt
@@ -0,0 +1,52 @@
+coverage >= 4.2, < 5.0.0, != 4.3.2 ; python_version <= '3.7' # features in 4.2+ required, avoid known bug in 4.3.2 on python 2.6, coverage 5.0+ incompatible
+coverage >= 4.5.4, < 5.0.0 ; python_version > '3.7' # coverage had a bug in < 4.5.4 that would cause unit tests to hang in Python 3.8, coverage 5.0+ incompatible
+cryptography < 2.2 ; python_version < '2.7' # cryptography 2.2 drops support for python 2.6
+deepdiff < 4.0.0 ; python_version < '3' # deepdiff 4.0.0 and later require python 3
+jinja2 < 2.11 ; python_version < '2.7' # jinja2 2.11 and later require python 2.7 or later
+urllib3 < 1.24 ; python_version < '2.7' # urllib3 1.24 and later require python 2.7 or later
+pywinrm >= 0.3.0 # message encryption support
+sphinx < 1.6 ; python_version < '2.7' # sphinx 1.6 and later require python 2.7 or later
+sphinx < 1.8 ; python_version >= '2.7' # sphinx 1.8 and later are currently incompatible with rstcheck 3.3
+pygments >= 2.4.0 # Pygments 2.4.0 includes bugfixes for YAML and YAML+Jinja lexers
+wheel < 0.30.0 ; python_version < '2.7' # wheel 0.30.0 and later require python 2.7 or later
+yamllint != 1.8.0, < 1.14.0 ; python_version < '2.7' # yamllint 1.8.0 and 1.14.0+ require python 2.7+
+pycrypto >= 2.6 # Need features found in 2.6 and greater
+ncclient >= 0.5.2 # Need features added in 0.5.2 and greater
+idna < 2.6, >= 2.5 # linode requires idna < 2.9, >= 2.5, requests requires idna < 2.6, but cryptography will cause the latest version to be installed instead
+paramiko < 2.4.0 ; python_version < '2.7' # paramiko 2.4.0 drops support for python 2.6
+pytest < 3.3.0 ; python_version < '2.7' # pytest 3.3.0 drops support for python 2.6
+pytest < 5.0.0 ; python_version == '2.7' # pytest 5.0.0 and later will no longer support python 2.7
+pytest-forked < 1.0.2 ; python_version < '2.7' # pytest-forked 1.0.2 and later require python 2.7 or later
+pytest-forked >= 1.0.2 ; python_version >= '2.7' # pytest-forked before 1.0.2 does not work with pytest 4.2.0+ (which requires python 2.7+)
+ntlm-auth >= 1.3.0 # message encryption support using cryptography
+requests < 2.20.0 ; python_version < '2.7' # requests 2.20.0 drops support for python 2.6
+requests-ntlm >= 1.1.0 # message encryption support
+requests-credssp >= 0.1.0 # message encryption support
+voluptuous >= 0.11.0 # Schema recursion via Self
+openshift >= 0.6.2, < 0.9.0 # merge_type support
+virtualenv < 16.0.0 ; python_version < '2.7' # virtualenv 16.0.0 and later require python 2.7 or later
+pathspec < 0.6.0 ; python_version < '2.7' # pathspec 0.6.0 and later require python 2.7 or later
+pyopenssl < 18.0.0 ; python_version < '2.7' # pyOpenSSL 18.0.0 and later require python 2.7 or later
+pyfmg == 0.6.1 # newer versions do not pass current unit tests
+pyyaml < 5.1 ; python_version < '2.7' # pyyaml 5.1 and later require python 2.7 or later
+pycparser < 2.19 ; python_version < '2.7' # pycparser 2.19 and later require python 2.7 or later
+mock >= 2.0.0 # needed for features backported from Python 3.6 unittest.mock (assert_called, assert_called_once...)
+pytest-mock >= 1.4.0 # needed for mock_use_standalone_module pytest option
+xmltodict < 0.12.0 ; python_version < '2.7' # xmltodict 0.12.0 and later require python 2.7 or later
+lxml < 4.3.0 ; python_version < '2.7' # lxml 4.3.0 and later require python 2.7 or later
+pyvmomi < 6.0.0 ; python_version < '2.7' # pyvmomi 6.0.0 and later require python 2.7 or later
+pyone == 1.1.9 # newer versions do not pass current integration tests
+boto3 < 1.11 ; python_version < '2.7' # boto3 1.11 drops Python 2.6 support
+botocore >= 1.10.0, < 1.14 ; python_version < '2.7' # adds support for the following AWS services: secretsmanager, fms, and acm-pca; botocore 1.14 drops Python 2.6 support
+botocore >= 1.10.0 ; python_version >= '2.7' # adds support for the following AWS services: secretsmanager, fms, and acm-pca
+setuptools < 45 ; python_version <= '2.7' # setuptools 45 and later require python 3.5 or later
+cffi >= 1.14.2, != 1.14.3 # Yanked version which older versions of pip will still install:
+
+# freeze pylint and its requirements for consistent test results
+astroid == 2.2.5
+isort == 4.3.15
+lazy-object-proxy == 1.3.1
+mccabe == 0.6.1
+pylint == 2.3.1
+typed-ast == 1.4.0 # 1.4.0 is required to compile on Python 3.8
+wrapt == 1.11.1
diff --git a/ansible_collections/community/postgresql/tests/utils/shippable/aix.sh b/ansible_collections/community/postgresql/tests/utils/shippable/aix.sh
new file mode 100755
index 000000000..cd3014cca
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/utils/shippable/aix.sh
@@ -0,0 +1,22 @@
+#!/usr/bin/env bash
+
+set -o pipefail -eux
+
+declare -a args
+IFS='/:' read -ra args <<< "$1"
+
+platform="${args[0]}"
+version="${args[1]}"
+
+if [ "${#args[@]}" -gt 2 ]; then
+ target="shippable/posix/group${args[2]}/"
+else
+ target="shippable/posix/"
+fi
+
+stage="${S:-prod}"
+provider="${P:-default}"
+
+# shellcheck disable=SC2086
+ansible-test integration --color -v --retry-on-error "${target}" ${COVERAGE:+"$COVERAGE"} ${CHANGED:+"$CHANGED"} ${UNSTABLE:+"$UNSTABLE"} \
+ --remote "${platform}/${version}" --remote-terminate always --remote-stage "${stage}" --remote-provider "${provider}"
diff --git a/ansible_collections/community/postgresql/tests/utils/shippable/check_matrix.py b/ansible_collections/community/postgresql/tests/utils/shippable/check_matrix.py
new file mode 100755
index 000000000..608db6923
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/utils/shippable/check_matrix.py
@@ -0,0 +1,120 @@
+#!/usr/bin/env python
+"""Verify the currently executing Shippable test matrix matches the one defined in the "shippable.yml" file."""
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+import datetime
+import json
+import os
+import re
+import sys
+import time
+
+try:
+ from typing import NoReturn
+except ImportError:
+ NoReturn = None
+
+try:
+ # noinspection PyCompatibility
+ from urllib2 import urlopen # pylint: disable=ansible-bad-import-from
+except ImportError:
+ # noinspection PyCompatibility
+ from urllib.request import urlopen
+
+
+def main(): # type: () -> None
+ """Main entry point."""
+ repo_full_name = os.environ['REPO_FULL_NAME']
+ required_repo_full_name = 'ansible-collections/community.postgresql'
+
+ if repo_full_name != required_repo_full_name:
+ sys.stderr.write('Skipping matrix check on repo "%s" which is not "%s".\n' % (repo_full_name, required_repo_full_name))
+ return
+
+ with open('shippable.yml', 'rb') as yaml_file:
+ yaml = yaml_file.read().decode('utf-8').splitlines()
+
+ defined_matrix = [match.group(1) for match in [re.search(r'^ *- env: T=(.*)$', line) for line in yaml] if match and match.group(1) != 'none']
+
+ if not defined_matrix:
+ fail('No matrix entries found in the "shippable.yml" file.',
+ 'Did you modify the "shippable.yml" file?')
+
+ run_id = os.environ['SHIPPABLE_BUILD_ID']
+ sleep = 1
+ jobs = []
+
+ for attempts_remaining in range(4, -1, -1):
+ try:
+ jobs = json.loads(urlopen('https://api.shippable.com/jobs?runIds=%s' % run_id).read())
+
+ if not isinstance(jobs, list):
+ raise Exception('Shippable run %s data is not a list.' % run_id)
+
+ break
+ except Exception as ex:
+ if not attempts_remaining:
+ fail('Unable to retrieve Shippable run %s matrix.' % run_id,
+ str(ex))
+
+ sys.stderr.write('Unable to retrieve Shippable run %s matrix: %s\n' % (run_id, ex))
+ sys.stderr.write('Trying again in %d seconds...\n' % sleep)
+ time.sleep(sleep)
+ sleep *= 2
+
+ if len(jobs) != len(defined_matrix):
+ if len(jobs) == 1:
+ hint = '\n\nMake sure you do not use the "Rebuild with SSH" option.'
+ else:
+ hint = ''
+
+ fail('Shippable run %s has %d jobs instead of the expected %d jobs.' % (run_id, len(jobs), len(defined_matrix)),
+ 'Try re-running the entire matrix.%s' % hint)
+
+ actual_matrix = dict((job.get('jobNumber'), dict(tuple(line.split('=', 1)) for line in job.get('env', [])).get('T', '')) for job in jobs)
+ errors = [(job_number, test, actual_matrix.get(job_number)) for job_number, test in enumerate(defined_matrix, 1) if actual_matrix.get(job_number) != test]
+
+ if len(errors):
+ error_summary = '\n'.join('Job %s expected "%s" but found "%s" instead.' % (job_number, expected, actual) for job_number, expected, actual in errors)
+
+ fail('Shippable run %s has a job matrix mismatch.' % run_id,
+ 'Try re-running the entire matrix.\n\n%s' % error_summary)
+
+
+def fail(message, output): # type: (str, str) -> NoReturn
+ # Include a leading newline to improve readability on Shippable "Tests" tab.
+ # Without this, the first line becomes indented.
+ output = '\n' + output.strip()
+
+ timestamp = datetime.datetime.utcnow().replace(microsecond=0).isoformat()
+
+ # hack to avoid requiring junit-xml, which isn't pre-installed on Shippable outside our test containers
+ xml = '''
+<?xml version="1.0" encoding="utf-8"?>
+<testsuites disabled="0" errors="1" failures="0" tests="1" time="0.0">
+\t<testsuite disabled="0" errors="1" failures="0" file="None" log="None" name="ansible-test" skipped="0" tests="1" time="0" timestamp="%s" url="None">
+\t\t<testcase classname="timeout" name="timeout">
+\t\t\t<error message="%s" type="error">%s</error>
+\t\t</testcase>
+\t</testsuite>
+</testsuites>
+''' % (timestamp, message, output)
+
+ path = 'shippable/testresults/check-matrix.xml'
+ dir_path = os.path.dirname(path)
+
+ if not os.path.exists(dir_path):
+ os.makedirs(dir_path)
+
+ with open(path, 'w') as junit_fd:
+ junit_fd.write(xml.lstrip())
+
+ sys.stderr.write(message + '\n')
+ sys.stderr.write(output + '\n')
+
+ sys.exit(1)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/community/postgresql/tests/utils/shippable/freebsd.sh b/ansible_collections/community/postgresql/tests/utils/shippable/freebsd.sh
new file mode 100755
index 000000000..cd3014cca
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/utils/shippable/freebsd.sh
@@ -0,0 +1,22 @@
+#!/usr/bin/env bash
+
+set -o pipefail -eux
+
+declare -a args
+IFS='/:' read -ra args <<< "$1"
+
+platform="${args[0]}"
+version="${args[1]}"
+
+if [ "${#args[@]}" -gt 2 ]; then
+ target="shippable/posix/group${args[2]}/"
+else
+ target="shippable/posix/"
+fi
+
+stage="${S:-prod}"
+provider="${P:-default}"
+
+# shellcheck disable=SC2086
+ansible-test integration --color -v --retry-on-error "${target}" ${COVERAGE:+"$COVERAGE"} ${CHANGED:+"$CHANGED"} ${UNSTABLE:+"$UNSTABLE"} \
+ --remote "${platform}/${version}" --remote-terminate always --remote-stage "${stage}" --remote-provider "${provider}"
diff --git a/ansible_collections/community/postgresql/tests/utils/shippable/linux.sh b/ansible_collections/community/postgresql/tests/utils/shippable/linux.sh
new file mode 100755
index 000000000..9cc2f966c
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/utils/shippable/linux.sh
@@ -0,0 +1,18 @@
+#!/usr/bin/env bash
+
+set -o pipefail -eux
+
+declare -a args
+IFS='/:' read -ra args <<< "$1"
+
+image="${args[1]}"
+
+if [ "${#args[@]}" -gt 2 ]; then
+ target="shippable/posix/group${args[2]}/"
+else
+ target="shippable/posix/"
+fi
+
+# shellcheck disable=SC2086
+ansible-test integration --color -v --retry-on-error "${target}" ${COVERAGE:+"$COVERAGE"} ${CHANGED:+"$CHANGED"} ${UNSTABLE:+"$UNSTABLE"} \
+ --docker "${image}"
diff --git a/ansible_collections/community/postgresql/tests/utils/shippable/remote.sh b/ansible_collections/community/postgresql/tests/utils/shippable/remote.sh
new file mode 100755
index 000000000..cd3014cca
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/utils/shippable/remote.sh
@@ -0,0 +1,22 @@
+#!/usr/bin/env bash
+
+set -o pipefail -eux
+
+declare -a args
+IFS='/:' read -ra args <<< "$1"
+
+platform="${args[0]}"
+version="${args[1]}"
+
+if [ "${#args[@]}" -gt 2 ]; then
+ target="shippable/posix/group${args[2]}/"
+else
+ target="shippable/posix/"
+fi
+
+stage="${S:-prod}"
+provider="${P:-default}"
+
+# shellcheck disable=SC2086
+ansible-test integration --color -v --retry-on-error "${target}" ${COVERAGE:+"$COVERAGE"} ${CHANGED:+"$CHANGED"} ${UNSTABLE:+"$UNSTABLE"} \
+ --remote "${platform}/${version}" --remote-terminate always --remote-stage "${stage}" --remote-provider "${provider}"
diff --git a/ansible_collections/community/postgresql/tests/utils/shippable/rhel.sh b/ansible_collections/community/postgresql/tests/utils/shippable/rhel.sh
new file mode 100755
index 000000000..cd3014cca
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/utils/shippable/rhel.sh
@@ -0,0 +1,22 @@
+#!/usr/bin/env bash
+
+set -o pipefail -eux
+
+declare -a args
+IFS='/:' read -ra args <<< "$1"
+
+platform="${args[0]}"
+version="${args[1]}"
+
+if [ "${#args[@]}" -gt 2 ]; then
+ target="shippable/posix/group${args[2]}/"
+else
+ target="shippable/posix/"
+fi
+
+stage="${S:-prod}"
+provider="${P:-default}"
+
+# shellcheck disable=SC2086
+ansible-test integration --color -v --retry-on-error "${target}" ${COVERAGE:+"$COVERAGE"} ${CHANGED:+"$CHANGED"} ${UNSTABLE:+"$UNSTABLE"} \
+ --remote "${platform}/${version}" --remote-terminate always --remote-stage "${stage}" --remote-provider "${provider}"
diff --git a/ansible_collections/community/postgresql/tests/utils/shippable/sanity.sh b/ansible_collections/community/postgresql/tests/utils/shippable/sanity.sh
new file mode 100755
index 000000000..c216220e8
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/utils/shippable/sanity.sh
@@ -0,0 +1,27 @@
+#!/usr/bin/env bash
+
+set -o pipefail -eux
+
+declare -a args
+IFS='/:' read -ra args <<< "$1"
+
+group="${args[1]}"
+
+if [ "${BASE_BRANCH:-}" ]; then
+ base_branch="origin/${BASE_BRANCH}"
+else
+ base_branch=""
+fi
+
+if [ "${group}" == "extra" ]; then
+ # ansible-galaxy -vvv collection install community.internal_test_tools
+ git clone --single-branch --depth 1 https://github.com/ansible-collections/community.internal_test_tools.git ../internal_test_tools
+
+ ../internal_test_tools/tools/run.py --color
+ exit
+fi
+
+# shellcheck disable=SC2086
+ansible-test sanity --color -v --junit ${COVERAGE:+"$COVERAGE"} ${CHANGED:+"$CHANGED"} \
+ --docker --base-branch "${base_branch}" \
+ --allow-disabled
diff --git a/ansible_collections/community/postgresql/tests/utils/shippable/shippable.sh b/ansible_collections/community/postgresql/tests/utils/shippable/shippable.sh
new file mode 100755
index 000000000..b181297f9
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/utils/shippable/shippable.sh
@@ -0,0 +1,208 @@
+#!/usr/bin/env bash
+
+set -o pipefail -eux
+
+declare -a args
+IFS='/:' read -ra args <<< "$1"
+
+ansible_version="${args[0]}"
+script="${args[1]}"
+
+function join {
+ local IFS="$1";
+ shift;
+ echo "$*";
+}
+
+test="$(join / "${args[@]:1}")"
+
+docker images ansible/ansible
+docker images quay.io/ansible/*
+docker ps
+
+for container in $(docker ps --format '{{.Image}} {{.ID}}' | grep -v -e '^drydock/' -e '^quay.io/ansible/azure-pipelines-test-container:' | sed 's/^.* //'); do
+ docker rm -f "${container}" || true # ignore errors
+done
+
+docker ps
+
+if [ -d /home/shippable/cache/ ]; then
+ ls -la /home/shippable/cache/
+fi
+
+command -v python
+python -V
+
+function retry
+{
+ # shellcheck disable=SC2034
+ for repetition in 1 2 3; do
+ set +e
+ "$@"
+ result=$?
+ set -e
+ if [ ${result} == 0 ]; then
+ return ${result}
+ fi
+ echo "@* -> ${result}"
+ done
+ echo "Command '@*' failed 3 times!"
+ exit 255
+}
+
+command -v pip
+pip --version
+pip list --disable-pip-version-check
+if [ "${ansible_version}" == "devel" ]; then
+ retry pip install https://github.com/ansible/ansible/archive/devel.tar.gz --disable-pip-version-check
+else
+ retry pip install "https://github.com/ansible/ansible/archive/stable-${ansible_version}.tar.gz" --disable-pip-version-check
+fi
+
+if [ "${SHIPPABLE_BUILD_ID:-}" ]; then
+ export ANSIBLE_COLLECTIONS_PATHS="${HOME}/.ansible"
+ SHIPPABLE_RESULT_DIR="$(pwd)/shippable"
+ TEST_DIR="${ANSIBLE_COLLECTIONS_PATHS}/ansible_collections/community/postgresql"
+ mkdir -p "${TEST_DIR}"
+ cp -aT "${SHIPPABLE_BUILD_DIR}" "${TEST_DIR}"
+ cd "${TEST_DIR}"
+else
+ export ANSIBLE_COLLECTIONS_PATHS="${PWD}/../../../"
+fi
+
+# START: HACK install dependencies for integration tests
+if [ "${script}" != "units" ] && [ "${script}" != "sanity" ] && [ "${ansible_version}" != "2.9" ]; then
+ git clone --depth=1 --single-branch https://github.com/ansible-collections/community.general \
+ "${ANSIBLE_COLLECTIONS_PATHS}/ansible_collections/community/general"
+fi
+# END: HACK
+
+export PYTHONIOENCODING='utf-8'
+
+if [ "${JOB_TRIGGERED_BY_NAME:-}" == "nightly-trigger" ]; then
+ COVERAGE=yes
+ COMPLETE=yes
+fi
+
+if [ -n "${COVERAGE:-}" ]; then
+ # on-demand coverage reporting triggered by setting the COVERAGE environment variable to a non-empty value
+ export COVERAGE="--coverage"
+elif [[ "${COMMIT_MESSAGE}" =~ ci_coverage ]]; then
+ # on-demand coverage reporting triggered by having 'ci_coverage' in the latest commit message
+ export COVERAGE="--coverage"
+else
+ # on-demand coverage reporting disabled (default behavior, always-on coverage reporting remains enabled)
+ export COVERAGE="--coverage-check"
+fi
+
+if [ -n "${COMPLETE:-}" ]; then
+ # disable change detection triggered by setting the COMPLETE environment variable to a non-empty value
+ export CHANGED=""
+elif [[ "${COMMIT_MESSAGE}" =~ ci_complete ]]; then
+ # disable change detection triggered by having 'ci_complete' in the latest commit message
+ export CHANGED=""
+else
+ # enable change detection (default behavior)
+ export CHANGED="--changed"
+fi
+
+if [ "${IS_PULL_REQUEST:-}" == "true" ]; then
+ # run unstable tests which are targeted by focused changes on PRs
+ export UNSTABLE="--allow-unstable-changed"
+else
+ # do not run unstable tests outside PRs
+ export UNSTABLE=""
+fi
+
+# remove empty core/extras module directories from PRs created prior to the repo-merge
+find plugins -type d -empty -print -delete
+
+function cleanup
+{
+ # for complete on-demand coverage generate a report for all files with no coverage on the "sanity/5" job so we only have one copy
+ if [ "${COVERAGE}" == "--coverage" ] && [ "${CHANGED}" == "" ] && [ "${test}" == "sanity/5" ]; then
+ stub="--stub"
+ # trigger coverage reporting for stubs even if no other coverage data exists
+ mkdir -p tests/output/coverage/
+ else
+ stub=""
+ fi
+
+ if [ -d tests/output/coverage/ ]; then
+ if find tests/output/coverage/ -mindepth 1 -name '.*' -prune -o -print -quit | grep -q .; then
+ process_coverage='yes' # process existing coverage files
+ elif [ "${stub}" ]; then
+ process_coverage='yes' # process coverage when stubs are enabled
+ else
+ process_coverage=''
+ fi
+
+ if [ "${process_coverage}" ]; then
+ # use python 3.7 for coverage to avoid running out of memory during coverage xml processing
+ # only use it for coverage to avoid the additional overhead of setting up a virtual environment for a potential no-op job
+ virtualenv --python /usr/bin/python3.7 ~/ansible-venv
+ set +ux
+ . ~/ansible-venv/bin/activate
+ set -ux
+
+ # shellcheck disable=SC2086
+ ansible-test coverage xml --color -v --requirements --group-by command --group-by version ${stub:+"$stub"}
+ cp -a tests/output/reports/coverage=*.xml "$SHIPPABLE_RESULT_DIR/codecoverage/"
+
+ if [ "${ansible_version}" != "2.9" ]; then
+ # analyze and capture code coverage aggregated by integration test target
+ ansible-test coverage analyze targets generate -v "$SHIPPABLE_RESULT_DIR/testresults/coverage-analyze-targets.json"
+ fi
+
+ # upload coverage report to codecov.io only when using complete on-demand coverage
+ if [ "${COVERAGE}" == "--coverage" ] && [ "${CHANGED}" == "" ]; then
+ for file in tests/output/reports/coverage=*.xml; do
+ flags="${file##*/coverage=}"
+ flags="${flags%-powershell.xml}"
+ flags="${flags%.xml}"
+ # remove numbered component from stub files when converting to tags
+ flags="${flags//stub-[0-9]*/stub}"
+ flags="${flags//=/,}"
+ flags="${flags//[^a-zA-Z0-9_,]/_}"
+
+ bash <(curl -s https://ansible-ci-files.s3.us-east-1.amazonaws.com/codecov/codecov.sh) \
+ -f "${file}" \
+ -F "${flags}" \
+ -n "${test}" \
+ -t 00c0f9fa-ac1b-43d3-addf-99de803232c1 \
+ -X coveragepy \
+ -X gcov \
+ -X fix \
+ -X search \
+ -X xcode \
+ || echo "Failed to upload code coverage report to codecov.io: ${file}"
+ done
+ fi
+ fi
+ fi
+
+ if [ -d tests/output/junit/ ]; then
+ cp -aT tests/output/junit/ "$SHIPPABLE_RESULT_DIR/testresults/"
+ fi
+
+ if [ -d tests/output/data/ ]; then
+ cp -a tests/output/data/ "$SHIPPABLE_RESULT_DIR/testresults/"
+ fi
+
+ if [ -d tests/output/bot/ ]; then
+ cp -aT tests/output/bot/ "$SHIPPABLE_RESULT_DIR/testresults/"
+ fi
+}
+
+if [ "${SHIPPABLE_BUILD_ID:-}" ]; then trap cleanup EXIT; fi
+
+if [[ "${COVERAGE:-}" == "--coverage" ]]; then
+ timeout=60
+else
+ timeout=50
+fi
+
+ansible-test env --dump --show --timeout "${timeout}" --color -v
+
+if [ "${SHIPPABLE_BUILD_ID:-}" ]; then "tests/utils/shippable/check_matrix.py"; fi
+"tests/utils/shippable/${script}.sh" "${test}"
diff --git a/ansible_collections/community/postgresql/tests/utils/shippable/timing.py b/ansible_collections/community/postgresql/tests/utils/shippable/timing.py
new file mode 100755
index 000000000..fb538271b
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/utils/shippable/timing.py
@@ -0,0 +1,16 @@
+#!/usr/bin/env python3.7
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+import sys
+import time
+
+start = time.time()
+
+sys.stdin.reconfigure(errors='surrogateescape')
+sys.stdout.reconfigure(errors='surrogateescape')
+
+for line in sys.stdin:
+ seconds = time.time() - start
+ sys.stdout.write('%02d:%02d %s' % (seconds // 60, seconds % 60, line))
+ sys.stdout.flush()
diff --git a/ansible_collections/community/postgresql/tests/utils/shippable/timing.sh b/ansible_collections/community/postgresql/tests/utils/shippable/timing.sh
new file mode 100755
index 000000000..77e257830
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/utils/shippable/timing.sh
@@ -0,0 +1,5 @@
+#!/usr/bin/env bash
+
+set -o pipefail -eu
+
+"$@" 2>&1 | "$(dirname "$0")/timing.py"
diff --git a/ansible_collections/community/postgresql/tests/utils/shippable/units.sh b/ansible_collections/community/postgresql/tests/utils/shippable/units.sh
new file mode 100755
index 000000000..f204dc87e
--- /dev/null
+++ b/ansible_collections/community/postgresql/tests/utils/shippable/units.sh
@@ -0,0 +1,26 @@
+#!/usr/bin/env bash
+
+set -o pipefail -eux
+
+declare -a args
+IFS='/:' read -ra args <<< "$1"
+
+group="${args[1]}"
+
+if [[ "${COVERAGE:-}" == "--coverage" ]]; then
+ timeout=90
+else
+ timeout=30
+fi
+
+group1=()
+
+case "${group}" in
+ 1) options=("${group1[@]:+${group1[@]}}") ;;
+esac
+
+ansible-test env --timeout "${timeout}" --color -v
+
+# shellcheck disable=SC2086
+ansible-test units --color -v --docker default ${COVERAGE:+"$COVERAGE"} ${CHANGED:+"$CHANGED"} \
+ "${options[@]:+${options[@]}}" \