diff --git a/CHANGELOG.rst b/CHANGELOG.rst index f6c6cb8d..e61ffdc5 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -6,6 +6,18 @@ Community MySQL Collection Release Notes This changelog describes changes after version 2.0.0. +v3.9.0 +====== + +This is the minor release of the ``community.mysql`` collection. +This changelog contains all changes to the modules and plugins in this +collection that have been made after the previous release. + +Minor Changes +------------- + +- mysql_user - add user attribute support via the ``attributes`` parameter and return value (https://github.com/ansible-collections/community.mysql/pull/604). + v3.8.0 ====== diff --git a/plugins/module_utils/user.py b/plugins/module_utils/user.py index 8bd25336..59a3ef4d 100644 --- a/plugins/module_utils/user.py +++ b/plugins/module_utils/user.py @@ -155,7 +155,7 @@ def user_add(cursor, user, host, host_all, password, encrypted, attributes, tls_requires, reuse_existing_password, module): # If attributes are set, perform a sanity check to ensure server supports user attributes before creating user if attributes and not get_attribute_support(cursor): - module.fail_json(msg="user attributes were specified but the mysql server does not support user attributes") + module.fail_json(msg="user attributes were specified but the server does not support user attributes") # we cannot create users without a proper hostname if host_all: @@ -431,15 +431,14 @@ def user_mod(cursor, user, host, host_all, password, encrypted, if attributes: if not attribute_support: - module.fail_json(msg="user attributes were specified but the mysql server does not support user attributes") + module.fail_json(msg="user attributes were specified but the server does not support user attributes") else: current_attributes = attributes_get(cursor, user, host) attributes_to_change = {} for key, value in attributes.items(): if key not in current_attributes or current_attributes[key] != value: - # The mysql null value (None in python) is used to delete an attribute; we use False to represent None in the attributes parameter - attributes_to_change[key] = None if not value else value + attributes_to_change[key] = value if attributes_to_change: msg = "Attributes updated: %s" % (", ".join(["%s: %s" % (key, value) for key, value in attributes_to_change.items()])) @@ -451,7 +450,7 @@ def user_mod(cursor, user, host, host_all, password, encrypted, else: # Final if statements excludes items whose values are None in attributes_to_change, i.e. attributes that will be deleted final_attributes = {k: v for d in (current_attributes, attributes_to_change) for k, v in d.items() if k not in attributes_to_change or - attributes_to_change[k]} + attributes_to_change[k] is not None} changed = True else: final_attributes = current_attributes diff --git a/plugins/modules/mysql_user.py b/plugins/modules/mysql_user.py index bf83b3c3..b9cbc8ba 100644 --- a/plugins/modules/mysql_user.py +++ b/plugins/modules/mysql_user.py @@ -168,7 +168,7 @@ description: - "Create, update, or delete user attributes (arbitrary 'key: value' comments) for the user." - MySQL server must support the INFORMATION_SCHEMA.USER_ATTRIBUTES table. Provided since MySQL 8.0. - - To delete an existing attribute, set its value to False. + - To delete an existing attribute, set its value to null. type: dict version_added: '3.9.0' @@ -268,7 +268,7 @@ name: bob attributes: foo: "foo" - bar: False + bar: null - name: Modify user to require TLS connection with a valid client certificate community.mysql.mysql_user: diff --git a/tests/integration/targets/test_mysql_user/tasks/test_user_attributes.yml b/tests/integration/targets/test_mysql_user/tasks/test_user_attributes.yml index ec4a8f79..60ca2a6d 100644 --- a/tests/integration/targets/test_mysql_user/tasks/test_user_attributes.yml +++ b/tests/integration/targets/test_mysql_user/tasks/test_user_attributes.yml @@ -11,6 +11,11 @@ - when: db_engine == 'mariadb' block: + # ============================================================ + # Fail creating a user with mariadb + # + + # Check mode - name: Attributes | Attempt to create user with attributes with mariadb in check mode mysql_user: <<: *mysql_params @@ -20,14 +25,23 @@ attributes: key1: "value1" ignore_errors: yes - register: result + register: result_module check_mode: yes - - name: Attributes | Assert that creating user with attributes fails with mariadb + - name: Attributes | Run query to verify user creation with attributes fails with mariadb in check mode + mysql_query: + <<: *mysql_params + query: 'SELECT user FROM mysql.user WHERE user = "{{ user_name_2 }}" AND host = "%""' + ignore_errors: yes + register: result_query + + - name: Attributes | Assert that creating user with attributes fails with mariadb in check mode assert: that: - - result is failed + - result_module is failed + - result_query is failed + # Real mode - name: Attributes | Attempt to create user with attributes with mariadb mysql_user: <<: *mysql_params @@ -37,17 +51,29 @@ attributes: key1: "value1" ignore_errors: yes - register: result + register: result_module + + - name: Attributes | Run query to verify user creation with attributes fails with mariadb + mysql_query: + <<: *mysql_params + query: 'SELECT user FROM mysql.user WHERE user = "{{ user_name_2 }}" AND host = "%""' + ignore_errors: yes + register: result_query - name: Attributes | Assert that creating user with attributes fails with mariadb assert: that: - - result is failed + - result_module is failed + - result_query is failed - when: db_engine == 'mysql' block: + # ============================================================ # Create user with attributes + # + + # Check mode - name: Attributes | Test creating a user with attributes in check mode mysql_user: <<: *mysql_params @@ -56,15 +82,24 @@ password: '{{ user_password_2 }}' attributes: key1: "value1" - register: result + register: result_module check_mode: yes + - name: Attributes | Run query to verify user creation did not take place in check mode + mysql_query: + <<: *mysql_params + query: 'SELECT user FROM mysql.user WHERE user = "{{ user_name_2 }} AND host = "%""' + ignore_errors: yes + register: result_query + - name: Attributes | Assert that user would have been created with attributes assert: that: - - result is changed - - result.attributes.key1 == "value1" + - result_module is changed + - result_module.attributes.key1 == "value1" + - result_query is failed + # Real mode - name: Attributes | Test creating a user with attributes mysql_user: <<: *mysql_params @@ -73,15 +108,26 @@ password: '{{ user_password_2 }}' attributes: key1: "value1" - register: result + register: result_module + + - name: Attributes | Run query to verify created user attributes + mysql_query: + <<: *mysql_params + query: 'SELECT attribute FROM INFORMATION_SCHEMA.USER_ATTRIBUTES WHERE user = "{{ user_name_2 }}" AND host = "%"' + register: result_query - name: Attributes | Assert that user was created with attributes assert: that: - - result is changed - - result.attributes.key1 == "value1" + - result_module is changed + - result_module.attributes.key1 == "value1" + - (result_query.query_result[0][0]['ATTRIBUTE'] | from_yaml)['key1'] == "value1" + # ============================================================ # Append attributes on an existing user + # + + # Check mode - name: Attributes | Test appending attributes to an existing user in check mode mysql_user: <<: *mysql_params @@ -89,16 +135,24 @@ host: '%' attributes: key2: "value2" - register: result + register: result_module check_mode: yes + - name: Attributes | Run query to check appended attributes in check mode + mysql_query: + <<: *mysql_params + query: 'SELECT attribute FROM INFORMATION_SCHEMA.USER_ATTRIBUTES WHERE user = "{{ user_name_2 }}" AND host = "%"' + register: result_query + - name: Attributes | Assert that attribute would have been appended and existing attribute stays assert: that: - - result is changed - - result.attributes.key1 == "value1" - - result.attributes.key2 == "value2" + - result_module is changed + - result_module.attributes.key1 == "value1" + - result_module.attributes.key2 == "value2" + - "'key2' not in result_query.query_result[0][0]['ATTRIBUTE'] | from_yaml" + # Real mode - name: Attributes | Test appending attributes to an existing user mysql_user: <<: *mysql_params @@ -106,16 +160,28 @@ host: '%' attributes: key2: "value2" - register: result + register: result_module + + - name: Attributes | Run query to check appended attributes + mysql_query: + <<: *mysql_params + query: 'SELECT attribute FROM INFORMATION_SCHEMA.USER_ATTRIBUTES WHERE user = "{{ user_name_2 }}" AND host = "%"' + register: result_query - name: Attributes | Assert that new attribute was appended and existing attribute stays assert: that: - - result is changed - - result.attributes.key1 == "value1" - - result.attributes.key2 == "value2" + - result_module is changed + - result_module.attributes.key1 == "value1" + - result_module.attributes.key2 == "value2" + - (result_query.query_result[0][0]['ATTRIBUTE'] | from_yaml)['key1'] == "value1" + - (result_query.query_result[0][0]['ATTRIBUTE'] | from_yaml)['key2'] == "value2" + # ============================================================ # Test updating existing attributes + # + + # Check mode - name: Attributes | Test updating attributes on an existing user in check mode mysql_user: <<: *mysql_params @@ -124,14 +190,22 @@ attributes: key2: "new_value2" check_mode: yes - register: result + register: result_module + + - name: Attributes | Run query to verify updated attribute in check mode + mysql_query: + <<: *mysql_params + query: 'SELECT attribute FROM INFORMATION_SCHEMA.USER_ATTRIBUTES WHERE user = "{{ user_name_2 }}" AND host = "%"' + register: result_query - name: Attributes | Assert that attribute would have been updated assert: that: - - result is changed - - result.attributes.key2 == "new_value2" + - result_module is changed + - result_module.attributes.key2 == "new_value2" + - (result_query.query_result[0][0]['ATTRIBUTE'] | from_yaml)['key2'] == "value2" + # Real mode - name: Attributes | Test updating attributes on an existing user mysql_user: <<: *mysql_params @@ -139,47 +213,77 @@ host: '%' attributes: key2: "new_value2" - register: result + register: result_module + + - name: Attributes | Run query to verify updated attribute + mysql_query: + <<: *mysql_params + query: 'SELECT attribute FROM INFORMATION_SCHEMA.USER_ATTRIBUTES WHERE user = "{{ user_name_2 }}" AND host = "%"' + register: result_query - name: Attributes | Assert that attribute was updated assert: that: - - result is changed - - result.attributes.key2 == "new_value2" + - result_module is changed + - result_module.attributes.key2 == "new_value2" + - (result_query.query_result[0][0]['ATTRIBUTE'] | from_yaml)['key2'] == "new_value2" + # ============================================================ # Test deleting attributes + # + + # Check mode - name: Attributes | Test deleting attributes on an existing user in check mode mysql_user: <<: *mysql_params name: '{{ user_name_2 }}' host: '%' attributes: - key2: False - register: result + key2: null + register: result_module check_mode: yes + - name: Attributes | Run query to verify deleted attribute in check mode + mysql_query: + <<: *mysql_params + query: 'SELECT attribute FROM INFORMATION_SCHEMA.USER_ATTRIBUTES WHERE user = "{{ user_name_2 }}" AND host = "%"' + register: result_query + - name: Attributes | Assert that attribute would have been deleted assert: that: - - result is changed - - "'key2' not in result.attributes" + - result_module is changed + - "'key2' not in result_module.attributes" + - (result_query.query_result[0][0]['ATTRIBUTE'] | from_yaml)['key2'] == "new_value2" + # Real mode - name: Attributes | Test deleting attributes on an existing user mysql_user: <<: *mysql_params name: '{{ user_name_2 }}' host: '%' attributes: - key2: False - register: result + key2: null + register: result_module + + - name: Attributes | Run query to verify deleted attribute + mysql_query: + <<: *mysql_params + query: 'SELECT attribute FROM INFORMATION_SCHEMA.USER_ATTRIBUTES WHERE user = "{{ user_name_2 }}" AND host = "%"' + register: result_query - name: Attributes | Assert that attribute was deleted assert: that: - - result is changed - - "'key2' not in result.attributes" + - result_module is changed + - "'key2' not in result_module.attributes" + - "'key2' not in result_query.query_result[0][0]['ATTRIBUTE'] | from_yaml" + + # ============================================================ + # Test attribute idempotency when specifying attributes + # - # Test attribute idempotency + # Check mode - name: Attributes | Test attribute idempotency by trying to change an already correct attribute in check mode mysql_user: <<: *mysql_params @@ -187,15 +291,23 @@ host: '%' attributes: key1: "value1" - register: result + register: result_module check_mode: yes + - name: Attributes | Run query to verify idempotency of already correct attribute in check mode + mysql_query: + <<: *mysql_params + query: 'SELECT attribute FROM INFORMATION_SCHEMA.USER_ATTRIBUTES WHERE user = "{{ user_name_2 }}" AND host = "%"' + register: result_query + - name: Attributes | Assert that attribute would not have been updated assert: that: - - result is not changed - - result.attributes.key1 == "value1" + - result_module is not changed + - result_module.attributes.key1 == "value1" + - (result_query.query_result[0][0]['ATTRIBUTE'] | from_yaml)['key1'] == "value1" + # Real mode - name: Attributes | Test attribute idempotency by trying to change an already correct attribute mysql_user: <<: *mysql_params @@ -203,42 +315,71 @@ host: '%' attributes: key1: "value1" - register: result + register: result_module + + - name: Attributes | Run query to verify idempotency of already correct attribute + mysql_query: + <<: *mysql_params + query: 'SELECT attribute FROM INFORMATION_SCHEMA.USER_ATTRIBUTES WHERE user = "{{ user_name_2 }}" AND host = "%"' + register: result_query - name: Attributes | Assert that attribute was not updated assert: that: - - result is not changed - - result.attributes.key1 == "value1" + - result_module is not changed + - result_module.attributes.key1 == "value1" + - (result_query.query_result[0][0]['ATTRIBUTE'] | from_yaml)['key1'] == "value1" + + # ============================================================ + # Test attribute idempotency when not specifying attribute parameter + # + # Check mode - name: Attributes | Test attribute idempotency by not specifying attribute parameter in check mode mysql_user: <<: *mysql_params name: '{{ user_name_2 }}' host: '%' - register: result + register: result_module check_mode: yes + - name: Attributes | Run query to verify idempotency when not specifying attribute parameter in check mode + mysql_query: + <<: *mysql_params + query: 'SELECT attribute FROM INFORMATION_SCHEMA.USER_ATTRIBUTES WHERE user = "{{ user_name_2 }}" AND host = "%"' + register: result_query + - name: Attributes | Assert that attribute is returned in check mode assert: that: - - result is not changed - - result.attributes.key1 == "value1" + - result_module is not changed + - result_module.attributes.key1 == "value1" + - (result_query.query_result[0][0]['ATTRIBUTE'] | from_yaml)['key1'] == "value1" + # Real mode - name: Attributes | Test attribute idempotency by not specifying attribute parameter mysql_user: <<: *mysql_params name: '{{ user_name_2 }}' host: '%' - register: result + register: result_module + + - name: Attributes | Run query to verify idempotency when not specifying attribute parameter + mysql_query: + <<: *mysql_params + query: 'SELECT attribute FROM INFORMATION_SCHEMA.USER_ATTRIBUTES WHERE user = "{{ user_name_2 }}" AND host = "%"' + register: result_query - name: Attributes | Assert that attribute is returned assert: that: - - result is not changed - - result.attributes.key1 == "value1" + - result_module is not changed + - result_module.attributes.key1 == "value1" + - (result_query.query_result[0][0]['ATTRIBUTE'] | from_yaml)['key1'] == "value1" + # ============================================================ # Cleanup + # - include_tasks: utils/remove_user.yml vars: user_name: "{{ user_name_2 }}"