From 35122ce3f1228a84ac4eab3089f65d2a00db4afc Mon Sep 17 00:00:00 2001 From: Sebastian Pfahl Date: Fri, 14 Jun 2024 20:20:10 +0200 Subject: [PATCH 01/13] Add options to start and stop group replication --- ...ication_add_group_replication_commands.yml | 2 + plugins/modules/mysql_replication.py | 87 ++++++++++++++++++- .../tasks/mysql_replication_initial.yml | 2 +- 3 files changed, 87 insertions(+), 4 deletions(-) create mode 100644 changelogs/fragments/mysql_replication_add_group_replication_commands.yml diff --git a/changelogs/fragments/mysql_replication_add_group_replication_commands.yml b/changelogs/fragments/mysql_replication_add_group_replication_commands.yml new file mode 100644 index 0000000..95c5f4a --- /dev/null +++ b/changelogs/fragments/mysql_replication_add_group_replication_commands.yml @@ -0,0 +1,2 @@ +minor_changes: + - mysql_replication - add ``startgroupreplication`` and ``stopgroupreplication`` options (https://github.com/ansible-collections/community.mysql/pull/647). \ No newline at end of file diff --git a/plugins/modules/mysql_replication.py b/plugins/modules/mysql_replication.py index a743b82..ec1d1f0 100644 --- a/plugins/modules/mysql_replication.py +++ b/plugins/modules/mysql_replication.py @@ -20,6 +20,7 @@ author: - Balazs Pocze (@banyek) - Andrew Klychkov (@Andersson007) - Dennis Urtubia (@dennisurtubia) +- Sebastian Pfahl (@eryx12o45) - Laurent Indermühle (@laurent-indermuehle) options: mode: @@ -33,7 +34,9 @@ options: C(stopreplica) (STOP REPLICA), C(resetprimary) (RESET MASTER) - supported since community.mysql 0.1.0, C(resetreplica) (RESET REPLICA), - C(resetreplicaall) (RESET REPLICA ALL). + C(resetreplicaall) (RESET REPLICA ALL), + C(startgroupreplication) (START GROUP_REPLICATION) - supported since community.mysql 3.10.0, + C(stopgroupreplication) (STOP GROUP_REPLICATION) - supported since community.mysql 3.10.0. type: str choices: - changeprimary @@ -45,6 +48,8 @@ options: - resetprimary - resetreplica - resetreplicaall + - startgroupreplication + - stopgroupreplication default: getreplica primary_host: description: @@ -191,7 +196,16 @@ options: type: bool default: false version_added: '0.1.0' - + group_replication_user: + description: + - User for group replication. + type: str + version_added: '3.10.0' + group_replication_password: + description: + - Password for group replication user. + type: str + version_added: '3.10.0' notes: - Compatible with MariaDB or MySQL. - If an empty value for the parameter of string type is needed, use an empty string. @@ -285,6 +299,17 @@ EXAMPLES = r''' community.mysql.mysql_replication: mode: changeprimary fail_on_error: true + +- name: Start mysql group replication + community.mysql.mysql_replication: + mode: startgroupreplication + group_replication_user: group_repl_user + group_replication_password: group_repl_passwd + +- name: Stop mysql group replication + community.mysql.mysql_replication: + mode: stopgroupreplication + ''' RETURN = r''' @@ -465,6 +490,38 @@ def changereplication(cursor, chm, channel=''): cursor.execute(query) +def startgroupreplication(module, cursor, chm, fail_on_error=False, term='GROUP_REPLICATION'): + query = 'START %s %s' % (term, ','.join(chm)) + + try: + executed_queries.append(query) + cursor.execute(query) + started = True + except mysql_driver.Warning as e: + started = False + except Exception as e: + if fail_on_error: + module.fail_json(msg="START %s failed: %s" % (term, to_native(e))) + started = False + return started + + +def stopgroupreplication(module, cursor, fail_on_error=False, term='GROUP_REPLICATION'): + query = 'STOP %s' % term + + try: + executed_queries.append(query) + cursor.execute(query) + stopped = True + except mysql_driver.Warning as e: + stopped = False + except Exception as e: + if fail_on_error: + module.fail_json(msg="STOP %s failed: %s" % (term, to_native(e))) + stopped = False + return stopped + + def main(): argument_spec = mysql_common_argument_spec() argument_spec.update( @@ -477,7 +534,9 @@ def main(): 'resetprimary', 'resetreplica', 'resetreplicaall', - 'changereplication']), + 'changereplication', + 'startgroupreplication', + 'stopgroupreplication']), primary_auto_position=dict(type='bool', default=False, aliases=['master_auto_position']), primary_host=dict(type='str', aliases=['master_host']), primary_user=dict(type='str', aliases=['master_user']), @@ -501,6 +560,8 @@ def main(): connection_name=dict(type='str'), channel=dict(type='str'), fail_on_error=dict(type='bool', default=False), + group_replication_user=dict(type='str'), + group_replication_password=dict(type='str', no_log=True), ) module = AnsibleModule( argument_spec=argument_spec, @@ -540,6 +601,8 @@ def main(): connection_name = module.params["connection_name"] channel = module.params['channel'] fail_on_error = module.params['fail_on_error'] + group_replication_user = module.params['group_replication_user'] + group_replication_password = module.params['group_replication_password'] if mysql_driver is None: module.fail_json(msg=mysql_driver_fail_msg) @@ -742,6 +805,24 @@ def main(): module.fail_json(msg='%s. Query == CHANGE REPLICATION SOURCE TO %s' % (to_native(e), chm)) result['changed'] = True module.exit_json(queries=executed_queries, **result) + elif mode == "startgroupreplication": + chm = [] + if group_replication_user is not None: + chm.append(" USER='%s'" % group_replication_user) + if group_replication_password is not None: + chm.append(" PASSWORD='%s'" % group_replication_password) + started = startgroupreplication(module, cursor, chm, fail_on_error) + if started: + module.exit_json(msg="Group replication started ", changed=True, queries=executed_queries) + else: + module.exit_json(msg="Group replication already started (Or cannot be started)", changed=False, + ueries=executed_queries) + elif mode == "stopgroupreplication": + stopped = stopgroupreplication(module, cursor, channel, fail_on_error) + if stopped: + module.exit_json(msg="Group replication stopped", changed=True, queries=executed_queries) + else: + module.exit_json(msg="Group replication already stopped", changed=False, queries=executed_queries) warnings.simplefilter("ignore") diff --git a/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_initial.yml b/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_initial.yml index 182ea5c..1f31f83 100644 --- a/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_initial.yml +++ b/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_initial.yml @@ -338,5 +338,5 @@ assert: that: - - "result.msg == 'value of mode must be one of: getprimary, getreplica, changeprimary, stopreplica, startreplica, resetprimary, resetreplica, resetreplicaall, changereplication, got: stopslave'" + "result.msg == 'value of mode must be one of: getprimary, getreplica, changeprimary, stopreplica, startreplica, resetprimary, resetreplica, resetreplicaall, changereplication, startgroupreplication, stopgroupreplication, got: stopslave'" - result is failed From 6f686aad9a941b1af5da8c2a54b0f2a800d5919b Mon Sep 17 00:00:00 2001 From: Sebastian Pfahl Date: Mon, 14 Jul 2025 16:59:42 +0200 Subject: [PATCH 02/13] Added test --- .../test_mysql_replication/defaults/main.yml | 8 ++ .../test_mysql_replication/tasks/main.yml | 6 + .../tasks/mysql_replication_group.yml | 104 ++++++++++++++++++ .../module_utils/test_mariadb_replication.py | 97 ++++++++++++++++ .../module_utils/test_mysql_replication.py | 97 ++++++++++++++++ 5 files changed, 312 insertions(+) create mode 100644 tests/integration/targets/test_mysql_replication/tasks/mysql_replication_group.yml diff --git a/tests/integration/targets/test_mysql_replication/defaults/main.yml b/tests/integration/targets/test_mysql_replication/defaults/main.yml index 48fd560..76f66d3 100644 --- a/tests/integration/targets/test_mysql_replication/defaults/main.yml +++ b/tests/integration/targets/test_mysql_replication/defaults/main.yml @@ -15,3 +15,11 @@ test_channel: test_channel-1 user_name_1: 'db_user1' user_password_1: 'gadfFDSdtTU^Sdfuj' + +enable_group_replication: true +group_replication_group_name: 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee' +group_replication_port: 33061 +group_replication_user: 'group_repl_user' +group_replication_pass: 'group_repl_pass' +group_replication_local_address: "{{ mysql_host }}:{{ group_replication_port }}" +group_replication_group_seeds: "{{ mysql_host }}:{{ group_replication_port }}" diff --git a/tests/integration/targets/test_mysql_replication/tasks/main.yml b/tests/integration/targets/test_mysql_replication/tasks/main.yml index e77af38..e90568d 100644 --- a/tests/integration/targets/test_mysql_replication/tasks/main.yml +++ b/tests/integration/targets/test_mysql_replication/tasks/main.yml @@ -35,3 +35,9 @@ when: - db_engine == 'mysql' - db_version is version('8.0.23', '>=') + +# Tests of group replication: +- import_tasks: mysql_replication_group.yml + when: + - (db_engine == 'mysql' and db_version is version('8.0.0', '>=')) or + (db_engine == 'mariadb' and db_version is version('10.1.0', '>=')) diff --git a/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_group.yml b/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_group.yml new file mode 100644 index 0000000..14af793 --- /dev/null +++ b/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_group.yml @@ -0,0 +1,104 @@ +--- +# Copyright: (c) 2025, Sebastian Pfahl (@eryx12o45) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +- vars: + mysql_params: &mysql_params + login_user: '{{ mysql_user }}' + login_password: '{{ mysql_password }}' + login_host: '{{ mysql_host }}' + + block: + # Setup group replication prerequisites + - name: Create group replication user + shell: + "echo \"CREATE USER IF NOT EXISTS \ + '{{ group_replication_user }}'@'{{ mysql_host }}' \ + IDENTIFIED {% if db_engine == 'mysql' %}WITH mysql_native_password {% endif %}BY '{{ group_replication_pass }}'; \ + GRANT REPLICATION SLAVE ON *.* TO \ + '{{ group_replication_user }}'@'{{ mysql_host }}'; \ + GRANT BACKUP_ADMIN ON *.* TO \ + '{{ group_replication_user }}'@'{{ mysql_host }}'; \ + GRANT GROUP_REPLICATION_STREAM ON *.* TO \ + '{{ group_replication_user }}'@'{{ mysql_host }}';\" | {{ mysql_command }}" + when: db_version is version('8.0.0', '>=') + + - name: Create group replication user for MariaDB + shell: + "echo \"CREATE USER IF NOT EXISTS \ + '{{ group_replication_user }}'@'{{ mysql_host }}' \ + IDENTIFIED BY '{{ group_replication_pass }}'; \ + GRANT REPLICATION SLAVE ON *.* TO \ + '{{ group_replication_user }}'@'{{ mysql_host }}';\" | {{ mysql_command }}" + when: db_engine == 'mariadb' + + # Configure group replication settings + - name: Configure group replication settings for MySQL + shell: + "echo \"SET GLOBAL group_replication_group_name='{{ group_replication_group_name }}'; \ + SET GLOBAL group_replication_local_address='{{ mysql_host }}:{{ group_replication_port }}'; \ + SET GLOBAL group_replication_group_seeds='{{ mysql_host }}:{{ group_replication_port }}'; \ + SET GLOBAL group_replication_bootstrap_group=ON;\" | {{ mysql_command }}" + when: db_engine == 'mysql' and db_version is version('8.0.0', '>=') + + - name: Configure group replication settings for MariaDB + shell: + "echo \"SET GLOBAL wsrep_provider='/usr/lib/galera/libgalera_smm.so'; \ + SET GLOBAL wsrep_cluster_name='{{ group_replication_group_name }}'; \ + SET GLOBAL wsrep_cluster_address='gcomm://{{ mysql_host }}:{{ group_replication_port }}'; \ + SET GLOBAL wsrep_node_address='{{ mysql_host }}:{{ group_replication_port }}';\" | {{ mysql_command }}" + when: db_engine == 'mariadb' and db_version is version('10.1.0', '>=') + ignore_errors: true + + # Test startgroupreplication mode + - name: Start group replication + mysql_replication: + <<: *mysql_params + mode: startgroupreplication + group_replication_user: '{{ group_replication_user }}' + group_replication_password: '{{ group_replication_pass }}' + register: result + ignore_errors: true + + - name: Assert that startgroupreplication returns expected values + assert: + that: + - result is changed + - result.queries | length > 0 + - "'START GROUP_REPLICATION' in result.queries[0]" + when: result is not failed + + # Check group replication status + - name: Check group replication status + shell: + "echo \"SHOW STATUS LIKE 'group_replication_status';\" | {{ mysql_command }}" + register: gr_status + ignore_errors: true + + # Test stopgroupreplication mode + - name: Stop group replication + mysql_replication: + <<: *mysql_params + mode: stopgroupreplication + register: result + ignore_errors: true + + - name: Assert that stopgroupreplication returns expected values + assert: + that: + - result is changed + - result.queries | length > 0 + - "'STOP GROUP_REPLICATION' in result.queries[0]" + when: result is not failed + + # Cleanup group replication settings + - name: Reset group replication settings for MySQL + shell: + "echo \"SET GLOBAL group_replication_bootstrap_group=OFF;\" | {{ mysql_command }}" + when: db_engine == 'mysql' and db_version is version('8.0.0', '>=') + ignore_errors: true + + - name: Drop group replication user + shell: + "echo \"DROP USER IF EXISTS '{{ group_replication_user }}'@'{{ mysql_host }}';\" | {{ mysql_command }}" + ignore_errors: true diff --git a/tests/unit/plugins/module_utils/test_mariadb_replication.py b/tests/unit/plugins/module_utils/test_mariadb_replication.py index 513d8cf..7e8940f 100644 --- a/tests/unit/plugins/module_utils/test_mariadb_replication.py +++ b/tests/unit/plugins/module_utils/test_mariadb_replication.py @@ -10,6 +10,20 @@ from ansible_collections.community.mysql.plugins.module_utils.implementations.ma from ..utils import dummy_cursor_class +class MockCursor: + def __init__(self, status="ONLINE"): + self.status = status + self.executed_queries = [] + + def execute(self, query): + self.executed_queries.append(query) + + def fetchone(self): + if "group_replication_status" in self.executed_queries[-1]: + return ["group_replication_status", self.status] + return None + + @pytest.mark.parametrize( 'f_output,c_output,c_ret_type', [ @@ -22,3 +36,86 @@ from ..utils import dummy_cursor_class def test_uses_replica_terminology(f_output, c_output, c_ret_type): cursor = dummy_cursor_class(c_output, c_ret_type) assert uses_replica_terminology(cursor) == f_output + + +@pytest.mark.parametrize( + 'user,password,expected_query', + [ + (None, None, "START GROUP_REPLICATION"), + ("repl_user", None, "START GROUP_REPLICATION USER='repl_user'"), + (None, "repl_pass", "START GROUP_REPLICATION"), + ("repl_user", "repl_pass", "START GROUP_REPLICATION USER='repl_user' PASSWORD='repl_pass'"), + ] +) +def test_start_group_replication(user, password, expected_query): + """Test startgroupreplication function with different parameters.""" + from ansible_collections.community.mysql.plugins.modules.mysql_replication import startgroupreplication + + cursor = MockCursor() + module = type('obj', (object,), { + 'fail_json': lambda msg: None, + }) + + chm = [] + if user: + chm.append(" USER='%s'" % user) + if password: + chm.append(" PASSWORD='%s'" % password) + + result = startgroupreplication(module, cursor, chm, False) + + assert result is True + assert cursor.executed_queries[0] == expected_query + assert cursor.executed_queries[1] == "SHOW STATUS LIKE 'group_replication_status';" + + +def test_stop_group_replication(): + """Test stopgroupreplication function.""" + from ansible_collections.community.mysql.plugins.modules.mysql_replication import stopgroupreplication + + cursor = MockCursor() + module = type('obj', (object,), { + 'fail_json': lambda msg: None, + }) + + result = stopgroupreplication(module, cursor, False) + + assert result is True + assert cursor.executed_queries[0] == "STOP GROUP_REPLICATION" + assert cursor.executed_queries[1] == "SHOW STATUS LIKE 'group_replication_status';" + + +def test_start_group_replication_fail(): + """Test startgroupreplication function with failure.""" + from ansible_collections.community.mysql.plugins.modules.mysql_replication import startgroupreplication + import pymysql + + cursor = MockCursor(status="ERROR") + module = type('obj', (object,), { + 'fail_json': lambda msg: None, + }) + + # Mock the Warning exception + pymysql.Warning = Exception + + result = startgroupreplication(module, cursor, [], True) + + assert result is False + + +def test_stop_group_replication_fail(): + """Test stopgroupreplication function with failure.""" + from ansible_collections.community.mysql.plugins.modules.mysql_replication import stopgroupreplication + import pymysql + + cursor = MockCursor(status="ERROR") + module = type('obj', (object,), { + 'fail_json': lambda msg: None, + }) + + # Mock the Warning exception + pymysql.Warning = Exception + + result = stopgroupreplication(module, cursor, True) + + assert result is False diff --git a/tests/unit/plugins/module_utils/test_mysql_replication.py b/tests/unit/plugins/module_utils/test_mysql_replication.py index c4126a5..cd4a9d7 100644 --- a/tests/unit/plugins/module_utils/test_mysql_replication.py +++ b/tests/unit/plugins/module_utils/test_mysql_replication.py @@ -10,6 +10,20 @@ from ansible_collections.community.mysql.plugins.module_utils.implementations.my from ..utils import dummy_cursor_class +class MockCursor: + def __init__(self, status="ONLINE"): + self.status = status + self.executed_queries = [] + + def execute(self, query): + self.executed_queries.append(query) + + def fetchone(self): + if "group_replication_status" in self.executed_queries[-1]: + return ["group_replication_status", self.status] + return None + + @pytest.mark.parametrize( 'f_output,c_output,c_ret_type', [ @@ -26,3 +40,86 @@ from ..utils import dummy_cursor_class def test_uses_replica_terminology(f_output, c_output, c_ret_type): cursor = dummy_cursor_class(c_output, c_ret_type) assert uses_replica_terminology(cursor) == f_output + + +@pytest.mark.parametrize( + 'user,password,expected_query', + [ + (None, None, "START GROUP_REPLICATION"), + ("repl_user", None, "START GROUP_REPLICATION USER='repl_user'"), + (None, "repl_pass", "START GROUP_REPLICATION"), + ("repl_user", "repl_pass", "START GROUP_REPLICATION USER='repl_user' PASSWORD='repl_pass'"), + ] +) +def test_start_group_replication(user, password, expected_query): + """Test startgroupreplication function with different parameters.""" + from ansible_collections.community.mysql.plugins.modules.mysql_replication import startgroupreplication + + cursor = MockCursor() + module = type('obj', (object,), { + 'fail_json': lambda msg: None, + }) + + chm = [] + if user: + chm.append(" USER='%s'" % user) + if password: + chm.append(" PASSWORD='%s'" % password) + + result = startgroupreplication(module, cursor, chm, False) + + assert result is True + assert cursor.executed_queries[0] == expected_query + assert cursor.executed_queries[1] == "SHOW STATUS LIKE 'group_replication_status';" + + +def test_stop_group_replication(): + """Test stopgroupreplication function.""" + from ansible_collections.community.mysql.plugins.modules.mysql_replication import stopgroupreplication + + cursor = MockCursor() + module = type('obj', (object,), { + 'fail_json': lambda msg: None, + }) + + result = stopgroupreplication(module, cursor, False) + + assert result is True + assert cursor.executed_queries[0] == "STOP GROUP_REPLICATION" + assert cursor.executed_queries[1] == "SHOW STATUS LIKE 'group_replication_status';" + + +def test_start_group_replication_fail(): + """Test startgroupreplication function with failure.""" + from ansible_collections.community.mysql.plugins.modules.mysql_replication import startgroupreplication + import pymysql + + cursor = MockCursor(status="ERROR") + module = type('obj', (object,), { + 'fail_json': lambda msg: None, + }) + + # Mock the Warning exception + pymysql.Warning = Exception + + result = startgroupreplication(module, cursor, [], True) + + assert result is False + + +def test_stop_group_replication_fail(): + """Test stopgroupreplication function with failure.""" + from ansible_collections.community.mysql.plugins.modules.mysql_replication import stopgroupreplication + import pymysql + + cursor = MockCursor(status="ERROR") + module = type('obj', (object,), { + 'fail_json': lambda msg: None, + }) + + # Mock the Warning exception + pymysql.Warning = Exception + + result = stopgroupreplication(module, cursor, True) + + assert result is False From a2dff5c61c2ebf1e2e25a8ec0035bd2a168e05b4 Mon Sep 17 00:00:00 2001 From: Sebastian Pfahl Date: Mon, 14 Jul 2025 17:19:32 +0200 Subject: [PATCH 03/13] fix liniting --- .../module_utils/test_mariadb_replication.py | 30 +++++++++---------- .../module_utils/test_mysql_replication.py | 30 +++++++++---------- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/tests/unit/plugins/module_utils/test_mariadb_replication.py b/tests/unit/plugins/module_utils/test_mariadb_replication.py index 7e8940f..7442e30 100644 --- a/tests/unit/plugins/module_utils/test_mariadb_replication.py +++ b/tests/unit/plugins/module_utils/test_mariadb_replication.py @@ -50,20 +50,20 @@ def test_uses_replica_terminology(f_output, c_output, c_ret_type): def test_start_group_replication(user, password, expected_query): """Test startgroupreplication function with different parameters.""" from ansible_collections.community.mysql.plugins.modules.mysql_replication import startgroupreplication - + cursor = MockCursor() module = type('obj', (object,), { 'fail_json': lambda msg: None, }) - + chm = [] if user: chm.append(" USER='%s'" % user) if password: chm.append(" PASSWORD='%s'" % password) - + result = startgroupreplication(module, cursor, chm, False) - + assert result is True assert cursor.executed_queries[0] == expected_query assert cursor.executed_queries[1] == "SHOW STATUS LIKE 'group_replication_status';" @@ -72,14 +72,14 @@ def test_start_group_replication(user, password, expected_query): def test_stop_group_replication(): """Test stopgroupreplication function.""" from ansible_collections.community.mysql.plugins.modules.mysql_replication import stopgroupreplication - + cursor = MockCursor() module = type('obj', (object,), { 'fail_json': lambda msg: None, }) - + result = stopgroupreplication(module, cursor, False) - + assert result is True assert cursor.executed_queries[0] == "STOP GROUP_REPLICATION" assert cursor.executed_queries[1] == "SHOW STATUS LIKE 'group_replication_status';" @@ -89,17 +89,17 @@ def test_start_group_replication_fail(): """Test startgroupreplication function with failure.""" from ansible_collections.community.mysql.plugins.modules.mysql_replication import startgroupreplication import pymysql - + cursor = MockCursor(status="ERROR") module = type('obj', (object,), { 'fail_json': lambda msg: None, }) - + # Mock the Warning exception pymysql.Warning = Exception - + result = startgroupreplication(module, cursor, [], True) - + assert result is False @@ -107,15 +107,15 @@ def test_stop_group_replication_fail(): """Test stopgroupreplication function with failure.""" from ansible_collections.community.mysql.plugins.modules.mysql_replication import stopgroupreplication import pymysql - + cursor = MockCursor(status="ERROR") module = type('obj', (object,), { 'fail_json': lambda msg: None, }) - + # Mock the Warning exception pymysql.Warning = Exception - + result = stopgroupreplication(module, cursor, True) - + assert result is False diff --git a/tests/unit/plugins/module_utils/test_mysql_replication.py b/tests/unit/plugins/module_utils/test_mysql_replication.py index cd4a9d7..19ba12b 100644 --- a/tests/unit/plugins/module_utils/test_mysql_replication.py +++ b/tests/unit/plugins/module_utils/test_mysql_replication.py @@ -54,20 +54,20 @@ def test_uses_replica_terminology(f_output, c_output, c_ret_type): def test_start_group_replication(user, password, expected_query): """Test startgroupreplication function with different parameters.""" from ansible_collections.community.mysql.plugins.modules.mysql_replication import startgroupreplication - + cursor = MockCursor() module = type('obj', (object,), { 'fail_json': lambda msg: None, }) - + chm = [] if user: chm.append(" USER='%s'" % user) if password: chm.append(" PASSWORD='%s'" % password) - + result = startgroupreplication(module, cursor, chm, False) - + assert result is True assert cursor.executed_queries[0] == expected_query assert cursor.executed_queries[1] == "SHOW STATUS LIKE 'group_replication_status';" @@ -76,14 +76,14 @@ def test_start_group_replication(user, password, expected_query): def test_stop_group_replication(): """Test stopgroupreplication function.""" from ansible_collections.community.mysql.plugins.modules.mysql_replication import stopgroupreplication - + cursor = MockCursor() module = type('obj', (object,), { 'fail_json': lambda msg: None, }) - + result = stopgroupreplication(module, cursor, False) - + assert result is True assert cursor.executed_queries[0] == "STOP GROUP_REPLICATION" assert cursor.executed_queries[1] == "SHOW STATUS LIKE 'group_replication_status';" @@ -93,17 +93,17 @@ def test_start_group_replication_fail(): """Test startgroupreplication function with failure.""" from ansible_collections.community.mysql.plugins.modules.mysql_replication import startgroupreplication import pymysql - + cursor = MockCursor(status="ERROR") module = type('obj', (object,), { 'fail_json': lambda msg: None, }) - + # Mock the Warning exception pymysql.Warning = Exception - + result = startgroupreplication(module, cursor, [], True) - + assert result is False @@ -111,15 +111,15 @@ def test_stop_group_replication_fail(): """Test stopgroupreplication function with failure.""" from ansible_collections.community.mysql.plugins.modules.mysql_replication import stopgroupreplication import pymysql - + cursor = MockCursor(status="ERROR") module = type('obj', (object,), { 'fail_json': lambda msg: None, }) - + # Mock the Warning exception pymysql.Warning = Exception - + result = stopgroupreplication(module, cursor, True) - + assert result is False From 1a7b06b14dee5526ac0332c4f562e846254bb783 Mon Sep 17 00:00:00 2001 From: Sebastian Pfahl Date: Mon, 14 Jul 2025 21:04:43 +0200 Subject: [PATCH 04/13] fix missing mysql def in script --- .../tasks/mysql_replication_group.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_group.yml b/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_group.yml index 14af793..a6a1177 100644 --- a/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_group.yml +++ b/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_group.yml @@ -17,11 +17,14 @@ IDENTIFIED {% if db_engine == 'mysql' %}WITH mysql_native_password {% endif %}BY '{{ group_replication_pass }}'; \ GRANT REPLICATION SLAVE ON *.* TO \ '{{ group_replication_user }}'@'{{ mysql_host }}'; \ + GRANT CONNECTION_ADMIN ON *.* TO \ + '{{ group_replication_user }}'@'{{ mysql_host }}'; \ GRANT BACKUP_ADMIN ON *.* TO \ '{{ group_replication_user }}'@'{{ mysql_host }}'; \ GRANT GROUP_REPLICATION_STREAM ON *.* TO \ - '{{ group_replication_user }}'@'{{ mysql_host }}';\" | {{ mysql_command }}" - when: db_version is version('8.0.0', '>=') + '{{ group_replication_user }}'@'{{ mysql_host }}'; \ + FLUSH PRIVILEGES;\" | {{ mysql_command }}" + when: db_engine == 'mysql' and db_version is version('8.0.0', '>=') - name: Create group replication user for MariaDB shell: From 7c376590e72f866d4bfbb61f608411ca0b7c9ccb Mon Sep 17 00:00:00 2001 From: Sebastian Pfahl Date: Mon, 14 Jul 2025 21:30:15 +0200 Subject: [PATCH 05/13] fix more tests --- .../module_utils/test_mariadb_replication.py | 8 +++----- .../module_utils/test_mysql_replication.py | 20 +++++-------------- 2 files changed, 8 insertions(+), 20 deletions(-) diff --git a/tests/unit/plugins/module_utils/test_mariadb_replication.py b/tests/unit/plugins/module_utils/test_mariadb_replication.py index 7442e30..7aaa667 100644 --- a/tests/unit/plugins/module_utils/test_mariadb_replication.py +++ b/tests/unit/plugins/module_utils/test_mariadb_replication.py @@ -41,10 +41,10 @@ def test_uses_replica_terminology(f_output, c_output, c_ret_type): @pytest.mark.parametrize( 'user,password,expected_query', [ - (None, None, "START GROUP_REPLICATION"), + (None, None, "START GROUP_REPLICATION "), ("repl_user", None, "START GROUP_REPLICATION USER='repl_user'"), - (None, "repl_pass", "START GROUP_REPLICATION"), - ("repl_user", "repl_pass", "START GROUP_REPLICATION USER='repl_user' PASSWORD='repl_pass'"), + (None, "repl_pass", "START GROUP_REPLICATION PASSWORD='repl_pass'"), + ("repl_user", "repl_pass", "START GROUP_REPLICATION USER='repl_user', PASSWORD='repl_pass'"), ] ) def test_start_group_replication(user, password, expected_query): @@ -66,7 +66,6 @@ def test_start_group_replication(user, password, expected_query): assert result is True assert cursor.executed_queries[0] == expected_query - assert cursor.executed_queries[1] == "SHOW STATUS LIKE 'group_replication_status';" def test_stop_group_replication(): @@ -82,7 +81,6 @@ def test_stop_group_replication(): assert result is True assert cursor.executed_queries[0] == "STOP GROUP_REPLICATION" - assert cursor.executed_queries[1] == "SHOW STATUS LIKE 'group_replication_status';" def test_start_group_replication_fail(): diff --git a/tests/unit/plugins/module_utils/test_mysql_replication.py b/tests/unit/plugins/module_utils/test_mysql_replication.py index 19ba12b..8768b1e 100644 --- a/tests/unit/plugins/module_utils/test_mysql_replication.py +++ b/tests/unit/plugins/module_utils/test_mysql_replication.py @@ -45,10 +45,10 @@ def test_uses_replica_terminology(f_output, c_output, c_ret_type): @pytest.mark.parametrize( 'user,password,expected_query', [ - (None, None, "START GROUP_REPLICATION"), + (None, None, "START GROUP_REPLICATION "), ("repl_user", None, "START GROUP_REPLICATION USER='repl_user'"), - (None, "repl_pass", "START GROUP_REPLICATION"), - ("repl_user", "repl_pass", "START GROUP_REPLICATION USER='repl_user' PASSWORD='repl_pass'"), + (None, "repl_pass", "START GROUP_REPLICATION PASSWORD='repl_pass'"), + ("repl_user", "repl_pass", "START GROUP_REPLICATION USER='repl_user', PASSWORD='repl_pass'"), ] ) def test_start_group_replication(user, password, expected_query): @@ -62,15 +62,14 @@ def test_start_group_replication(user, password, expected_query): chm = [] if user: - chm.append(" USER='%s'" % user) + chm.append("USER='%s'" % user) if password: - chm.append(" PASSWORD='%s'" % password) + chm.append("PASSWORD='%s'" % password) result = startgroupreplication(module, cursor, chm, False) assert result is True assert cursor.executed_queries[0] == expected_query - assert cursor.executed_queries[1] == "SHOW STATUS LIKE 'group_replication_status';" def test_stop_group_replication(): @@ -86,22 +85,17 @@ def test_stop_group_replication(): assert result is True assert cursor.executed_queries[0] == "STOP GROUP_REPLICATION" - assert cursor.executed_queries[1] == "SHOW STATUS LIKE 'group_replication_status';" def test_start_group_replication_fail(): """Test startgroupreplication function with failure.""" from ansible_collections.community.mysql.plugins.modules.mysql_replication import startgroupreplication - import pymysql cursor = MockCursor(status="ERROR") module = type('obj', (object,), { 'fail_json': lambda msg: None, }) - # Mock the Warning exception - pymysql.Warning = Exception - result = startgroupreplication(module, cursor, [], True) assert result is False @@ -110,16 +104,12 @@ def test_start_group_replication_fail(): def test_stop_group_replication_fail(): """Test stopgroupreplication function with failure.""" from ansible_collections.community.mysql.plugins.modules.mysql_replication import stopgroupreplication - import pymysql cursor = MockCursor(status="ERROR") module = type('obj', (object,), { 'fail_json': lambda msg: None, }) - # Mock the Warning exception - pymysql.Warning = Exception - result = stopgroupreplication(module, cursor, True) assert result is False From 461efe61fe357b9de1bd3214e3982c0e79edd453 Mon Sep 17 00:00:00 2001 From: Sebastian Pfahl Date: Mon, 14 Jul 2025 21:30:33 +0200 Subject: [PATCH 06/13] fix more tests --- .../unit/plugins/module_utils/test_mariadb_replication.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/tests/unit/plugins/module_utils/test_mariadb_replication.py b/tests/unit/plugins/module_utils/test_mariadb_replication.py index 7aaa667..3599ad0 100644 --- a/tests/unit/plugins/module_utils/test_mariadb_replication.py +++ b/tests/unit/plugins/module_utils/test_mariadb_replication.py @@ -86,16 +86,12 @@ def test_stop_group_replication(): def test_start_group_replication_fail(): """Test startgroupreplication function with failure.""" from ansible_collections.community.mysql.plugins.modules.mysql_replication import startgroupreplication - import pymysql cursor = MockCursor(status="ERROR") module = type('obj', (object,), { 'fail_json': lambda msg: None, }) - # Mock the Warning exception - pymysql.Warning = Exception - result = startgroupreplication(module, cursor, [], True) assert result is False @@ -104,16 +100,12 @@ def test_start_group_replication_fail(): def test_stop_group_replication_fail(): """Test stopgroupreplication function with failure.""" from ansible_collections.community.mysql.plugins.modules.mysql_replication import stopgroupreplication - import pymysql cursor = MockCursor(status="ERROR") module = type('obj', (object,), { 'fail_json': lambda msg: None, }) - # Mock the Warning exception - pymysql.Warning = Exception - result = stopgroupreplication(module, cursor, True) assert result is False From 1439274e158ab3bc61880c5b95a4fa7f7b9190d6 Mon Sep 17 00:00:00 2001 From: Sebastian Pfahl Date: Mon, 14 Jul 2025 21:40:53 +0200 Subject: [PATCH 07/13] fix more tests --- .../unit/plugins/module_utils/test_mariadb_replication.py | 8 +++++--- tests/unit/plugins/module_utils/test_mysql_replication.py | 4 +++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/tests/unit/plugins/module_utils/test_mariadb_replication.py b/tests/unit/plugins/module_utils/test_mariadb_replication.py index 3599ad0..4082ad2 100644 --- a/tests/unit/plugins/module_utils/test_mariadb_replication.py +++ b/tests/unit/plugins/module_utils/test_mariadb_replication.py @@ -16,6 +16,8 @@ class MockCursor: self.executed_queries = [] def execute(self, query): + if self.status == "ERROR": + raise Exception("Mocked execution error") self.executed_queries.append(query) def fetchone(self): @@ -44,7 +46,7 @@ def test_uses_replica_terminology(f_output, c_output, c_ret_type): (None, None, "START GROUP_REPLICATION "), ("repl_user", None, "START GROUP_REPLICATION USER='repl_user'"), (None, "repl_pass", "START GROUP_REPLICATION PASSWORD='repl_pass'"), - ("repl_user", "repl_pass", "START GROUP_REPLICATION USER='repl_user', PASSWORD='repl_pass'"), + ("repl_user", "repl_pass", "START GROUP_REPLICATION USER='repl_user',PASSWORD='repl_pass'"), ] ) def test_start_group_replication(user, password, expected_query): @@ -58,9 +60,9 @@ def test_start_group_replication(user, password, expected_query): chm = [] if user: - chm.append(" USER='%s'" % user) + chm.append("USER='%s'" % user) if password: - chm.append(" PASSWORD='%s'" % password) + chm.append("PASSWORD='%s'" % password) result = startgroupreplication(module, cursor, chm, False) diff --git a/tests/unit/plugins/module_utils/test_mysql_replication.py b/tests/unit/plugins/module_utils/test_mysql_replication.py index 8768b1e..a4f906c 100644 --- a/tests/unit/plugins/module_utils/test_mysql_replication.py +++ b/tests/unit/plugins/module_utils/test_mysql_replication.py @@ -16,6 +16,8 @@ class MockCursor: self.executed_queries = [] def execute(self, query): + if self.status == "ERROR": + raise Exception("Mocked execution error") self.executed_queries.append(query) def fetchone(self): @@ -48,7 +50,7 @@ def test_uses_replica_terminology(f_output, c_output, c_ret_type): (None, None, "START GROUP_REPLICATION "), ("repl_user", None, "START GROUP_REPLICATION USER='repl_user'"), (None, "repl_pass", "START GROUP_REPLICATION PASSWORD='repl_pass'"), - ("repl_user", "repl_pass", "START GROUP_REPLICATION USER='repl_user', PASSWORD='repl_pass'"), + ("repl_user", "repl_pass", "START GROUP_REPLICATION USER='repl_user',PASSWORD='repl_pass'"), ] ) def test_start_group_replication(user, password, expected_query): From 781bf601e7afbec4470be74329c019b1261e6a2f Mon Sep 17 00:00:00 2001 From: Sebastian Pfahl Date: Tue, 15 Jul 2025 08:09:13 +0200 Subject: [PATCH 08/13] adding missing mysql group_replication module --- .../test_mysql_replication/tasks/mysql_replication_group.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_group.yml b/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_group.yml index a6a1177..14853d3 100644 --- a/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_group.yml +++ b/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_group.yml @@ -38,7 +38,8 @@ # Configure group replication settings - name: Configure group replication settings for MySQL shell: - "echo \"SET GLOBAL group_replication_group_name='{{ group_replication_group_name }}'; \ + "echo \"INSTALL PLUGIN group_replication SONAME 'group_replication.so'; \ + SET GLOBAL group_replication_group_name='{{ group_replication_group_name }}'; \ SET GLOBAL group_replication_local_address='{{ mysql_host }}:{{ group_replication_port }}'; \ SET GLOBAL group_replication_group_seeds='{{ mysql_host }}:{{ group_replication_port }}'; \ SET GLOBAL group_replication_bootstrap_group=ON;\" | {{ mysql_command }}" From 54b4effcc63e3a43b7f1908cf6c8811009800bf1 Mon Sep 17 00:00:00 2001 From: Sebastian Pfahl Date: Tue, 15 Jul 2025 08:39:05 +0200 Subject: [PATCH 09/13] fix 'fail' tests --- .../module_utils/test_mariadb_replication.py | 72 +++++++++++-------- .../module_utils/test_mysql_replication.py | 72 +++++++++++-------- tests/unit/plugins/utils.py | 21 ++++++ 3 files changed, 107 insertions(+), 58 deletions(-) diff --git a/tests/unit/plugins/module_utils/test_mariadb_replication.py b/tests/unit/plugins/module_utils/test_mariadb_replication.py index 4082ad2..a984d90 100644 --- a/tests/unit/plugins/module_utils/test_mariadb_replication.py +++ b/tests/unit/plugins/module_utils/test_mariadb_replication.py @@ -7,23 +7,7 @@ __metaclass__ = type import pytest from ansible_collections.community.mysql.plugins.module_utils.implementations.mariadb.replication import uses_replica_terminology -from ..utils import dummy_cursor_class - - -class MockCursor: - def __init__(self, status="ONLINE"): - self.status = status - self.executed_queries = [] - - def execute(self, query): - if self.status == "ERROR": - raise Exception("Mocked execution error") - self.executed_queries.append(query) - - def fetchone(self): - if "group_replication_status" in self.executed_queries[-1]: - return ["group_replication_status", self.status] - return None +from ..utils import dummy_cursor_class, MockCursor @pytest.mark.parametrize( @@ -89,25 +73,55 @@ def test_start_group_replication_fail(): """Test startgroupreplication function with failure.""" from ansible_collections.community.mysql.plugins.modules.mysql_replication import startgroupreplication - cursor = MockCursor(status="ERROR") - module = type('obj', (object,), { - 'fail_json': lambda msg: None, - }) + # Create a mock mysql_driver with a Warning attribute + class MockDriver: + Warning = MockCursor.Warning - result = startgroupreplication(module, cursor, [], True) + # Save the original mysql_driver + from ansible_collections.community.mysql.plugins.modules import mysql_replication + original_driver = mysql_replication.mysql_driver - assert result is False + try: + # Replace with our mock driver + mysql_replication.mysql_driver = MockDriver + + cursor = MockCursor(status="ERROR") + module = type('obj', (object,), { + 'fail_json': lambda msg: None, + }) + + result = startgroupreplication(module, cursor, [], True) + + assert result is False + finally: + # Restore the original driver + mysql_replication.mysql_driver = original_driver def test_stop_group_replication_fail(): """Test stopgroupreplication function with failure.""" from ansible_collections.community.mysql.plugins.modules.mysql_replication import stopgroupreplication - cursor = MockCursor(status="ERROR") - module = type('obj', (object,), { - 'fail_json': lambda msg: None, - }) + # Create a mock mysql_driver with a Warning attribute + class MockDriver: + Warning = MockCursor.Warning - result = stopgroupreplication(module, cursor, True) + # Save the original mysql_driver + from ansible_collections.community.mysql.plugins.modules import mysql_replication + original_driver = mysql_replication.mysql_driver - assert result is False + try: + # Replace with our mock driver + mysql_replication.mysql_driver = MockDriver + + cursor = MockCursor(status="ERROR") + module = type('obj', (object,), { + 'fail_json': lambda msg: None, + }) + + result = stopgroupreplication(module, cursor, True) + + assert result is False + finally: + # Restore the original driver + mysql_replication.mysql_driver = original_driver diff --git a/tests/unit/plugins/module_utils/test_mysql_replication.py b/tests/unit/plugins/module_utils/test_mysql_replication.py index a4f906c..6dfea29 100644 --- a/tests/unit/plugins/module_utils/test_mysql_replication.py +++ b/tests/unit/plugins/module_utils/test_mysql_replication.py @@ -7,23 +7,7 @@ __metaclass__ = type import pytest from ansible_collections.community.mysql.plugins.module_utils.implementations.mysql.replication import uses_replica_terminology -from ..utils import dummy_cursor_class - - -class MockCursor: - def __init__(self, status="ONLINE"): - self.status = status - self.executed_queries = [] - - def execute(self, query): - if self.status == "ERROR": - raise Exception("Mocked execution error") - self.executed_queries.append(query) - - def fetchone(self): - if "group_replication_status" in self.executed_queries[-1]: - return ["group_replication_status", self.status] - return None +from ..utils import dummy_cursor_class, MockCursor @pytest.mark.parametrize( @@ -93,25 +77,55 @@ def test_start_group_replication_fail(): """Test startgroupreplication function with failure.""" from ansible_collections.community.mysql.plugins.modules.mysql_replication import startgroupreplication - cursor = MockCursor(status="ERROR") - module = type('obj', (object,), { - 'fail_json': lambda msg: None, - }) + # Create a mock mysql_driver with a Warning attribute + class MockDriver: + Warning = MockCursor.Warning - result = startgroupreplication(module, cursor, [], True) + # Save the original mysql_driver + from ansible_collections.community.mysql.plugins.modules import mysql_replication + original_driver = mysql_replication.mysql_driver - assert result is False + try: + # Replace with our mock driver + mysql_replication.mysql_driver = MockDriver + + cursor = MockCursor(status="ERROR") + module = type('obj', (object,), { + 'fail_json': lambda msg: None, + }) + + result = startgroupreplication(module, cursor, [], True) + + assert result is False + finally: + # Restore the original driver + mysql_replication.mysql_driver = original_driver def test_stop_group_replication_fail(): """Test stopgroupreplication function with failure.""" from ansible_collections.community.mysql.plugins.modules.mysql_replication import stopgroupreplication - cursor = MockCursor(status="ERROR") - module = type('obj', (object,), { - 'fail_json': lambda msg: None, - }) + # Create a mock mysql_driver with a Warning attribute + class MockDriver: + Warning = MockCursor.Warning - result = stopgroupreplication(module, cursor, True) + # Save the original mysql_driver + from ansible_collections.community.mysql.plugins.modules import mysql_replication + original_driver = mysql_replication.mysql_driver - assert result is False + try: + # Replace with our mock driver + mysql_replication.mysql_driver = MockDriver + + cursor = MockCursor(status="ERROR") + module = type('obj', (object,), { + 'fail_json': lambda msg: None, + }) + + result = stopgroupreplication(module, cursor, True) + + assert result is False + finally: + # Restore the original driver + mysql_replication.mysql_driver = original_driver diff --git a/tests/unit/plugins/utils.py b/tests/unit/plugins/utils.py index 7712d1c..ce32525 100644 --- a/tests/unit/plugins/utils.py +++ b/tests/unit/plugins/utils.py @@ -17,3 +17,24 @@ class dummy_cursor_class(): elif self.ret_val_type == 'list': return [self.output] + + +class MockCursor: + def __init__(self, status="ONLINE"): + self.status = status + self.executed_queries = [] + + def execute(self, query): + self.executed_queries.append(query) + if self.status == "ERROR": + # Create a custom exception that mimics mysql_driver.Warning + class MockWarning(Exception): + pass + # Make it available as a class attribute for tests to use + MockCursor.Warning = MockWarning + raise MockWarning("Mocked execution error") + + def fetchone(self): + if len(self.executed_queries) > 0 and "group_replication_status" in self.executed_queries[-1]: + return ["group_replication_status", self.status] + return None From 81045d074bd3efdf1471f29dde580a261a43aaf6 Mon Sep 17 00:00:00 2001 From: Sebastian Pfahl Date: Tue, 15 Jul 2025 12:49:18 +0200 Subject: [PATCH 10/13] fix missing Warning attr. on MockCursor --- tests/unit/plugins/utils.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/unit/plugins/utils.py b/tests/unit/plugins/utils.py index ce32525..5315a99 100644 --- a/tests/unit/plugins/utils.py +++ b/tests/unit/plugins/utils.py @@ -20,9 +20,12 @@ class dummy_cursor_class(): class MockCursor: - def __init__(self, status="ONLINE"): + Warning = None + + def __init__(self, status="ONLINE", warning = None): self.status = status self.executed_queries = [] + self.Warning = warning def execute(self, query): self.executed_queries.append(query) From 4b14ac1b5055346f0948c772931a10875e1a5582 Mon Sep 17 00:00:00 2001 From: Sebastian Pfahl Date: Tue, 15 Jul 2025 12:53:21 +0200 Subject: [PATCH 11/13] fix linting --- tests/unit/plugins/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/plugins/utils.py b/tests/unit/plugins/utils.py index 5315a99..fea9ad4 100644 --- a/tests/unit/plugins/utils.py +++ b/tests/unit/plugins/utils.py @@ -22,7 +22,7 @@ class dummy_cursor_class(): class MockCursor: Warning = None - def __init__(self, status="ONLINE", warning = None): + def __init__(self, status="ONLINE", warning=None): self.status = status self.executed_queries = [] self.Warning = warning From 12a95c444685382e3ed0b3ef9458cf54bba9a2dd Mon Sep 17 00:00:00 2001 From: Sebastian Pfahl Date: Tue, 15 Jul 2025 12:59:42 +0200 Subject: [PATCH 12/13] fix warning baseexception class error --- .../module_utils/test_mariadb_replication.py | 6 ++++-- .../module_utils/test_mysql_replication.py | 6 ++++-- tests/unit/plugins/utils.py | 18 +++++++++--------- 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/tests/unit/plugins/module_utils/test_mariadb_replication.py b/tests/unit/plugins/module_utils/test_mariadb_replication.py index a984d90..f5f3792 100644 --- a/tests/unit/plugins/module_utils/test_mariadb_replication.py +++ b/tests/unit/plugins/module_utils/test_mariadb_replication.py @@ -72,10 +72,11 @@ def test_stop_group_replication(): def test_start_group_replication_fail(): """Test startgroupreplication function with failure.""" from ansible_collections.community.mysql.plugins.modules.mysql_replication import startgroupreplication + from ..utils import MockWarning # Create a mock mysql_driver with a Warning attribute class MockDriver: - Warning = MockCursor.Warning + Warning = MockWarning # Save the original mysql_driver from ansible_collections.community.mysql.plugins.modules import mysql_replication @@ -101,10 +102,11 @@ def test_start_group_replication_fail(): def test_stop_group_replication_fail(): """Test stopgroupreplication function with failure.""" from ansible_collections.community.mysql.plugins.modules.mysql_replication import stopgroupreplication + from ..utils import MockWarning # Create a mock mysql_driver with a Warning attribute class MockDriver: - Warning = MockCursor.Warning + Warning = MockWarning # Save the original mysql_driver from ansible_collections.community.mysql.plugins.modules import mysql_replication diff --git a/tests/unit/plugins/module_utils/test_mysql_replication.py b/tests/unit/plugins/module_utils/test_mysql_replication.py index 6dfea29..ead1e87 100644 --- a/tests/unit/plugins/module_utils/test_mysql_replication.py +++ b/tests/unit/plugins/module_utils/test_mysql_replication.py @@ -76,10 +76,11 @@ def test_stop_group_replication(): def test_start_group_replication_fail(): """Test startgroupreplication function with failure.""" from ansible_collections.community.mysql.plugins.modules.mysql_replication import startgroupreplication + from ..utils import MockWarning # Create a mock mysql_driver with a Warning attribute class MockDriver: - Warning = MockCursor.Warning + Warning = MockWarning # Save the original mysql_driver from ansible_collections.community.mysql.plugins.modules import mysql_replication @@ -105,10 +106,11 @@ def test_start_group_replication_fail(): def test_stop_group_replication_fail(): """Test stopgroupreplication function with failure.""" from ansible_collections.community.mysql.plugins.modules.mysql_replication import stopgroupreplication + from ..utils import MockWarning # Create a mock mysql_driver with a Warning attribute class MockDriver: - Warning = MockCursor.Warning + Warning = MockWarning # Save the original mysql_driver from ansible_collections.community.mysql.plugins.modules import mysql_replication diff --git a/tests/unit/plugins/utils.py b/tests/unit/plugins/utils.py index fea9ad4..bea8514 100644 --- a/tests/unit/plugins/utils.py +++ b/tests/unit/plugins/utils.py @@ -19,22 +19,22 @@ class dummy_cursor_class(): return [self.output] -class MockCursor: - Warning = None +# Define MockWarning at module level to ensure it's a proper exception class +class MockWarning(Exception): + pass - def __init__(self, status="ONLINE", warning=None): + +class MockCursor: + # Set the Warning class at the class level + Warning = MockWarning + + def __init__(self, status="ONLINE"): self.status = status self.executed_queries = [] - self.Warning = warning def execute(self, query): self.executed_queries.append(query) if self.status == "ERROR": - # Create a custom exception that mimics mysql_driver.Warning - class MockWarning(Exception): - pass - # Make it available as a class attribute for tests to use - MockCursor.Warning = MockWarning raise MockWarning("Mocked execution error") def fetchone(self): From 9e90a23af8007876d34cc4a93453ec2c4f0b77f4 Mon Sep 17 00:00:00 2001 From: Sebastian Pfahl Date: Tue, 15 Jul 2025 23:11:08 +0200 Subject: [PATCH 13/13] fix version strings --- plugins/modules/mysql_replication.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/modules/mysql_replication.py b/plugins/modules/mysql_replication.py index ec1d1f0..e138051 100644 --- a/plugins/modules/mysql_replication.py +++ b/plugins/modules/mysql_replication.py @@ -35,8 +35,8 @@ options: C(resetprimary) (RESET MASTER) - supported since community.mysql 0.1.0, C(resetreplica) (RESET REPLICA), C(resetreplicaall) (RESET REPLICA ALL), - C(startgroupreplication) (START GROUP_REPLICATION) - supported since community.mysql 3.10.0, - C(stopgroupreplication) (STOP GROUP_REPLICATION) - supported since community.mysql 3.10.0. + C(startgroupreplication) (START GROUP_REPLICATION) - supported since community.mysql 3.15.0, + C(stopgroupreplication) (STOP GROUP_REPLICATION) - supported since community.mysql 3.15.0. type: str choices: - changeprimary @@ -200,12 +200,12 @@ options: description: - User for group replication. type: str - version_added: '3.10.0' + version_added: '3.15.0' group_replication_password: description: - Password for group replication user. type: str - version_added: '3.10.0' + version_added: '3.15.0' notes: - Compatible with MariaDB or MySQL. - If an empty value for the parameter of string type is needed, use an empty string.