From ad5200a7c03cf935475f307facd028cfed239650 Mon Sep 17 00:00:00 2001 From: Andrey Klychkov Date: Thu, 3 Oct 2019 15:33:06 +0300 Subject: [PATCH] postgresql_lang: add owner parameter (#62999) * postgresql_lang: add owner parameter * postgresql_lang: add owner parameter, add changelog fragment --- ...99-postgresql_lang_add_owner_parameter.yml | 2 + .../database/postgresql/postgresql_lang.py | 50 ++++- .../targets/postgresql_lang/tasks/main.yml | 4 + .../tasks/postgresql_lang_add_owner_param.yml | 196 ++++++++++++++++++ 4 files changed, 251 insertions(+), 1 deletion(-) create mode 100644 changelogs/fragments/62999-postgresql_lang_add_owner_parameter.yml create mode 100644 test/integration/targets/postgresql_lang/tasks/postgresql_lang_add_owner_param.yml diff --git a/changelogs/fragments/62999-postgresql_lang_add_owner_parameter.yml b/changelogs/fragments/62999-postgresql_lang_add_owner_parameter.yml new file mode 100644 index 0000000000..8c4b1be7dc --- /dev/null +++ b/changelogs/fragments/62999-postgresql_lang_add_owner_parameter.yml @@ -0,0 +1,2 @@ +minor_changes: +- postgresql_lang - add ``owner`` parameter (https://github.com/ansible/ansible/pull/62999). diff --git a/lib/ansible/modules/database/postgresql/postgresql_lang.py b/lib/ansible/modules/database/postgresql/postgresql_lang.py index dbb458377d..774d424dfc 100644 --- a/lib/ansible/modules/database/postgresql/postgresql_lang.py +++ b/lib/ansible/modules/database/postgresql/postgresql_lang.py @@ -103,6 +103,12 @@ options: type: str aliases: [ ssl_rootcert ] version_added: '2.8' + owner: + description: + - Set an owner for the language. + - Ignored when I(state=absent). + type: str + version_added: '2.10' seealso: - name: PostgreSQL languages description: General information about PostgreSQL languages. @@ -156,6 +162,12 @@ EXAMPLES = r''' lang: pltclu state: absent fail_on_drop: no + +- name: In testdb change owner of mylang to alice + postgresql_lang: + db: testdb + lang: mylang + owner: alice ''' RETURN = r''' @@ -228,6 +240,34 @@ def lang_drop(cursor, lang, cascade): return True +def get_lang_owner(cursor, lang): + """Get language owner. + + Args: + cursor (cursor): psycopg2 cursor object. + lang (str): language name. + """ + query = ("SELECT r.rolname FROM pg_language l " + "JOIN pg_roles r ON l.lanowner = r.oid " + "WHERE l.lanname = '%s'" % lang) + cursor.execute(query) + return cursor.fetchone()[0] + + +def set_lang_owner(cursor, lang, owner): + """Set language owner. + + Args: + cursor (cursor): psycopg2 cursor object. + lang (str): language name. + owner (str): name of new owner. + """ + query = "ALTER LANGUAGE %s OWNER TO %s" % (lang, owner) + executed_queries.append(query) + cursor.execute(query) + return True + + def main(): argument_spec = postgres_common_argument_spec() argument_spec.update( @@ -239,6 +279,7 @@ def main(): cascade=dict(type="bool", default="no"), fail_on_drop=dict(type="bool", default="yes"), session_role=dict(type="str"), + owner=dict(type="str"), ) module = AnsibleModule( @@ -253,6 +294,7 @@ def main(): force_trust = module.params["force_trust"] cascade = module.params["cascade"] fail_on_drop = module.params["fail_on_drop"] + owner = module.params["owner"] conn_params = get_conn_params(module, module.params) db_connection = connect_to_db(module, conn_params, autocommit=False) @@ -285,10 +327,16 @@ def main(): else: changed = lang_drop(cursor, lang, cascade) if fail_on_drop and not changed: - msg = "unable to drop language, use cascade to delete dependencies or fail_on_drop=no to ignore" + msg = ("unable to drop language, use cascade " + "to delete dependencies or fail_on_drop=no to ignore") module.fail_json(msg=msg) kw['lang_dropped'] = changed + if owner and state == 'present': + if lang_exists(cursor, lang): + if owner != get_lang_owner(cursor, lang): + changed = set_lang_owner(cursor, lang, owner) + if changed: if module.check_mode: db_connection.rollback() diff --git a/test/integration/targets/postgresql_lang/tasks/main.yml b/test/integration/targets/postgresql_lang/tasks/main.yml index 6e6409397f..b053702825 100644 --- a/test/integration/targets/postgresql_lang/tasks/main.yml +++ b/test/integration/targets/postgresql_lang/tasks/main.yml @@ -1,3 +1,7 @@ # Initial CI tests of postgresql_lang module - import_tasks: postgresql_lang_initial.yml when: ansible_distribution == 'CentOS' + +# CI tests of owner param +- import_tasks: postgresql_lang_add_owner_param.yml + when: ansible_distribution == 'CentOS' diff --git a/test/integration/targets/postgresql_lang/tasks/postgresql_lang_add_owner_param.yml b/test/integration/targets/postgresql_lang/tasks/postgresql_lang_add_owner_param.yml new file mode 100644 index 0000000000..eeb77e5d5a --- /dev/null +++ b/test/integration/targets/postgresql_lang/tasks/postgresql_lang_add_owner_param.yml @@ -0,0 +1,196 @@ +# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) +# 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: yes + 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 }}' + check_mode: yes + + - 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 }}' + + - 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 }}' + check_mode: yes + + - 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 }}'