From 1fad110f10e64134bb9476945463609014e127ef Mon Sep 17 00:00:00 2001 From: Andrew Klychkov Date: Thu, 22 Aug 2024 20:04:17 +0200 Subject: [PATCH] clickhouse_user: set default role (#70) * clickhouse_user: set default role * Add integration tests * Add changelog fragment * tmp * Change implementation * Change test formatting * Make the argument a list * Correct the changelog fragment * Use alter user to assign multiple roles as default * add roles argument * tmp * tmp * Update changelog fragment * Fix examples * New options * Update changelog fragment with new options * Fixes * Fix a typo --- changelogs/fragments/0-clickhouse_user.yml | 5 + plugins/modules/clickhouse_user.py | 198 ++++++- .../targets/clickhouse_user/tasks/initial.yml | 506 ++++++++++++++++-- 3 files changed, 674 insertions(+), 35 deletions(-) create mode 100644 changelogs/fragments/0-clickhouse_user.yml diff --git a/changelogs/fragments/0-clickhouse_user.yml b/changelogs/fragments/0-clickhouse_user.yml new file mode 100644 index 0000000..ee3da27 --- /dev/null +++ b/changelogs/fragments/0-clickhouse_user.yml @@ -0,0 +1,5 @@ +minor_changes: +- clickhouse_user - add the ``roles`` argument to grant roles (https://github.com/ansible-collections/community.clickhouse/pull/70). +- clickhouse_user - add the ``default_roles`` argument to set default roles (https://github.com/ansible-collections/community.clickhouse/pull/70). +- clickhouse_user - add the ``roles_mode`` argument to specify how to handle roles passed through ``roles`` argument (https://github.com/ansible-collections/community.clickhouse/pull/70). +- clickhouse_user - add the ``default_roles_mode`` argument to specify how to handle roles passed through ``default_roles`` argument (https://github.com/ansible-collections/community.clickhouse/pull/70). diff --git a/plugins/modules/clickhouse_user.py b/plugins/modules/clickhouse_user.py index 8c82c11..888188d 100644 --- a/plugins/modules/clickhouse_user.py +++ b/plugins/modules/clickhouse_user.py @@ -82,10 +82,52 @@ type: list elements: str version_added: '0.5.0' + roles: + description: + - Grants specified roles to the user. + - To append or remove roles, use the I(roles_mode) argument. + - To revoke all roles, pass an empty list (C([])) and I(default_roles_mode=listed_only). + type: list + elements: str + version_added: '0.6.0' + default_roles: + description: + - Sets specified roles as default for the user. + - The roles must be explicitly granted to the user whether manually + before using this argument or by using the I(roles) + argument in the same task. + - To append or remove roles, use the I(default_roles_mode) argument. + - To unset all roles as default, pass an empty list (C([])) and I(default_roles_mode=listed_only). + type: list + elements: str + version_added: '0.6.0' + roles_mode: + description: + - When C(listed_only) (default), makes the user a member of only roles specified in I(roles). + It will remove the user from all other roles. + - When C(append), appends roles specified in I(roles) to existing user roles. + - When C(remove), removes roles specified in I(roles) from user roles. + - The argument is ignored without I(roles) set. + type: str + choices: ['append', 'listed_only', 'remove'] + default: 'listed_only' + version_added: '0.6.0' + default_roles_mode: + description: + - When C(listed_only) (default), sets only roles specified in I(default_roles) as user default roles. + It will unset all other roles as default roles. + - When C(append), appends roles specified in I(default_roles) to existing user default roles. + default roles instead of unsetting not specified ones. + - When C(remove), removes roles specified in I(default_roles) from user default roles. + - Ignored without I(default_roles) set. + type: str + choices: ['append', 'listed_only', 'remove'] + default: 'listed_only' + version_added: '0.6.0' ''' EXAMPLES = r''' -- name: Create user +- name: Create user granting roles and setting default role community.clickhouse.clickhouse_user: login_host: localhost login_user: alice @@ -94,6 +136,40 @@ name: test_user password: qwerty type_password: sha256_password + roles: + - accountant + - manager + default_roles: + - accountant + +- name: Append the sales role to test_user's roles + community.clickhouse.clickhouse_user: + login_host: localhost + login_user: alice + login_db: foo + login_password: my_password + name: test_user + roles: + - sales + roles_mode: append + +- name: Unset all test_user's default roles + community.clickhouse.clickhouse_user: + login_host: localhost + login_user: alice + login_db: foo + login_password: my_password + name: test_user + default_roles: [] + +- name: Revoke all roles from test_user + community.clickhouse.clickhouse_user: + login_host: localhost + login_user: alice + login_db: foo + login_password: my_password + name: test_user + roles: [] - name: If user exists, update password community.clickhouse.clickhouse_user: @@ -155,6 +231,7 @@ class ClickHouseUser(): def __init__(self, module, client, name, password, type_password, cluster): + self.changed = False self.module = module self.client = client self.name = name @@ -163,11 +240,15 @@ def __init__(self, module, client, name, password, type_password, cluster): self.cluster = cluster # Set default values, then update self.user_exists = False + self.current_default_roles = [] + self.current_roles = [] + # Fetch actual values from DB and + # update the attributes with them self.__populate_info() def __populate_info(self): # Collecting user information - query = ("SELECT name, storage, auth_type " + query = ("SELECT name, storage, auth_type, default_roles_list " "FROM system.users " "WHERE name = '%s'" % self.name) @@ -180,6 +261,16 @@ def __populate_info(self): if result != []: self.user_exists = True + self.current_default_roles = result[0][3] + + if self.user_exists: + self.current_roles = self.__fetch_user_groups() + + def __fetch_user_groups(self): + query = ("SELECT granted_role_name FROM system.role_grants " + "WHERE user_name = '%s'" % self.name) + result = execute_query(self.module, self.client, query) + return [row[0] for row in result] def create(self): list_settings = self.module.params['settings'] @@ -204,11 +295,65 @@ def create(self): if not self.module.check_mode: execute_query(self.module, self.client, query) + if self.module.params['roles'] and self.module.params['roles_mode'] != 'remove': + self.__grant_role(self.module.params['roles']) + + if self.module.params['default_roles'] and self.module.params['default_roles_mode'] != 'remove': + self.__set_default_roles(self.module.params['default_roles']) + return True def update(self, update_password): + if self.module.params['roles'] is not None: + desired = set(self.module.params['roles']) + current = set(self.current_roles) + + if self.module.params['roles_mode'] == 'remove': + # Remove only roles already present in current roles + roles_to_revoke = list(desired & current) + if roles_to_revoke: + self.__revoke_roles(roles_to_revoke) + + elif self.module.params['roles_mode'] == 'append': + # Grant only roles from decired that + # are not already present in current roles + roles_to_grant = list(desired - current) + if roles_to_grant: + self.__grant_roles(roles_to_grant) + + elif self.module.params['roles_mode'] == 'listed_only': + if self.module.params['roles'] == [] and self.current_roles: + self.__revoke_roles(self.current_roles) + elif self.module.params['roles'] != []: + roles_to_grant = list(desired - current) + roles_to_revoke = list(current - desired) + if roles_to_grant: + self.__grant_roles(roles_to_grant) + if roles_to_revoke: + self.__revoke_roles(roles_to_revoke) + + if self.module.params['default_roles'] is not None: + desired = set(self.module.params['default_roles']) + current = set(self.current_default_roles) + + if self.module.params['default_roles_mode'] == 'remove' and desired & current: + if self.module.params['default_roles'] != []: + # In this case, "desired" means "desired to get removed" + self.__set_default_roles(list(current - desired)) + + elif self.module.params['default_roles_mode'] == 'append': + if self.module.params['default_roles'] != [] and desired != current: + self.__set_default_roles(list(current.union(desired))) + + elif self.module.params['default_roles_mode'] == 'listed_only': + if self.module.params['default_roles'] == [] and self.current_roles: + self.__unset_default_roles() + + elif self.module.params['default_roles'] != [] and desired != current: + self.__set_default_roles(self.module.params['default_roles']) + if update_password == 'on_create': - return False + return False or self.changed # If update_password is always # TODO: When ClickHouse will allow to retrieve password hashes, @@ -235,6 +380,47 @@ def drop(self): return True + def __grant_roles(self, roles_to_set): + query = "GRANT %s TO %s" % (', '.join(roles_to_set), self.name) + executed_statements.append(query) + + if not self.module.check_mode: + execute_query(self.module, self.client, query) + + self.changed = True + + def __revoke_roles(self, roles_to_revoke): + query = "REVOKE %s FROM %s" % (', '.join(roles_to_revoke), self.name) + executed_statements.append(query) + + if not self.module.check_mode: + execute_query(self.module, self.client, query) + + self.changed = True + + def __set_default_roles(self, roles_to_set): + self.current_roles = self.__fetch_user_groups() + for role in roles_to_set: + if role not in self.current_roles and role not in self.module.params["roles"]: + self.module.fail_json("User %s is not in %s role. Grant it explicitly first." % (self.name, role)) + + query = "ALTER USER %s DEFAULT ROLE %s" % (self.name, ', '.join(roles_to_set)) + executed_statements.append(query) + + if not self.module.check_mode: + execute_query(self.module, self.client, query) + + self.changed = True + + def __unset_default_roles(self): + query = "SET DEFAULT ROLE NONE TO %s" % self.name + executed_statements.append(query) + + if not self.module.check_mode: + execute_query(self.module, self.client, query) + + self.changed = True + def main(): argument_spec = client_common_argument_spec() @@ -249,6 +435,12 @@ def main(): default='on_create', no_log=False ), settings=dict(type='list', elements='str'), + roles=dict(type='list', elements='str', default=None), + default_roles=dict(type='list', elements='str', default=None), + roles_mode=dict(type='str', choices=['listed_only', 'append', 'remove'], + default='listed_only'), + default_roles_mode=dict(type='str', choices=['listed_only', 'append', 'remove'], + default='listed_only'), ) # Instantiate an object of module class diff --git a/tests/integration/targets/clickhouse_user/tasks/initial.yml b/tests/integration/targets/clickhouse_user/tasks/initial.yml index c3c73f4..1b4c212 100644 --- a/tests/integration/targets/clickhouse_user/tasks/initial.yml +++ b/tests/integration/targets/clickhouse_user/tasks/initial.yml @@ -3,8 +3,7 @@ # and should not be used as examples of how to write Ansible roles # #################################################################### -# Test 1 -- name: Test 1 - Create test_user in check mode +- name: Create test_user in check mode register: result check_mode: true community.clickhouse.clickhouse_user: @@ -12,33 +11,31 @@ name: test_user password: querty -- name: Test 1 - Check ret values in +- name: Check ret values in ansible.builtin.assert: that: - result is changed - result.executed_statements == ["CREATE USER test_user IDENTIFIED WITH sha256_password BY '********'"] -- name: Test 1 - Check the actual state +- name: Check the actual state register: result community.clickhouse.clickhouse_info: login_host: localhost client_kwargs: connect_timeout: 20 -- name: Test 1 - Check result +- name: Check result ansible.builtin.assert: that: - result is not changed - result["users"]["test_user"] is not defined - -# Test 2 -- name: Test 2 - Create test_user +- name: Create test_user community.clickhouse.clickhouse_user: state: present name: test_user -- name: Test 2 - Check the actual state +- name: Check the actual state register: result community.clickhouse.clickhouse_info: login_host: localhost @@ -50,63 +47,55 @@ that: - result["users"]["test_user"] != {} - -# Test 3 -- name: Test 3 - Create test_user if it exists +- name: Create test_user if it exists register: result community.clickhouse.clickhouse_user: state: present name: test_user -- name: Test 3 - Check result +- name: Check result ansible.builtin.assert: that: - result is not changed - result.executed_statements == [] - -# Test 4 -- name: Test 4 - Drop test_user +- name: Drop test_user community.clickhouse.clickhouse_user: state: absent name: test_user -- name: Test 4 - Check the actual state +- name: Check the actual state register: result community.clickhouse.clickhouse_info: login_host: localhost client_kwargs: connect_timeout: 20 -- name: Test 4 - Check result +- name: Check result ansible.builtin.assert: that: - result["users"]["test_user"] is not defined - -# Test 5 -- name: Test 5 - Drop test_user if it does not exists +- name: Drop test_user if it does not exists register: result community.clickhouse.clickhouse_user: state: absent name: test_user -- name: Test 5 - Check ret values +- name: Check ret values ansible.builtin.assert: that: - result is not changed - result.executed_statements == [] - -# Test 6 -- name: Test 6 - Create test_user +- name: Create test_user register: result community.clickhouse.clickhouse_user: state: present name: test_user password: querty -- name: Test 6 - Create test_user again with update_password always +- name: Create test_user again with update_password always register: result community.clickhouse.clickhouse_user: state: present @@ -114,20 +103,18 @@ password: querty update_password: always -- name: Test 6 - Check result +- name: Check result ansible.builtin.assert: that: - result is changed - result.executed_statements == ["ALTER USER test_user IDENTIFIED WITH sha256_password BY '********'"] -- name: Test 6 - Drop test_user +- name: Drop test_user community.clickhouse.clickhouse_user: state: absent name: test_user - -# Test 7 -- name: Test 7 - Create test_user with settings +- name: Create test_user with settings register: result community.clickhouse.clickhouse_user: state: present @@ -137,8 +124,463 @@ - max_memory_usage = 15000 READONLY - max_memory_usage_for_all_queries = 15000 MIN 15000 MAX 16000 WRITABLE -- name: Test 7 - Check result +- name: Check result ansible.builtin.assert: that: - result is changed - result.executed_statements != [] + +# Test roles and default_roles argument +- name: Create test roles + loop: + - accountant + - manager + - sales + community.clickhouse.clickhouse_role: + name: "{{ item }}" + +- name: Set default role in check mode + register: result + check_mode: true + community.clickhouse.clickhouse_user: + state: present + name: test_user + roles: + - accountant + - manager + default_roles: + - accountant + - manager + +- name: Check ret values + ansible.builtin.assert: + that: + - result is changed + - result.executed_statements[0] == "GRANT accountant, manager TO test_user" or result.executed_statements[0] == "GRANT manager, accountant TO test_user" + - result.executed_statements[1] == "ALTER USER test_user DEFAULT ROLE accountant, manager" or result.executed_statements[1] == "ALTER USER test_user DEFAULT ROLE manager, accountant" + +- name: Check the actual state + register: result + community.clickhouse.clickhouse_info: + login_host: localhost + +- name: Check result + ansible.builtin.assert: + that: + - result is not changed + - result["users"]["test_user"]["roles"] == [] + - result["users"]["test_user"]["default_roles_list"] == [] + +- name: Set default role in real mode + register: result + community.clickhouse.clickhouse_user: + state: present + name: test_user + roles: + - accountant + - manager + default_roles: + - accountant + - manager + +- name: Check ret values + ansible.builtin.assert: + that: + - result is changed + - result.executed_statements[0] == "GRANT accountant, manager TO test_user" or result.executed_statements[0] == "GRANT manager, accountant TO test_user" + - result.executed_statements[1] == "ALTER USER test_user DEFAULT ROLE accountant, manager" or result.executed_statements[1] == "ALTER USER test_user DEFAULT ROLE manager, accountant" + +- name: Check the actual state + register: result + community.clickhouse.clickhouse_info: + login_host: localhost + +- name: Check result + ansible.builtin.assert: + that: + - result is not changed + - result["users"]["test_user"]["roles"] == ["accountant", "manager"] or result["users"]["test_user"]["roles"] == ["manager", "accountant"] + - result["users"]["test_user"]["default_roles_list"] == ["accountant", "manager"] + +- name: Grant and set another role as default in check mode + register: result + check_mode: true + community.clickhouse.clickhouse_user: + state: present + name: test_user + roles: + - sales + default_roles: + - sales + +- name: Check ret values + ansible.builtin.assert: + that: + - result is changed + - result.executed_statements[0] == "GRANT sales TO test_user" + - result.executed_statements[1] == "REVOKE accountant, manager FROM test_user" or result.executed_statements[1] == "REVOKE manager, accountant FROM test_user" + - result.executed_statements[2] == "ALTER USER test_user DEFAULT ROLE sales" + +- name: Check the actual state + register: result + community.clickhouse.clickhouse_info: + login_host: localhost + +- name: Check the state has not been changed + ansible.builtin.assert: + that: + - result is not changed + - result["users"]["test_user"]["roles"] == ["accountant", "manager"] or result["users"]["test_user"]["roles"] == ["manager", "accountant"] + - result["users"]["test_user"]["default_roles_list"] == ["accountant", "manager"] or result["users"]["test_user"]["default_roles_list"] == ["manager", "accountant"] + +- name: Set another role as default in real mode + register: result + community.clickhouse.clickhouse_user: + state: present + name: test_user + roles: sales + default_roles: sales + +- name: Check ret values + ansible.builtin.assert: + that: + - result is changed + - result.executed_statements[0] == "GRANT sales TO test_user" + - result.executed_statements[1] == "REVOKE accountant, manager FROM test_user" or result.executed_statements[1] == "REVOKE manager, accountant FROM test_user" + - result.executed_statements[2] == "ALTER USER test_user DEFAULT ROLE sales" + +- name: Check the actual state + register: result + community.clickhouse.clickhouse_info: + login_host: localhost + +- name: Check result + ansible.builtin.assert: + that: + - result is not changed + - result["users"]["test_user"]["roles"] == ["sales"] + - result["users"]["test_user"]["default_roles_list"] == ["sales"] + +- name: Set the role again + register: result + community.clickhouse.clickhouse_user: + state: present + name: test_user + roles: + - sales + default_roles: + - sales + +- name: Check ret values + ansible.builtin.assert: + that: + - result is not changed + - result.executed_statements == [] + +- name: Set the role again in check mode + register: result + check_mode: true + community.clickhouse.clickhouse_user: + state: present + name: test_user + roles: + - sales + default_roles: + - sales + +- name: Check ret values + ansible.builtin.assert: + that: + - result is not changed + - result.executed_statements == [] + +- name: Append the first role in real mode + register: result + community.clickhouse.clickhouse_user: + state: present + name: test_user + roles: + - accountant + default_roles: + - accountant + roles_mode: append + default_roles_mode: append + +- name: Check ret values + ansible.builtin.assert: + that: + - result is changed + - result.executed_statements[0] == "GRANT accountant TO test_user" + - result.executed_statements[1] == "ALTER USER test_user DEFAULT ROLE sales, accountant" or result.executed_statements[1] == "ALTER USER test_user DEFAULT ROLE accountant, sales" + +- name: Check the actual state + register: result + community.clickhouse.clickhouse_info: + login_host: localhost + +- name: Check result + ansible.builtin.assert: + that: + - result is not changed + - result["users"]["test_user"]["roles"] == ["sales", "accountant"] or result["users"]["test_user"]["roles"] == ["accountant", "sales"] + - result["users"]["test_user"]["default_roles_list"] == ["sales", "accountant"] or result["users"]["test_user"]["default_roles_list"] == ["accountant", "sales"] + +- name: Unset all default roles in check mode + register: result + check_mode: true + community.clickhouse.clickhouse_user: + state: present + name: test_user + default_roles: [] + +- name: Check ret values + ansible.builtin.assert: + that: + - result is changed + - result.executed_statements[0] == "SET DEFAULT ROLE NONE TO test_user" + +- name: Check the actual state + register: result + community.clickhouse.clickhouse_info: + login_host: localhost + +- name: Check result + ansible.builtin.assert: + that: + - result is not changed + - result["users"]["test_user"]["roles"] == ["sales", "accountant"] or result["users"]["test_user"]["roles"] == ["accountant", "sales"] + - result["users"]["test_user"]["default_roles_list"] == ["sales", "accountant"] or result["users"]["test_user"]["default_roles_list"] == ["accountant", "sales"] + +- name: Unset all default roles in real mode + register: result + community.clickhouse.clickhouse_user: + state: present + name: test_user + default_roles: [] + +- name: Check ret values + ansible.builtin.assert: + that: + - result is changed + - result.executed_statements[0] == "SET DEFAULT ROLE NONE TO test_user" + +- name: Check the actual state + register: result + community.clickhouse.clickhouse_info: + login_host: localhost + +- name: Check result + ansible.builtin.assert: + that: + - result is not changed + - result["users"]["test_user"]["roles"] == ["sales", "accountant"] or result["users"]["test_user"]["roles"] == ["accountant", "sales"] + - result["users"]["test_user"]["default_roles_list"] == [] + +- name: Revoke all roles in check mode + register: result + check_mode: true + community.clickhouse.clickhouse_user: + state: present + name: test_user + roles: [] + +- name: Check ret values + ansible.builtin.assert: + that: + - result is changed + - result.executed_statements[0] == "REVOKE accountant, sales FROM test_user" or result.executed_statements[0] == "REVOKE sales, accountant FROM test_user" + +- name: Check the actual state + register: result + community.clickhouse.clickhouse_info: + login_host: localhost + +- name: Check result + ansible.builtin.assert: + that: + - result is not changed + - result["users"]["test_user"]["roles"] == ["sales", "accountant"] or result["users"]["test_user"]["roles"] == ["accountant", "sales"] + - result["users"]["test_user"]["default_roles_list"] == [] + +- name: Revoke all roles in real mode + register: result + community.clickhouse.clickhouse_user: + state: present + name: test_user + roles: [] + +- name: Check ret values + ansible.builtin.assert: + that: + - result is changed + - result.executed_statements[0] == "REVOKE accountant, sales FROM test_user" or result.executed_statements[0] == "REVOKE sales, accountant FROM test_user" + +- name: Check the actual state + register: result + community.clickhouse.clickhouse_info: + login_host: localhost + +- name: Check result + ansible.builtin.assert: + that: + - result is not changed + - result["users"]["test_user"]["roles"] == [] + - result["users"]["test_user"]["default_roles_list"] == [] + +- name: Revoke all roles and unset all default roles in check mode + register: result + check_mode: true + community.clickhouse.clickhouse_user: + state: present + name: test_user + roles: [] + default_roles: [] + +- name: Check ret values + ansible.builtin.assert: + that: + - result is not changed + - result.executed_statements == [] + +- name: Check the actual state + register: result + community.clickhouse.clickhouse_info: + login_host: localhost + +- name: Check result + ansible.builtin.assert: + that: + - result is not changed + - result["users"]["test_user"]["roles"] == [] + - result["users"]["test_user"]["default_roles_list"] == [] + +- name: Revoke all roles and unset all default roles in real mode + register: result + community.clickhouse.clickhouse_user: + state: present + name: test_user + roles: [] + default_roles: [] + +- name: Check ret values + ansible.builtin.assert: + that: + - result is not changed + - result.executed_statements == [] + +- name: Check the actual state + register: result + community.clickhouse.clickhouse_info: + login_host: localhost + +- name: Check result + ansible.builtin.assert: + that: + - result is not changed + - result["users"]["test_user"]["roles"] == [] + - result["users"]["test_user"]["default_roles_list"] == [] + +- name: Set default role in real mode + register: result + community.clickhouse.clickhouse_user: + state: present + name: test_user + roles: + - accountant + - manager + default_roles: + - accountant + - manager + +- name: Remove role and default role in check mode + register: result + check_mode: true + community.clickhouse.clickhouse_user: + state: present + name: test_user + roles: + - manager + default_roles: + - manager + roles_mode: remove + default_roles_mode: remove + +- name: Check ret values + ansible.builtin.assert: + that: + - result is changed + - result.executed_statements[0] == "REVOKE manager FROM test_user" + - result.executed_statements[1] == "ALTER USER test_user DEFAULT ROLE accountant" + +- name: Check the actual state + register: result + community.clickhouse.clickhouse_info: + login_host: localhost + +- name: Check result + ansible.builtin.assert: + that: + - result is not changed + - result["users"]["test_user"]["roles"] == ["accountant", "manager"] or result["users"]["test_user"]["roles"] == ["manager", "accountant"] + - result["users"]["test_user"]["default_roles_list"] == ["accountant", "manager"] + +- name: Remove role and default role in real mode + register: result + community.clickhouse.clickhouse_user: + state: present + name: test_user + roles: + - manager + default_roles: + - manager + roles_mode: remove + default_roles_mode: remove + +- name: Check ret values + ansible.builtin.assert: + that: + - result is changed + - result.executed_statements[0] == "REVOKE manager FROM test_user" + - result.executed_statements[1] == "ALTER USER test_user DEFAULT ROLE accountant" + +- name: Check the actual state + register: result + community.clickhouse.clickhouse_info: + login_host: localhost + +- name: Check result + ansible.builtin.assert: + that: + - result is not changed + - result["users"]["test_user"]["roles"] == ["accountant"] + - result["users"]["test_user"]["default_roles_list"] == ["accountant"] + +- name: Remove role and default role in real mode again + register: result + community.clickhouse.clickhouse_user: + state: present + name: test_user + roles: + - manager + default_roles: + - manager + roles_mode: remove + default_roles_mode: remove + +- name: Check ret values + ansible.builtin.assert: + that: + - result is not changed + - result.executed_statements == [] + +- name: Check the actual state + register: result + community.clickhouse.clickhouse_info: + login_host: localhost + +- name: Check result + ansible.builtin.assert: + that: + - result is not changed + - result["users"]["test_user"]["roles"] == ["accountant"] + - result["users"]["test_user"]["default_roles_list"] == ["accountant"]