Fix deprecated options from MySQL 8.2 (#662)

* Fix show master status for MySQL 8.2+

* Fix mysqldump option form --master-data to --source-data

* Fix incompatibility between mysqldump 8.0 and MySQL 8.4

Installing the same version between the client and the server makes
sense anyway. The incompatibility arise when you use mysqldump with
--source-data. The the tool tries to perform a SHOW MASTER STATUS which
is deprecated in MySQL 8.2+.

* Fix missing condition

* Fix unit tests

* Add a query resolver depending on implementation and version

* Sanity

* Fix SHOW REPLICA STATUS queries

* Fix mariadb's SHOW REPLICA HOSTS query

* Fix CHANGE MASTER for MySQL 8.0.23+

* Fix integration test for CHANGE MASTER

* Fix integration test for CHANGE MASTER

* Fix replication queries for MySQL 8.0.23+ and 8.4+

* Revert file edited by mistake

* Enhance tests format
This commit is contained in:
Laurent Indermühle 2024-08-05 08:55:18 +02:00 committed by GitHub
commit cd9f4fcf57
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 503 additions and 94 deletions

View file

@ -0,0 +1,49 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: SKS 1.1.6
Comment: Hostname: pgp.mit.edu
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
cAZUlaj3id3TxquAlud4lWDz
=h5nH
-----END PGP PUBLIC KEY BLOCK-----

View file

@ -2,6 +2,38 @@
# We use the ubuntu2204 image provided by ansible-test.
# The GPG key is imported in the files folder from:
# https://dev.mysql.com/doc/refman/8.4/en/checking-gpg-signature.html
# Downloading the key on each iteration of the tests is too slow.
- name: Install MySQL PGP public key
ansible.builtin.copy:
src: files/mysql.gpg
dest: /usr/share/keyrings/mysql.gpg
owner: root
group: root
mode: '0644'
when:
- db_engine == 'mysql'
- db_version is version('8.4', '>=')
- name: Add Apt signing key to keyring
ansible.builtin.apt_key:
id: A8D3785C
file: /usr/share/keyrings/mysql.gpg
state: present
when:
- db_engine == 'mysql'
- db_version is version('8.4', '>=')
- name: Add MySQL 8.4 repository
ansible.builtin.apt_repository:
repo: deb http://repo.mysql.com/apt/ubuntu/ jammy mysql-8.4-lts mysql-tools
state: present
filename: mysql
when:
- db_engine == 'mysql'
- db_version is version('8.4', '>=')
- name: "{{ role_name }} | Requirements | Install Linux packages"
ansible.builtin.package:
name:

View file

@ -111,11 +111,24 @@
check_implicit_admin: no
register: result
- name: Dump and Import | Assert successful completion of dump operation
- name: Dump and Import | Assert successful completion of dump operation for MariaDB and MySQL < 8.2
assert:
that:
- result is changed
- result.executed_commands[0] is search(".department --master-data=1 --skip-triggers")
when:
- >
db_engine == 'mariadb' or
(db_engine == 'mysql' and db_version is version('8.2', '<'))
- name: Dump and Import | Assert successful completion of dump operation for MySQL >= 8.2
assert:
that:
- result is changed
- result.executed_commands[0] is search(".department --source-data=1 --skip-triggers")
when:
- db_engine == 'mysql'
- db_version is version('8.2', '>=')
- name: Dump and Import | State dump/import - file name should exist (db_file_name)
file:

View file

@ -1,3 +1,4 @@
---
####################################################################
# WARNING: These are designed specifically for Ansible tests #
# and should not be used as examples of how to write Ansible roles #
@ -18,8 +19,7 @@
# Tests of channel parameter:
- import_tasks: mysql_replication_channel.yml
when:
- db_engine == 'mysql' # FIXME: mariadb introduces FOR CHANNEL in 10.7
- mysql8022_and_higher == true # FIXME: mysql 5.7 should work, but our tets fails, why?
- db_engine == 'mysql' # FIXME: mariadb introduces FOR CHANNEL in 10.7
# Tests of resetprimary mode:
- import_tasks: mysql_replication_resetprimary_mode.yml
@ -30,3 +30,4 @@
- import_tasks: mysql_replication_changereplication_mode.yml
when:
- db_engine == 'mysql'
- db_version is version('8.0.23', '>=')

View file

@ -32,10 +32,15 @@
channel: '{{ test_channel }}'
register: result
- assert:
- name: Assert that run replication with channel is changed and query matches for MariaDB and MySQL < 8.0.23
ansible.builtin.assert:
that:
- result is changed
- result.queries == result_query
when:
- >
db_engine == 'mariadb' or
(db_engine == 'mysql' and db_version is version('8.0.23', '<'))
vars:
result_query: ["CHANGE MASTER TO MASTER_HOST='{{ mysql_host }}',\
MASTER_USER='{{ replication_user }}',MASTER_PASSWORD='********',\
@ -43,6 +48,21 @@
'{{ mysql_primary_status.File }}',MASTER_LOG_POS=\
{{ mysql_primary_status.Position }} FOR CHANNEL '{{ test_channel }}'"]
- name: Assert that run replication with channel is changed and query matches for MySQL >= 8.0.23
ansible.builtin.assert:
that:
- result is changed
- result.queries == result_query
when:
- db_engine == 'mysql'
- db_version is version('8.0.23', '>=')
vars:
result_query: ["CHANGE REPLICATION SOURCE TO SOURCE_HOST='{{ mysql_host }}',\
SOURCE_USER='{{ replication_user }}',SOURCE_PASSWORD='********',\
SOURCE_PORT={{ mysql_primary_port }},SOURCE_LOG_FILE=\
'{{ mysql_primary_status.File }}',SOURCE_LOG_POS=\
{{ mysql_primary_status.Position }} FOR CHANNEL '{{ test_channel }}'"]
# Test startreplica mode:
- name: Start replica with channel
mysql_replication:
@ -83,7 +103,10 @@
mysql_host_value: '{{ mysql_host }}'
mysql_primary_port_value: '{{ mysql_primary_port }}'
test_channel_value: '{{ test_channel }}'
when: mysql8022_and_higher == false
when:
- >
db_engine == 'mariadb' or
(db_engine == 'mysql' and db_version is version('8.0.22', '<'))
- assert:
that:
@ -99,7 +122,9 @@
mysql_host_value: '{{ mysql_host }}'
mysql_primary_port_value: '{{ mysql_primary_port }}'
test_channel_value: '{{ test_channel }}'
when: mysql8022_and_higher == true
when:
- db_engine == 'mysql'
- db_version is version('8.0.22', '>=')
# Test stopreplica mode:

View file

@ -9,16 +9,6 @@
login_host: '{{ mysql_host }}'
block:
- name: Set mysql8022_and_higher
set_fact:
mysql8022_and_higher: false
- name: Set mysql8022_and_higher
set_fact:
mysql8022_and_higher: true
when:
- db_engine == 'mysql'
- db_version is version('8.0.22', '>=')
# We use iF NOT EXISTS because the GITHUB Action:
# "ansible-community/ansible-test-gh-action" uses "--retry-on-error".
@ -136,11 +126,10 @@
that:
- result is not failed
# Test changeprimary mode:
# primary_ssl_ca will be set as '' to check the module's behaviour for #23976,
# must be converted to an empty string
- name: Run replication
mysql_replication:
- name: Test changeprimary mode with empty primary_ssl_ca
community.mysql.mysql_replication:
<<: *mysql_params
login_port: '{{ mysql_replica1_port }}'
mode: changeprimary
@ -151,14 +140,18 @@
primary_log_file: '{{ mysql_primary_status.File }}'
primary_log_pos: '{{ mysql_primary_status.Position }}'
primary_ssl_ca: ''
primary_ssl: no
primary_ssl: false
register: result
- name: Assert that changeprimmary is changed and return expected query
assert:
- name: Assert that changeprimmary is changed and return expected query for MariaDB and MySQL < 8.0.23
ansible.builtin.assert:
that:
- result is changed
- result.queries == expected_queries
when:
- >
db_engine == 'mariadb' or
(db_engine == 'mysql' and db_version is version('8.0.23', '<'))
vars:
expected_queries: ["CHANGE MASTER TO MASTER_HOST='{{ mysql_host }}',\
MASTER_USER='{{ replication_user }}',MASTER_PASSWORD='********',\
@ -166,6 +159,22 @@
'{{ mysql_primary_status.File }}',MASTER_LOG_POS=\
{{ mysql_primary_status.Position }},MASTER_SSL=0,MASTER_SSL_CA=''"]
- name: Assert that changeprimmary is changed and return expected query for MySQL > 8.0.23
ansible.builtin.assert:
that:
- result is changed
- result.queries == expected_queries
when:
- db_engine == 'mysql'
- db_version is version('8.0.23', '>=')
vars:
expected_queries: ["CHANGE REPLICATION SOURCE TO \
SOURCE_HOST='{{ mysql_host }}',\
SOURCE_USER='{{ replication_user }}',SOURCE_PASSWORD='********',\
SOURCE_PORT={{ mysql_primary_port }},SOURCE_LOG_FILE=\
'{{ mysql_primary_status.File }}',SOURCE_LOG_POS=\
{{ mysql_primary_status.Position }},SOURCE_SSL=0,SOURCE_SSL_CA=''"]
# Test startreplica mode:
- name: Start replica
mysql_replication:
@ -201,7 +210,10 @@
vars:
mysql_host_value: "{{ mysql_host }}"
mysql_primary_port_value: "{{ mysql_primary_port }}"
when: mysql8022_and_higher is falsy(convert_bool=True)
when:
- >
db_engine == 'mariadb' or
(db_engine == 'mysql' and db_version is version('8.0.22', '<'))
- name: Assert that getreplica returns expected values for MySQL newer than 8.0.22
assert:
@ -216,7 +228,9 @@
vars:
mysql_host_value: "{{ mysql_host }}"
mysql_primary_port_value: "{{ mysql_primary_port }}"
when: mysql8022_and_higher is truthy(convert_bool=True)
when:
- db_engine == 'mysql'
- db_version is version('8.0.22', '>=')
# Create test table and add data to it:
- name: Create test table
@ -243,13 +257,18 @@
assert:
that:
- replica_status.Exec_Master_Log_Pos != mysql_primary_status.Position
when: mysql8022_and_higher == false
when:
- >
db_engine == 'mariadb' or
(db_engine == 'mysql' and db_version is version('8.0.22', '<'))
- name: Assert that getreplica Log_Pos is different for MySQL newer than 8.0.22
assert:
that:
- replica_status.Exec_Source_Log_Pos != mysql_primary_status.Position
when: mysql8022_and_higher == true
when:
- db_engine == 'mysql'
- db_version is version('8.0.22', '>=')
- name: Start replica that is already running
mysql_replication:

View file

@ -18,10 +18,24 @@
primary_delay: '{{ test_primary_delay }}'
register: result
- assert:
- name: Assert that run replication is changed and query match expectation for MariaDB and MySQL < 8.0.23
ansible.builtin.assert:
that:
- result is changed
- result.queries == ["CHANGE MASTER TO MASTER_DELAY=60"]
- result is changed
- result.queries == ["CHANGE MASTER TO MASTER_DELAY=60"]
when:
- >
db_engine == 'mariadb' or
(db_engine == 'mysql' and db_version is version('8.0.23', '<'))
- name: Assert that run replication is changed and query match expectation for MySQL >= 8.0.23
ansible.builtin.assert:
that:
- result is changed
- result.queries == ["CHANGE REPLICATION SOURCE TO SOURCE_DELAY=60"]
when:
- db_engine == 'mysql'
- db_version is version('8.0.23', '>=')
# Auxiliary step:
- name: Start replica

View file

@ -1,3 +1,4 @@
---
# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) <aaklychkov@mail.ru>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
@ -38,10 +39,24 @@
mode: resetprimary
register: result
- assert:
- name: Assert that reset primary is changed and query matches for MariaDB and MySQL < 8.4
ansible.builtin.assert:
that:
- result is changed
- result.queries == ["RESET MASTER"]
- result is changed
- result.queries == ["RESET MASTER"]
when:
- >
db_engine == 'mariadb' or
(db_engine == 'mysql' and db_version is version('8.4.0', '<'))
- name: Assert that reset primary is changed and query matches for MySQL > 8.4
ansible.builtin.assert:
that:
- result is changed
- result.queries == ["RESET BINARY LOGS AND GTIDS"]
when:
- db_engine == 'mysql'
- db_version is version('8.4.0', '>=')
# Get primary final status:
- name: Get primary status

View file

@ -0,0 +1,39 @@
# -*- coding: utf-8 -*-
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
import pytest
from ansible_collections.community.mysql.plugins.module_utils.command_resolver import (
CommandResolver,
)
@pytest.mark.parametrize(
'server_implementation,server_version,command,expected_output,expected_exception,expected_message',
[
('mysql', '1.0.0', 'SHOW NOTHING', '', ValueError, 'Unsupported command: SHOW NOTHING'),
('mysql', '8.0.20', 'SHOW MASTER STATUS', 'SHOW MASTER STATUS', None, None), # Case insensitive
('mysql', '8.0.20', 'show master status', 'SHOW MASTER STATUS', None, None), # Case insensitive
('mysql', '8.0.20', 'SHOW master STATUS', 'SHOW MASTER STATUS', None, None), # Case insensitive
('mysql', '8.2.0', 'SHOW MASTER STATUS', 'SHOW BINARY LOG STATUS', None, None),
('mysql', '9.0.0', 'SHOW MASTER STATUS', 'SHOW BINARY LOG STATUS', None, None),
('mariadb', '10.4.23', 'SHOW MASTER STATUS', 'SHOW MASTER STATUS', None, None), # Default
('mariadb', '10.5.1', 'SHOW MASTER STATUS', 'SHOW MASTER STATUS', None, None), # Default
('mariadb', '10.5.2', 'SHOW MASTER STATUS', 'SHOW BINLOG STATUS', None, None),
('mariadb', '10.6.17', 'SHOW MASTER STATUS', 'SHOW BINLOG STATUS', None, None),
('mysql', '8.4.1', 'CHANGE MASTER', 'CHANGE REPLICATION SOURCE', None, None),
]
)
def test_resolve_command(server_implementation, server_version, command, expected_output, expected_exception, expected_message):
"""
Tests that the CommandResolver method resolve_command return the correct query.
"""
resolver = CommandResolver(server_implementation, server_version)
if expected_exception:
with pytest.raises(expected_exception) as excinfo:
resolver.resolve_command(command)
assert str(excinfo.value) == expected_message
else:
assert resolver.resolve_command(command) == expected_output

View file

@ -14,15 +14,15 @@ from ansible_collections.community.mysql.plugins.modules.mysql_info import MySQL
@pytest.mark.parametrize(
'suffix,cursor_output,server_implementation,user_implementation',
'suffix,cursor_output,server_implementation,server_version,user_implementation',
[
('mysql', '5.5.1-mysql', 'mysql', 'mysql'),
('log', '5.7.31-log', 'mysql', 'mysql'),
('mariadb', '10.5.0-mariadb', 'mariadb', 'mariadb'),
('', '8.0.22', 'mysql', 'mysql'),
('mysql', '5.5.1-mysql', 'mysql', '5.5.1', 'mysql'),
('log', '5.7.31-log', 'mysql', '5.7.31', 'mysql'),
('mariadb', '10.5.0-mariadb', 'mariadb', '10.5.0', 'mariadb'),
('', '8.0.22', 'mysql', '8.0.22', 'mysql'),
]
)
def test_get_info_suffix(suffix, cursor_output, server_implementation, user_implementation):
def test_get_info_suffix(suffix, cursor_output, server_implementation, server_version, user_implementation):
def __cursor_return_value(input_parameter):
if input_parameter == "SHOW GLOBAL VARIABLES":
cursor.fetchall.return_value = [{"Variable_name": "version", "Value": cursor_output}]
@ -32,6 +32,6 @@ def test_get_info_suffix(suffix, cursor_output, server_implementation, user_impl
cursor = MagicMock()
cursor.execute.side_effect = __cursor_return_value
info = MySQL_Info(MagicMock(), cursor, server_implementation, user_implementation)
info = MySQL_Info(MagicMock(), cursor, server_implementation, server_version, user_implementation)
assert info.get_info([], [], False)['version']['suffix'] == suffix