From e34e94873e5137b7407e8e82e69e12c28090e7c3 Mon Sep 17 00:00:00 2001 From: Andrew Klychkov Date: Sun, 10 Dec 2023 12:41:41 +0100 Subject: [PATCH 1/4] postgresql_db: add comment feature --- changelogs/fragments/0-postgresql_db.yml | 2 + plugins/modules/postgresql_db.py | 33 ++++++- .../targets/postgresql_db/tasks/main.yml | 3 + .../tasks/postgresql_db_comment.yml | 95 +++++++++++++++++++ 4 files changed, 128 insertions(+), 5 deletions(-) create mode 100644 changelogs/fragments/0-postgresql_db.yml create mode 100644 tests/integration/targets/postgresql_db/tasks/postgresql_db_comment.yml diff --git a/changelogs/fragments/0-postgresql_db.yml b/changelogs/fragments/0-postgresql_db.yml new file mode 100644 index 000000000..9de6476d6 --- /dev/null +++ b/changelogs/fragments/0-postgresql_db.yml @@ -0,0 +1,2 @@ +minor_changes: +- postgresql_db - add the ``comment`` argument (https://github.com/ansible-collections/community.postgresql/issues/614). diff --git a/plugins/modules/postgresql_db.py b/plugins/modules/postgresql_db.py index ea43df41c..41e313e99 100644 --- a/plugins/modules/postgresql_db.py +++ b/plugins/modules/postgresql_db.py @@ -131,6 +131,11 @@ type: bool default: true version_added: '0.2.0' + comment: + description: + - A comment to apply to the database. + type: str + version_added: '3.3.0' seealso: - name: CREATE DATABASE reference description: Complete reference of the CREATE DATABASE command documentation. @@ -165,6 +170,7 @@ - name: Create a new database with name "acme" community.postgresql.postgresql_db: name: acme + comment: My test DB # Note: If a template different from "template0" is specified, # encoding and locale settings must match those of the template. @@ -310,6 +316,13 @@ def set_conn_limit(cursor, db, conn_limit): return True +def set_comment(cursor, db, comment): + query = "COMMENT ON DATABASE \"%s\" IS '%s'" % (db, comment) + executed_commands.append(query) + cursor.execute(query) + return True + + def get_encoding_id(cursor, encoding): query = "SELECT pg_char_to_encoding(%(encoding)s) AS encoding_id;" cursor.execute(query, {'encoding': encoding}) @@ -321,7 +334,8 @@ def get_db_info(cursor, db): SELECT rolname AS owner, pg_encoding_to_char(encoding) AS encoding, encoding AS encoding_id, datcollate AS lc_collate, datctype AS lc_ctype, pg_database.datconnlimit AS conn_limit, - spcname AS tablespace + spcname AS tablespace, + pg_catalog.shobj_description(pg_database.oid, 'pg_database') AS comment FROM pg_database JOIN pg_roles ON pg_roles.oid = pg_database.datdba JOIN pg_tablespace ON pg_tablespace.oid = pg_database.dattablespace @@ -367,7 +381,7 @@ def db_delete(cursor, db, force=False): return False -def db_create(cursor, db, owner, template, encoding, lc_collate, lc_ctype, conn_limit, tablespace): +def db_create(cursor, db, owner, template, encoding, lc_collate, lc_ctype, conn_limit, tablespace, comment): params = dict(enc=encoding, collate=lc_collate, ctype=lc_ctype, conn_limit=conn_limit, tablespace=tablespace) if not db_exists(cursor, db): query_fragments = ['CREATE DATABASE "%s"' % db] @@ -388,6 +402,8 @@ def db_create(cursor, db, owner, template, encoding, lc_collate, lc_ctype, conn_ query = ' '.join(query_fragments) executed_commands.append(cursor.mogrify(query, params)) cursor.execute(query, params) + if comment: + set_comment(cursor, db, comment) return True else: db_info = get_db_info(cursor, db) @@ -418,10 +434,13 @@ def db_create(cursor, db, owner, template, encoding, lc_collate, lc_ctype, conn_ if tablespace and tablespace != db_info['tablespace']: changed = set_tablespace(cursor, db, tablespace) + if comment and comment != db_info['comment']: + changed = set_comment(cursor, db, comment) + return changed -def db_matches(cursor, db, owner, template, encoding, lc_collate, lc_ctype, conn_limit, tablespace): +def db_matches(cursor, db, owner, template, encoding, lc_collate, lc_ctype, conn_limit, tablespace, comment): if not db_exists(cursor, db): return False else: @@ -438,6 +457,8 @@ def db_matches(cursor, db, owner, template, encoding, lc_collate, lc_ctype, conn return False elif tablespace and tablespace != db_info['tablespace']: return False + elif comment and comment != db_info['comment']: + return False else: return True @@ -650,6 +671,7 @@ def main(): dump_extra_args=dict(type='str', default=None), trust_input=dict(type='bool', default=True), force=dict(type='bool', default=False), + comment=dict(type='str', default=None), ) module = AnsibleModule( @@ -674,6 +696,7 @@ def main(): dump_extra_args = module.params['dump_extra_args'] trust_input = module.params['trust_input'] force = module.params['force'] + comment = module.params['comment'] if state == 'rename': if not target: @@ -722,7 +745,7 @@ def main(): changed = db_exists(cursor, db) elif state == "present": - changed = not db_matches(cursor, db, owner, template, encoding, lc_collate, lc_ctype, conn_limit, tablespace) + changed = not db_matches(cursor, db, owner, template, encoding, lc_collate, lc_ctype, conn_limit, tablespace, comment) elif state == "rename": changed = rename_db(module, cursor, db, target, check_mode=True) @@ -737,7 +760,7 @@ def main(): elif state == "present": try: - changed = db_create(cursor, db, owner, template, encoding, lc_collate, lc_ctype, conn_limit, tablespace) + changed = db_create(cursor, db, owner, template, encoding, lc_collate, lc_ctype, conn_limit, tablespace, comment) except SQLParseError as e: module.fail_json(msg=to_native(e), exception=traceback.format_exc()) diff --git a/tests/integration/targets/postgresql_db/tasks/main.yml b/tests/integration/targets/postgresql_db/tasks/main.yml index dd55c3f98..2c5fc7c95 100644 --- a/tests/integration/targets/postgresql_db/tasks/main.yml +++ b/tests/integration/targets/postgresql_db/tasks/main.yml @@ -45,3 +45,6 @@ # Simple test to create and then drop with force - import_tasks: manage_database.yml + +# Test the comment feature +- import_tasks: postgresql_db_comment.yml diff --git a/tests/integration/targets/postgresql_db/tasks/postgresql_db_comment.yml b/tests/integration/targets/postgresql_db/tasks/postgresql_db_comment.yml new file mode 100644 index 000000000..57490caf6 --- /dev/null +++ b/tests/integration/targets/postgresql_db/tasks/postgresql_db_comment.yml @@ -0,0 +1,95 @@ +# Test code for the postgresql_db comment module feature +# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Set parameters we use with most of tasks + ansible.builtin.set_fact: + task_parameters: &task_parameters + become_user: "{{ pg_user }}" + become: true + register: result + +- name: Create DB with comment + <<: *task_parameters + postgresql_db: + state: present + name: comment_db + login_user: "{{ pg_user }}" + comment: Test DB comment 1 + +- name: Assert the executed commands + assert: + that: + - result is changed + - result.db == "comment_db" + - result.executed_commands == ['CREATE DATABASE "comment_db"', "COMMENT ON DATABASE \"comment_db\" IS 'Test DB comment 1'"] + +- name: Get the DB comment + <<: *task_parameters + postgresql_query: + login_user: "{{ pg_user }}" + query: "SELECT pg_catalog.shobj_description(d.oid, 'pg_database') AS comment FROM pg_catalog.pg_database d WHERE datname = 'comment_db'" + +- name: Check the comments match + assert: + that: + - result.query_result[0]['comment'] == "Test DB comment 1" + + +- name: Create DB with another comment in check mode + <<: *task_parameters + postgresql_db: + state: present + name: comment_db + login_user: "{{ pg_user }}" + comment: Another comment + check_mode: true + +- name: Assert the result + assert: + that: + - result is changed + +- name: Check the comment + <<: *task_parameters + postgresql_query: + login_user: "{{ pg_user }}" + query: "SELECT pg_catalog.shobj_description(d.oid, 'pg_database') AS comment FROM pg_catalog.pg_database d WHERE datname = 'comment_db'" + +- name: Check the comment hasn't changed + assert: + that: + - result.query_result[0]['comment'] == "Test DB comment 1" + + +- name: Create DB with another comment in real mode + <<: *task_parameters + postgresql_db: + state: present + name: comment_db + login_user: "{{ pg_user }}" + comment: Another comment + +- name: Assert the result + assert: + that: + - result is changed + - result.executed_commands == ["COMMENT ON DATABASE \"comment_db\" IS 'Another comment'"] + +- name: Check the comment + <<: *task_parameters + postgresql_query: + login_user: "{{ pg_user }}" + query: "SELECT pg_catalog.shobj_description(d.oid, 'pg_database') AS comment FROM pg_catalog.pg_database d WHERE datname = 'comment_db'" + +- name: Check the comments match + assert: + that: + - result.query_result[0]['comment'] == "Another comment" + +- name: Destroy DB + <<: *task_parameters + postgresql_db: + state: absent + name: comment_db + login_user: "{{ pg_user }}" From 6368b43cd0da45c946c6bb0ebbcf04ed7f218d8e Mon Sep 17 00:00:00 2001 From: Andrew Klychkov Date: Sun, 10 Dec 2023 14:09:50 +0100 Subject: [PATCH 2/4] Reset the comment --- plugins/modules/postgresql_db.py | 7 +- .../tasks/postgresql_db_comment.yml | 80 ++++++++++++++++++- 2 files changed, 83 insertions(+), 4 deletions(-) diff --git a/plugins/modules/postgresql_db.py b/plugins/modules/postgresql_db.py index 41e313e99..fae32c9c0 100644 --- a/plugins/modules/postgresql_db.py +++ b/plugins/modules/postgresql_db.py @@ -133,7 +133,8 @@ version_added: '0.2.0' comment: description: - - A comment to apply to the database. + - Sets a comment on the database. + - To reset the comment, pass an empty string. type: str version_added: '3.3.0' seealso: @@ -434,7 +435,7 @@ def db_create(cursor, db, owner, template, encoding, lc_collate, lc_ctype, conn_ if tablespace and tablespace != db_info['tablespace']: changed = set_tablespace(cursor, db, tablespace) - if comment and comment != db_info['comment']: + if comment is not None and comment != db_info['comment']: changed = set_comment(cursor, db, comment) return changed @@ -457,7 +458,7 @@ def db_matches(cursor, db, owner, template, encoding, lc_collate, lc_ctype, conn return False elif tablespace and tablespace != db_info['tablespace']: return False - elif comment and comment != db_info['comment']: + elif comment is not None and comment != db_info['comment']: return False else: return True diff --git a/tests/integration/targets/postgresql_db/tasks/postgresql_db_comment.yml b/tests/integration/targets/postgresql_db/tasks/postgresql_db_comment.yml index 57490caf6..fc00270ad 100644 --- a/tests/integration/targets/postgresql_db/tasks/postgresql_db_comment.yml +++ b/tests/integration/targets/postgresql_db/tasks/postgresql_db_comment.yml @@ -87,7 +87,85 @@ that: - result.query_result[0]['comment'] == "Another comment" -- name: Destroy DB + +- name: Create DB with the same comment in real mode + <<: *task_parameters + postgresql_db: + state: present + name: comment_db + login_user: "{{ pg_user }}" + comment: Another comment + +- name: Assert the result + assert: + that: + - result is not changed + - result.executed_commands == [] + +- name: Check the comment + <<: *task_parameters + postgresql_query: + login_user: "{{ pg_user }}" + query: "SELECT pg_catalog.shobj_description(d.oid, 'pg_database') AS comment FROM pg_catalog.pg_database d WHERE datname = 'comment_db'" + +- name: Check the comments match + assert: + that: + - result.query_result[0]['comment'] == "Another comment" + + +- name: Not specifiying the comment will not erase it + <<: *task_parameters + postgresql_db: + state: present + name: comment_db + login_user: "{{ pg_user }}" + +- name: Assert the result + assert: + that: + - result is not changed + - result.executed_commands == [] + +- name: Check the comment + <<: *task_parameters + postgresql_query: + login_user: "{{ pg_user }}" + query: "SELECT pg_catalog.shobj_description(d.oid, 'pg_database') AS comment FROM pg_catalog.pg_database d WHERE datname = 'comment_db'" + +- name: Check the comments match + assert: + that: + - result.query_result[0]['comment'] == "Another comment" + + +- name: Reset the comment + <<: *task_parameters + postgresql_db: + state: present + name: comment_db + login_user: "{{ pg_user }}" + comment: '' + +- name: Assert the result + assert: + that: + - result is changed + - result.executed_commands == ["COMMENT ON DATABASE \"comment_db\" IS ''"] + +- name: Check the comment + <<: *task_parameters + postgresql_query: + login_user: "{{ pg_user }}" + query: "SELECT pg_catalog.shobj_description(d.oid, 'pg_database') AS comment FROM pg_catalog.pg_database d WHERE datname = 'comment_db'" + +- name: Check the comments match + assert: + that: + - result.query_result[0]['comment'] == null + + +- name: Clean up <<: *task_parameters postgresql_db: state: absent From 79bd145421e286ce911b25b2dc04559aca74542e Mon Sep 17 00:00:00 2001 From: Andrew Klychkov Date: Sun, 10 Dec 2023 14:14:27 +0100 Subject: [PATCH 3/4] Fix tests --- .../targets/postgresql_db/tasks/postgresql_db_comment.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/targets/postgresql_db/tasks/postgresql_db_comment.yml b/tests/integration/targets/postgresql_db/tasks/postgresql_db_comment.yml index fc00270ad..c00eae394 100644 --- a/tests/integration/targets/postgresql_db/tasks/postgresql_db_comment.yml +++ b/tests/integration/targets/postgresql_db/tasks/postgresql_db_comment.yml @@ -162,7 +162,7 @@ - name: Check the comments match assert: that: - - result.query_result[0]['comment'] == null + - result.query_result[0]['comment'] == None - name: Clean up From 87a9ae82c8fcbc5580ee8b13ba39abbc84bcca74 Mon Sep 17 00:00:00 2001 From: Andrew Klychkov Date: Sun, 10 Dec 2023 14:20:26 +0100 Subject: [PATCH 4/4] Fix spelling --- .../targets/postgresql_db/tasks/postgresql_db_comment.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/targets/postgresql_db/tasks/postgresql_db_comment.yml b/tests/integration/targets/postgresql_db/tasks/postgresql_db_comment.yml index c00eae394..65949ce89 100644 --- a/tests/integration/targets/postgresql_db/tasks/postgresql_db_comment.yml +++ b/tests/integration/targets/postgresql_db/tasks/postgresql_db_comment.yml @@ -114,7 +114,7 @@ - result.query_result[0]['comment'] == "Another comment" -- name: Not specifiying the comment will not erase it +- name: Not specifying the comment will not erase it <<: *task_parameters postgresql_db: state: present