Embed pymysql within the collection and use default test container

This change eliminates the need to install the connector on each
controlled node, as `pymysql` version 1.1.1 is now included. As a
result, we can safely assume its availability, thus simplifying the
testing process.

Also, I managed to remove the need for pre-built test containers. We
now use the default test containers from ansible-test.
This commit is contained in:
Laurent Indermuehle 2024-06-07 14:05:40 +02:00
commit 04af62c400
No known key found for this signature in database
GPG key ID: 93FA944C9F34DD09
49 changed files with 4392 additions and 979 deletions

View file

@ -17,64 +17,11 @@ import os
from ansible.module_utils.six.moves import configparser
from ansible.module_utils._text import to_native
try:
import pymysql as mysql_driver
_mysql_cursor_param = 'cursor'
except ImportError:
try:
# mysqlclient is called MySQLdb
import MySQLdb as mysql_driver
import MySQLdb.cursors
_mysql_cursor_param = 'cursorclass'
except ImportError:
mysql_driver = None
mysql_driver_fail_msg = ('A MySQL module is required: for Python 2.7 either PyMySQL, or '
'MySQL-python, or for Python 3.X mysqlclient or PyMySQL. '
'Consider setting ansible_python_interpreter to use '
'the intended Python version.')
from ansible_collections.community.mysql.plugins.module_utils import pymysql as mysql_driver
from ansible_collections.community.mysql.plugins.module_utils.database import mysql_quote_identifier
def get_connector_name(connector):
""" (class) -> str
Return the name of the connector (pymysql or mysqlclient (MySQLdb))
or 'Unknown' if not pymysql or MySQLdb. When adding a
connector here, also modify get_connector_version.
"""
if connector is None or not hasattr(connector, '__name__'):
return 'Unknown'
return connector.__name__
def get_connector_version(connector):
""" (class) -> str
Return the version of pymysql or mysqlclient (MySQLdb).
Return 'Unknown' if the connector name is unknown.
"""
if connector is None:
return 'Unknown'
connector_name = get_connector_name(connector)
if connector_name == 'pymysql':
# pymysql has two methods:
# - __version__ that returns the string: 0.7.11.None
# - VERSION that returns the tuple (0, 7, 11, None)
v = connector.VERSION[:3]
return '.'.join(map(str, v))
elif connector_name == 'MySQLdb':
# version_info returns the tuple (2, 1, 1, 'final', 0)
v = connector.version_info[:3]
return '.'.join(map(str, v))
else:
return 'Unknown'
def parse_from_mysql_config_file(cnf):
# Default values of comment_prefix is '#' and ';'.
# '!' added to prevent a parsing error
@ -134,38 +81,10 @@ def mysql_connect(module, login_user=None, login_password=None, config_file='',
if connect_timeout is not None:
config['connect_timeout'] = connect_timeout
if check_hostname is not None:
if get_connector_name(mysql_driver) == 'pymysql':
version_tuple = (n for n in mysql_driver.__version__.split('.') if n != 'None')
if reduce(lambda x, y: int(x) * 100 + int(y), version_tuple) >= 711:
config['ssl']['check_hostname'] = check_hostname
else:
module.fail_json(msg='To use check_hostname, pymysql >= 0.7.11 is required on the target host')
if get_connector_name(mysql_driver) == 'pymysql':
# In case of PyMySQL driver:
if mysql_driver.version_info[0] < 1:
# for PyMySQL < 1.0.0, use 'db' instead of 'database' and 'passwd' instead of 'password'
if 'database' in config:
config['db'] = config['database']
del config['database']
if 'password' in config:
config['passwd'] = config['password']
del config['password']
db_connection = mysql_driver.connect(autocommit=autocommit, **config)
else:
# In case of MySQLdb driver
if mysql_driver.version_info[0] < 2 or (mysql_driver.version_info[0] == 2 and mysql_driver.version_info[1] < 1):
# for MySQLdb < 2.1.0, use 'db' instead of 'database' and 'passwd' instead of 'password'
if 'database' in config:
config['db'] = config['database']
del config['database']
if 'password' in config:
config['passwd'] = config['password']
del config['password']
db_connection = mysql_driver.connect(**config)
if autocommit:
db_connection.autocommit(True)
config['ssl']['check_hostname'] = check_hostname
db_connection = mysql_driver.connect(autocommit=autocommit, **config)
# Monkey patch the Connection class to close the connection when garbage collected
def _conn_patch(conn_self):
conn_self.close()
@ -173,7 +92,7 @@ def mysql_connect(module, login_user=None, login_password=None, config_file='',
# Patched
if cursor_class == 'DictCursor':
return db_connection.cursor(**{_mysql_cursor_param: mysql_driver.cursors.DictCursor}), db_connection
return db_connection.cursor(**{'cursor': mysql_driver.cursors.DictCursor}), db_connection
else:
return db_connection.cursor(), db_connection