Disable hostname check (#38)

* Add changelog fragment

* Add check_hostname option

* Propagate check_hostname option across the collection

* Update documentation fragment

* Propagate test to all other plugins

* Remove stray line

* Give test user privileges to run test operations

* Extend integration tests job matrix

* Add caution note to documentation fragment.

* Update matrix job name

* Rearrange job matrix

* Fix sanity issues

* Fix issue with mysqldb silently failing to update out of range variables

* Fix variable overwrite

* Ignore `check_hostname` when using MySQLdb

* Update plugins/doc_fragments/mysql.py

Co-authored-by: Andrew Klychkov <aaklychkov@mail.ru>

* Update plugins/doc_fragments/mysql.py

Co-authored-by: Andrew Klychkov <aaklychkov@mail.ru>

* Update changelogs/fragments/35-disable-hostname-check.yml

Co-authored-by: Andrew Klychkov <aaklychkov@mail.ru>

Co-authored-by: Andrew Klychkov <aaklychkov@mail.ru>
This commit is contained in:
Jorge Rodriguez (A.K.A. Tiriel) 2020-10-12 21:19:43 +03:00 committed by GitHub
parent dc98327483
commit 8c79011dbd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
32 changed files with 680 additions and 135 deletions

View file

@ -16,6 +16,7 @@ on:
env: env:
mysql_version_file: "./ansible_collections/community/mysql/tests/integration/targets/setup_mysql/defaults/main.yml" mysql_version_file: "./ansible_collections/community/mysql/tests/integration/targets/setup_mysql/defaults/main.yml"
connector_version_file: "./ansible_collections/community/mysql/tests/integration/targets/setup_mysql/vars/main.yml"
jobs: jobs:
sanity: sanity:
@ -47,7 +48,7 @@ jobs:
working-directory: ./ansible_collections/community/mysql working-directory: ./ansible_collections/community/mysql
integration: integration:
name: "Integration (Python: ${{ matrix.python }}, Ansible: ${{ matrix.ansible }}, MySQL: ${{ matrix.mysql }})" name: "Integration (Python: ${{ matrix.python }}, Ansible: ${{ matrix.ansible }}, MySQL: ${{ matrix.mysql }}, Connector: ${{ matrix.connector }})"
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: strategy:
fail-fast: false fail-fast: false
@ -61,6 +62,13 @@ jobs:
- devel - devel
python: python:
- 3.6 - 3.6
connector:
- pymysql==0.7.10
- pymysql==0.9.3
- mysqlclient==2.0.1
exclude:
- mysql: 8.0.21
connector: pymysql==0.7.10
steps: steps:
- name: Check out code - name: Check out code
@ -79,6 +87,9 @@ jobs:
- name: Set MySQL version (${{ matrix.mysql }}) - name: Set MySQL version (${{ matrix.mysql }})
run: "sed -i 's/^mysql_version:.*/mysql_version: \"${{ matrix.mysql }}\"/g' ${{ env.mysql_version_file }}" run: "sed -i 's/^mysql_version:.*/mysql_version: \"${{ matrix.mysql }}\"/g' ${{ env.mysql_version_file }}"
- name: Set Connector version (${{ matrix.connector }})
run: "sed -i 's/^python_packages:.*/python_packages: [${{ matrix.connector }}]/' ${{ env.connector_version_file }}"
- name: Run integration tests - name: Run integration tests
run: ansible-test integration --docker -v --color --retry-on-error --continue-on-error --python ${{ matrix.python }} --diff --coverage run: ansible-test integration --docker -v --color --retry-on-error --continue-on-error --python ${{ matrix.python }} --diff --coverage
working-directory: ./ansible_collections/community/mysql working-directory: ./ansible_collections/community/mysql

View file

@ -0,0 +1,2 @@
minor_changes:
- mysql modules - add the ``check_hostname`` option (https://github.com/ansible-collections/community.mysql/issues/28).

View file

@ -62,6 +62,14 @@ options:
- The path to the client private key. - The path to the client private key.
type: path type: path
aliases: [ ssl_key ] aliases: [ ssl_key ]
check_hostname:
description:
- Whether to validate the server host name when an SSL connection is required.
- Setting this to C(false) disables hostname verification. Use with caution.
- Requires pymysql >= 0.7.11.
- This optoin has no effect on MySQLdb.
type: bool
version_added: '1.1.0'
requirements: requirements:
- PyMySQL (Python 2.7 and Python 3.X), or - PyMySQL (Python 2.7 and Python 3.X), or
- MySQLdb (Python 2.x) - MySQLdb (Python 2.x)

View file

@ -10,6 +10,7 @@
# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause) # Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
from __future__ import (absolute_import, division, print_function) from __future__ import (absolute_import, division, print_function)
from functools import reduce
__metaclass__ = type __metaclass__ = type
import os import os
@ -37,8 +38,8 @@ def parse_from_mysql_config_file(cnf):
def mysql_connect(module, login_user=None, login_password=None, config_file='', ssl_cert=None, def mysql_connect(module, login_user=None, login_password=None, config_file='', ssl_cert=None,
ssl_key=None, ssl_ca=None, db=None, cursor_class=None, ssl_key=None, ssl_ca=None, db=None, cursor_class=None, connect_timeout=30,
connect_timeout=30, autocommit=False, config_overrides_defaults=False): autocommit=False, config_overrides_defaults=False, check_hostname=None):
config = {} config = {}
if config_file and os.path.exists(config_file): if config_file and os.path.exists(config_file):
@ -51,10 +52,10 @@ def mysql_connect(module, login_user=None, login_password=None, config_file='',
module.params['login_port'] = cp.getint('client', 'port', fallback=module.params['login_port']) module.params['login_port'] = cp.getint('client', 'port', fallback=module.params['login_port'])
except Exception as e: except Exception as e:
if "got an unexpected keyword argument 'fallback'" in e.message: if "got an unexpected keyword argument 'fallback'" in e.message:
module.fail_json('To use config_overrides_defaults, ' module.fail_json(msg='To use config_overrides_defaults, '
'it needs Python 3.5+ as the default interpreter on a target host') 'it needs Python 3.5+ as the default interpreter on a target host')
if ssl_ca is not None or ssl_key is not None or ssl_cert is not None: if ssl_ca is not None or ssl_key is not None or ssl_cert is not None or check_hostname is not None:
config['ssl'] = {} config['ssl'] = {}
if module.params['login_unix_socket']: if module.params['login_unix_socket']:
@ -79,6 +80,13 @@ def mysql_connect(module, login_user=None, login_password=None, config_file='',
config['db'] = db config['db'] = db
if connect_timeout is not None: if connect_timeout is not None:
config['connect_timeout'] = connect_timeout config['connect_timeout'] = connect_timeout
if check_hostname is not None:
if mysql_driver.__name__ == "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 _mysql_cursor_param == 'cursor': if _mysql_cursor_param == 'cursor':
# In case of PyMySQL driver: # In case of PyMySQL driver:
@ -113,4 +121,5 @@ def mysql_common_argument_spec():
client_cert=dict(type='path', aliases=['ssl_cert']), client_cert=dict(type='path', aliases=['ssl_cert']),
client_key=dict(type='path', aliases=['ssl_key']), client_key=dict(type='path', aliases=['ssl_key']),
ca_cert=dict(type='path', aliases=['ssl_ca']), ca_cert=dict(type='path', aliases=['ssl_ca']),
check_hostname=dict(type='bool', default=None),
) )

View file

@ -318,7 +318,7 @@ import traceback
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.mysql.plugins.module_utils.database import mysql_quote_identifier from ansible_collections.community.mysql.plugins.module_utils.database import mysql_quote_identifier
from ansible_collections.community.mysql.plugins.module_utils.mysql import mysql_connect, mysql_driver, mysql_driver_fail_msg from ansible_collections.community.mysql.plugins.module_utils.mysql import mysql_connect, mysql_driver, mysql_driver_fail_msg, mysql_common_argument_spec
from ansible.module_utils.six.moves import shlex_quote from ansible.module_utils.six.moves import shlex_quote
from ansible.module_utils._text import to_native from ansible.module_utils._text import to_native
@ -543,23 +543,13 @@ def db_create(cursor, db, encoding, collation):
def main(): def main():
module = AnsibleModule( argument_spec = mysql_common_argument_spec()
argument_spec=dict( argument_spec.update(
login_user=dict(type='str'),
login_password=dict(type='str', no_log=True),
login_host=dict(type='str', default='localhost'),
login_port=dict(type='int', default=3306),
login_unix_socket=dict(type='str'),
name=dict(type='list', required=True, aliases=['db']), name=dict(type='list', required=True, aliases=['db']),
encoding=dict(type='str', default=''), encoding=dict(type='str', default=''),
collation=dict(type='str', default=''), collation=dict(type='str', default=''),
target=dict(type='path'), target=dict(type='path'),
state=dict(type='str', default='present', choices=['absent', 'dump', 'import', 'present']), state=dict(type='str', default='present', choices=['absent', 'dump', 'import', 'present']),
client_cert=dict(type='path', aliases=['ssl_cert']),
client_key=dict(type='path', aliases=['ssl_key']),
ca_cert=dict(type='path', aliases=['ssl_ca']),
connect_timeout=dict(type='int', default=30),
config_file=dict(type='path', default='~/.my.cnf'),
single_transaction=dict(type='bool', default=False), single_transaction=dict(type='bool', default=False),
quick=dict(type='bool', default=True), quick=dict(type='bool', default=True),
ignore_tables=dict(type='list', default=[]), ignore_tables=dict(type='list', default=[]),
@ -569,11 +559,14 @@ def main():
skip_lock_tables=dict(type='bool', default=False), skip_lock_tables=dict(type='bool', default=False),
dump_extra_args=dict(type='str'), dump_extra_args=dict(type='str'),
use_shell=dict(type='bool', default=False), use_shell=dict(type='bool', default=False),
unsafe_login_password=dict(type='bool', default=False, no_log=True), unsafe_login_password=dict(type='bool', default=False),
restrict_config_file=dict(type='bool', default=False), restrict_config_file=dict(type='bool', default=False),
check_implicit_admin=dict(type='bool', default=False), check_implicit_admin=dict(type='bool', default=False),
config_overrides_defaults=dict(type='bool', default=False), config_overrides_defaults=dict(type='bool', default=False),
), )
module = AnsibleModule(
argument_spec=argument_spec,
supports_check_mode=True, supports_check_mode=True,
) )
@ -596,6 +589,7 @@ def main():
ssl_cert = module.params["client_cert"] ssl_cert = module.params["client_cert"]
ssl_key = module.params["client_key"] ssl_key = module.params["client_key"]
ssl_ca = module.params["ca_cert"] ssl_ca = module.params["ca_cert"]
check_hostname = module.params["check_hostname"]
connect_timeout = module.params['connect_timeout'] connect_timeout = module.params['connect_timeout']
config_file = module.params['config_file'] config_file = module.params['config_file']
login_password = module.params["login_password"] login_password = module.params["login_password"]
@ -636,7 +630,7 @@ def main():
if check_implicit_admin: if check_implicit_admin:
try: try:
cursor, db_conn = mysql_connect(module, 'root', '', config_file, ssl_cert, ssl_key, ssl_ca, cursor, db_conn = mysql_connect(module, 'root', '', config_file, ssl_cert, ssl_key, ssl_ca,
connect_timeout=connect_timeout, connect_timeout=connect_timeout, check_hostname=check_hostname,
config_overrides_defaults=config_overrides_defaults) config_overrides_defaults=config_overrides_defaults)
except Exception as e: except Exception as e:
check_implicit_admin = False check_implicit_admin = False
@ -644,7 +638,8 @@ def main():
if not cursor: if not cursor:
cursor, db_conn = mysql_connect(module, login_user, login_password, config_file, ssl_cert, ssl_key, ssl_ca, cursor, db_conn = mysql_connect(module, login_user, login_password, config_file, ssl_cert, ssl_key, ssl_ca,
connect_timeout=connect_timeout, config_overrides_defaults=config_overrides_defaults) connect_timeout=connect_timeout, config_overrides_defaults=config_overrides_defaults,
check_hostname=check_hostname)
except Exception as e: except Exception as e:
if os.path.exists(config_file): if os.path.exists(config_file):
module.fail_json(msg="unable to connect to database, check login_user and login_password are correct or %s has the credentials. " module.fail_json(msg="unable to connect to database, check login_user and login_password are correct or %s has the credentials. "

View file

@ -509,6 +509,7 @@ def main():
ssl_cert = module.params['client_cert'] ssl_cert = module.params['client_cert']
ssl_key = module.params['client_key'] ssl_key = module.params['client_key']
ssl_ca = module.params['ca_cert'] ssl_ca = module.params['ca_cert']
check_hostname = module.params['check_hostname']
config_file = module.params['config_file'] config_file = module.params['config_file']
filter_ = module.params['filter'] filter_ = module.params['filter']
exclude_fields = module.params['exclude_fields'] exclude_fields = module.params['exclude_fields']
@ -526,6 +527,7 @@ def main():
try: try:
cursor, db_conn = mysql_connect(module, login_user, login_password, cursor, db_conn = mysql_connect(module, login_user, login_password,
config_file, ssl_cert, ssl_key, ssl_ca, db, config_file, ssl_cert, ssl_key, ssl_ca, db,
check_hostname=check_hostname,
connect_timeout=connect_timeout, cursor_class='DictCursor') connect_timeout=connect_timeout, cursor_class='DictCursor')
except Exception as e: except Exception as e:
module.fail_json(msg="unable to connect to database, check login_user and login_password are correct or %s has the credentials. " module.fail_json(msg="unable to connect to database, check login_user and login_password are correct or %s has the credentials. "

View file

@ -144,6 +144,7 @@ def main():
ssl_cert = module.params['client_cert'] ssl_cert = module.params['client_cert']
ssl_key = module.params['client_key'] ssl_key = module.params['client_key']
ssl_ca = module.params['ca_cert'] ssl_ca = module.params['ca_cert']
check_hostname = module.params['check_hostname']
config_file = module.params['config_file'] config_file = module.params['config_file']
query = module.params["query"] query = module.params["query"]
if module.params["single_transaction"]: if module.params["single_transaction"]:
@ -165,12 +166,14 @@ def main():
try: try:
cursor, db_connection = mysql_connect(module, login_user, login_password, cursor, db_connection = mysql_connect(module, login_user, login_password,
config_file, ssl_cert, ssl_key, ssl_ca, db, config_file, ssl_cert, ssl_key, ssl_ca, db,
check_hostname=check_hostname,
connect_timeout=connect_timeout, connect_timeout=connect_timeout,
cursor_class='DictCursor', autocommit=autocommit) cursor_class='DictCursor', autocommit=autocommit)
except Exception as e: except Exception as e:
module.fail_json(msg="unable to connect to database, check login_user and " module.fail_json(msg="unable to connect to database, check login_user and "
"login_password are correct or %s has the credentials. " "login_password are correct or %s has the credentials. "
"Exception message: %s" % (config_file, to_native(e))) "Exception message: %s" % (config_file, to_native(e)))
# Set defaults: # Set defaults:
changed = False changed = False
@ -210,7 +213,11 @@ def main():
if keyword in q: if keyword in q:
changed = True changed = True
try:
executed_queries.append(cursor._last_executed) executed_queries.append(cursor._last_executed)
except AttributeError:
# MySQLdb removed cursor._last_executed as a duplicate of cursor._executed
executed_queries.append(cursor._executed)
rowcount.append(cursor.rowcount) rowcount.append(cursor.rowcount)
# When the module run with the single_transaction == True: # When the module run with the single_transaction == True:

View file

@ -238,7 +238,7 @@ import os
import warnings import warnings
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.mysql.plugins.module_utils.mysql import mysql_connect, mysql_driver, mysql_driver_fail_msg from ansible_collections.community.mysql.plugins.module_utils.mysql import mysql_connect, mysql_driver, mysql_driver_fail_msg, mysql_common_argument_spec
from ansible.module_utils._text import to_native from ansible.module_utils._text import to_native
executed_queries = [] executed_queries = []
@ -381,13 +381,8 @@ def changemaster(cursor, chm, connection_name='', channel=''):
def main(): def main():
module = AnsibleModule( argument_spec = mysql_common_argument_spec()
argument_spec=dict( argument_spec.update(
login_user=dict(type='str'),
login_password=dict(type='str', no_log=True),
login_host=dict(type='str', default='localhost'),
login_port=dict(type='int', default=3306),
login_unix_socket=dict(type='str'),
mode=dict(type='str', default='getslave', choices=[ mode=dict(type='str', default='getslave', choices=[
'getmaster', 'getslave', 'changemaster', 'stopslave', 'getmaster', 'getslave', 'changemaster', 'stopslave',
'startslave', 'resetmaster', 'resetslave', 'resetslaveall']), 'startslave', 'resetmaster', 'resetslave', 'resetslaveall']),
@ -407,17 +402,14 @@ def main():
master_ssl_cert=dict(type='str'), master_ssl_cert=dict(type='str'),
master_ssl_key=dict(type='str'), master_ssl_key=dict(type='str'),
master_ssl_cipher=dict(type='str'), master_ssl_cipher=dict(type='str'),
connect_timeout=dict(type='int', default=30),
config_file=dict(type='path', default='~/.my.cnf'),
client_cert=dict(type='path', aliases=['ssl_cert']),
client_key=dict(type='path', aliases=['ssl_key']),
ca_cert=dict(type='path', aliases=['ssl_ca']),
master_use_gtid=dict(type='str', choices=['current_pos', 'slave_pos', 'disabled']), master_use_gtid=dict(type='str', choices=['current_pos', 'slave_pos', 'disabled']),
master_delay=dict(type='int'), master_delay=dict(type='int'),
connection_name=dict(type='str'), connection_name=dict(type='str'),
channel=dict(type='str'), channel=dict(type='str'),
fail_on_error=dict(type='bool', default=False), fail_on_error=dict(type='bool', default=False),
), )
module = AnsibleModule(
argument_spec=argument_spec,
mutually_exclusive=[ mutually_exclusive=[
['connection_name', 'channel'] ['connection_name', 'channel']
], ],
@ -442,6 +434,7 @@ def main():
ssl_cert = module.params["client_cert"] ssl_cert = module.params["client_cert"]
ssl_key = module.params["client_key"] ssl_key = module.params["client_key"]
ssl_ca = module.params["ca_cert"] ssl_ca = module.params["ca_cert"]
check_hostname = module.params["check_hostname"]
connect_timeout = module.params['connect_timeout'] connect_timeout = module.params['connect_timeout']
config_file = module.params['config_file'] config_file = module.params['config_file']
master_delay = module.params['master_delay'] master_delay = module.params['master_delay']
@ -464,7 +457,7 @@ def main():
try: try:
cursor, db_conn = mysql_connect(module, login_user, login_password, config_file, cursor, db_conn = mysql_connect(module, login_user, login_password, config_file,
ssl_cert, ssl_key, ssl_ca, None, cursor_class='DictCursor', ssl_cert, ssl_key, ssl_ca, None, cursor_class='DictCursor',
connect_timeout=connect_timeout) connect_timeout=connect_timeout, check_hostname=check_hostname)
except Exception as e: except Exception as e:
if os.path.exists(config_file): if os.path.exists(config_file):
module.fail_json(msg="unable to connect to database, check login_user and login_password are correct or %s has the credentials. " module.fail_json(msg="unable to connect to database, check login_user and login_password are correct or %s has the credentials. "

View file

@ -297,7 +297,7 @@ import string
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.mysql.plugins.module_utils.database import SQLParseError from ansible_collections.community.mysql.plugins.module_utils.database import SQLParseError
from ansible_collections.community.mysql.plugins.module_utils.mysql import mysql_connect, mysql_driver, mysql_driver_fail_msg from ansible_collections.community.mysql.plugins.module_utils.mysql import mysql_connect, mysql_driver, mysql_driver_fail_msg, mysql_common_argument_spec
from ansible.module_utils.six import iteritems from ansible.module_utils.six import iteritems
from ansible.module_utils._text import to_native from ansible.module_utils._text import to_native
@ -996,13 +996,8 @@ def limit_resources(module, cursor, user, host, resource_limits, check_mode):
def main(): def main():
module = AnsibleModule( argument_spec = mysql_common_argument_spec()
argument_spec=dict( argument_spec.update(
login_user=dict(type='str'),
login_password=dict(type='str', no_log=True),
login_host=dict(type='str', default='localhost'),
login_port=dict(type='int', default=3306),
login_unix_socket=dict(type='str'),
user=dict(type='str', required=True, aliases=['name']), user=dict(type='str', required=True, aliases=['name']),
password=dict(type='str', no_log=True), password=dict(type='str', no_log=True),
encrypted=dict(type='bool', default=False), encrypted=dict(type='bool', default=False),
@ -1014,17 +1009,14 @@ def main():
append_privs=dict(type='bool', default=False), append_privs=dict(type='bool', default=False),
check_implicit_admin=dict(type='bool', default=False), check_implicit_admin=dict(type='bool', default=False),
update_password=dict(type='str', default='always', choices=['always', 'on_create'], no_log=False), update_password=dict(type='str', default='always', choices=['always', 'on_create'], no_log=False),
connect_timeout=dict(type='int', default=30),
config_file=dict(type='path', default='~/.my.cnf'),
sql_log_bin=dict(type='bool', default=True), sql_log_bin=dict(type='bool', default=True),
client_cert=dict(type='path', aliases=['ssl_cert']),
client_key=dict(type='path', aliases=['ssl_key']),
ca_cert=dict(type='path', aliases=['ssl_ca']),
plugin=dict(default=None, type='str'), plugin=dict(default=None, type='str'),
plugin_hash_string=dict(default=None, type='str'), plugin_hash_string=dict(default=None, type='str'),
plugin_auth_string=dict(default=None, type='str'), plugin_auth_string=dict(default=None, type='str'),
resource_limits=dict(type='dict'), resource_limits=dict(type='dict'),
), )
module = AnsibleModule(
argument_spec=argument_spec,
supports_check_mode=True, supports_check_mode=True,
) )
login_user = module.params["login_user"] login_user = module.params["login_user"]
@ -1045,6 +1037,7 @@ def main():
ssl_cert = module.params["client_cert"] ssl_cert = module.params["client_cert"]
ssl_key = module.params["client_key"] ssl_key = module.params["client_key"]
ssl_ca = module.params["ca_cert"] ssl_ca = module.params["ca_cert"]
check_hostname = module.params["check_hostname"]
db = '' db = ''
sql_log_bin = module.params["sql_log_bin"] sql_log_bin = module.params["sql_log_bin"]
plugin = module.params["plugin"] plugin = module.params["plugin"]
@ -1065,13 +1058,13 @@ def main():
if check_implicit_admin: if check_implicit_admin:
try: try:
cursor, db_conn = mysql_connect(module, "root", "", config_file, ssl_cert, ssl_key, ssl_ca, db, cursor, db_conn = mysql_connect(module, "root", "", config_file, ssl_cert, ssl_key, ssl_ca, db,
connect_timeout=connect_timeout) connect_timeout=connect_timeout, check_hostname=check_hostname)
except Exception: except Exception:
pass pass
if not cursor: if not cursor:
cursor, db_conn = mysql_connect(module, login_user, login_password, config_file, ssl_cert, ssl_key, ssl_ca, db, cursor, db_conn = mysql_connect(module, login_user, login_password, config_file, ssl_cert, ssl_key, ssl_ca, db,
connect_timeout=connect_timeout) connect_timeout=connect_timeout, check_hostname=check_hostname)
except Exception as e: except Exception as e:
module.fail_json(msg="unable to connect to database, check login_user and login_password are correct or %s has the credentials. " module.fail_json(msg="unable to connect to database, check login_user and login_password are correct or %s has the credentials. "
"Exception message: %s" % (config_file, to_native(e))) "Exception message: %s" % (config_file, to_native(e)))

View file

@ -82,7 +82,7 @@ from re import match
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.mysql.plugins.module_utils.database import SQLParseError, mysql_quote_identifier from ansible_collections.community.mysql.plugins.module_utils.database import SQLParseError, mysql_quote_identifier
from ansible_collections.community.mysql.plugins.module_utils.mysql import mysql_connect, mysql_driver, mysql_driver_fail_msg from ansible_collections.community.mysql.plugins.module_utils.mysql import mysql_connect, mysql_driver, mysql_driver_fail_msg, mysql_common_argument_spec
from ansible.module_utils._text import to_native from ansible.module_utils._text import to_native
executed_queries = [] executed_queries = []
@ -168,22 +168,15 @@ def setvariable(cursor, mysqlvar, value, mode='global'):
def main(): def main():
module = AnsibleModule( argument_spec = mysql_common_argument_spec()
argument_spec=dict( argument_spec.update(
login_user=dict(type='str'),
login_password=dict(type='str', no_log=True),
login_host=dict(type='str', default='localhost'),
login_port=dict(type='int', default=3306),
login_unix_socket=dict(type='str'),
variable=dict(type='str'), variable=dict(type='str'),
value=dict(type='str'), value=dict(type='str'),
client_cert=dict(type='path', aliases=['ssl_cert']),
client_key=dict(type='path', aliases=['ssl_key']),
ca_cert=dict(type='path', aliases=['ssl_ca']),
connect_timeout=dict(type='int', default=30),
config_file=dict(type='path', default='~/.my.cnf'),
mode=dict(type='str', choices=['global', 'persist', 'persist_only'], default='global'), mode=dict(type='str', choices=['global', 'persist', 'persist_only'], default='global'),
), )
module = AnsibleModule(
argument_spec=argument_spec
) )
user = module.params["login_user"] user = module.params["login_user"]
password = module.params["login_password"] password = module.params["login_password"]
@ -191,6 +184,7 @@ def main():
ssl_cert = module.params["client_cert"] ssl_cert = module.params["client_cert"]
ssl_key = module.params["client_key"] ssl_key = module.params["client_key"]
ssl_ca = module.params["ca_cert"] ssl_ca = module.params["ca_cert"]
check_hostname = module.params["check_hostname"]
config_file = module.params['config_file'] config_file = module.params['config_file']
db = 'mysql' db = 'mysql'
@ -209,7 +203,7 @@ def main():
try: try:
cursor, db_conn = mysql_connect(module, user, password, config_file, ssl_cert, ssl_key, ssl_ca, db, cursor, db_conn = mysql_connect(module, user, password, config_file, ssl_cert, ssl_key, ssl_ca, db,
connect_timeout=connect_timeout) connect_timeout=connect_timeout, check_hostname=check_hostname)
except Exception as e: except Exception as e:
if os.path.exists(config_file): if os.path.exists(config_file):
module.fail_json(msg=("unable to connect to database, check login_user and " module.fail_json(msg=("unable to connect to database, check login_user and "

View file

@ -18,6 +18,13 @@
environment: environment:
DEBIAN_FRONTEND: noninteractive DEBIAN_FRONTEND: noninteractive
- name: "{{ role_name }} | install | install packages required by mysql connector"
apt:
name: "{{ install_python_prereqs }}"
state: present
environment:
DEBIAN_FRONTEND: noninteractive
- name: "{{ role_name }} | install | install python packages" - name: "{{ role_name }} | install | install python packages"
pip: pip:
name: "{{ python_packages }}" name: "{{ python_packages }}"

View file

@ -14,13 +14,17 @@ percona_mysql_repos:
percona_mysql_packages: percona_mysql_packages:
- percona-server-client-{{ percona_client_version }} - percona-server-client-{{ percona_client_version }}
python_packages: python_packages: [pymysql == 0.9.3]
- pymysql==0.9.3 # temporary fix pinning to 0.9.3 to ensure warnings are handled (https://github.com/ansible-collections/community.mysql/pull/9#issuecomment-663040948)
install_prereqs: install_prereqs:
- libaio1 - libaio1
- libnuma1 - libnuma1
install_python_prereqs:
- python3-dev
- default-libmysqlclient-dev
- build-essential
mysql_tarball: "mysql-{{ mysql_version }}-linux-glibc2.12-x86_64.tar.{{ mysql_compression_extension }}" mysql_tarball: "mysql-{{ mysql_version }}-linux-glibc2.12-x86_64.tar.{{ mysql_compression_extension }}"
mysql_src: "https://dev.mysql.com/get/Downloads/MySQL-{{ mysql_major_version }}/{{ mysql_tarball }}" mysql_src: "https://dev.mysql.com/get/Downloads/MySQL-{{ mysql_major_version }}/{{ mysql_tarball }}"
mariadb_tarball: "mariadb-{{ mariadb_version }}-linux-x86_64.tar.gz" mariadb_tarball: "mariadb-{{ mariadb_version }}-linux-x86_64.tar.gz"

View file

@ -12,3 +12,6 @@ db_user2: 'datauser2'
tmp_dir: '/tmp' tmp_dir: '/tmp'
db_latin1_name: 'db_latin1' db_latin1_name: 'db_latin1'
file4: 'latin1_file' file4: 'latin1_file'
user_name_1: 'db_user1'
user_password_1: 'gadfFDSdtTU^Sdfuj'

View file

@ -71,7 +71,7 @@
assert: assert:
that: that:
- result is failed - result is failed
- result.msg is search("Can't connect to MySQL server on '{{ fake_host }}'") - result.msg is search("Can't connect to MySQL server on '{{ fake_host }}'") or result.msg is search("Unknown MySQL server host '{{ fake_host }}'")
# Clean up # Clean up
- name: Remove test db - name: Remove test db

View file

@ -0,0 +1,81 @@
---
- vars:
mysql_parameters: &mysql_params
login_user: '{{ mysql_user }}'
login_password: '{{ mysql_password }}'
login_host: 127.0.0.1
login_port: '{{ mysql_primary_port }}'
block:
# ============================================================
- shell: pip show pymysql | awk '/Version/ {print $2}'
register: pymysql_version
- name: get server certificate
copy:
content: "{{ lookup('pipe', \"openssl s_client -starttls mysql -connect localhost:3307 -showcerts 2>/dev/null </dev/null | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p'\") }}"
dest: /tmp/cert.pem
delegate_to: localhost
- name: Drop mysql user if exists
mysql_user:
<<: *mysql_params
name: '{{ user_name_1 }}'
state: absent
ignore_errors: yes
- name: create user with ssl requirement
mysql_user:
<<: *mysql_params
name: "{{ user_name_1 }}"
password: "{{ user_password_1 }}"
priv: '*.*:ALL,GRANT'
tls_requires:
SSL:
- name: attempt connection with newly created user (expect failure)
mysql_db:
name: '{{ db_name }}'
state: absent
login_user: '{{ user_name_1 }}'
login_password: '{{ user_password_1 }}'
login_host: 127.0.0.1
login_port: '{{ mysql_primary_port }}'
ca_cert: /tmp/cert.pem
register: result
ignore_errors: yes
- assert:
that:
- result is failed
when: pymysql_version.stdout != ""
- assert:
that:
- result is succeeded
when: pymysql_version.stdout == ""
- name: attempt connection with newly created user ignoring hostname
mysql_db:
name: '{{ db_name }}'
state: absent
login_user: '{{ user_name_1 }}'
login_password: '{{ user_password_1 }}'
login_host: 127.0.0.1
login_port: '{{ mysql_primary_port }}'
ca_cert: /tmp/cert.pem
check_hostname: no
register: result
ignore_errors: yes
- assert:
that:
- result is succeeded or 'pymysql >= 0.7.11 is required' in result.msg
- name: Drop mysql user
mysql_user:
<<: *mysql_params
name: '{{ user_name_1 }}'
host: 127.0.0.1
state: absent

View file

@ -322,3 +322,5 @@
- include: config_overrides_defaults.yml - include: config_overrides_defaults.yml
when: ansible_python.version_info[0] >= 3 when: ansible_python.version_info[0] >= 3
- include: issue-28.yml

View file

@ -6,3 +6,6 @@ mysql_host: 127.0.0.1
mysql_primary_port: 3307 mysql_primary_port: 3307
db_name: data db_name: data
user_name_1: 'db_user1'
user_password_1: 'gadfFDSdtTU^Sdfuj'

View file

@ -0,0 +1,78 @@
---
- vars:
mysql_parameters: &mysql_params
login_user: '{{ mysql_user }}'
login_password: '{{ mysql_password }}'
login_host: 127.0.0.1
login_port: '{{ mysql_primary_port }}'
block:
# ============================================================
- shell: pip show pymysql | awk '/Version/ {print $2}'
register: pymysql_version
- name: get server certificate
copy:
content: "{{ lookup('pipe', \"openssl s_client -starttls mysql -connect localhost:3307 -showcerts 2>/dev/null </dev/null | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p'\") }}"
dest: /tmp/cert.pem
delegate_to: localhost
- name: Drop mysql user if exists
mysql_user:
<<: *mysql_params
name: '{{ user_name_1 }}'
state: absent
ignore_errors: yes
- name: create user with ssl requirement
mysql_user:
<<: *mysql_params
name: "{{ user_name_1 }}"
password: "{{ user_password_1 }}"
tls_requires:
SSL:
- name: attempt connection with newly created user (expect failure)
mysql_info:
filter: version
login_user: '{{ user_name_1 }}'
login_password: '{{ user_password_1 }}'
login_host: 127.0.0.1
login_port: '{{ mysql_primary_port }}'
ca_cert: /tmp/cert.pem
register: result
ignore_errors: yes
- assert:
that:
- result is failed
when: pymysql_version.stdout != ""
- assert:
that:
- result is succeeded
when: pymysql_version.stdout == ""
- name: attempt connection with newly created user ignoring hostname
mysql_info:
filter: version
login_user: '{{ user_name_1 }}'
login_password: '{{ user_password_1 }}'
login_host: 127.0.0.1
login_port: '{{ mysql_primary_port }}'
ca_cert: /tmp/cert.pem
check_hostname: no
register: result
ignore_errors: yes
- assert:
that:
- result is succeeded or 'pymysql >= 0.7.11 is required' in result.msg
- name: Drop mysql user
mysql_user:
<<: *mysql_params
name: '{{ user_name_1 }}'
host: 127.0.0.1
state: absent

View file

@ -189,3 +189,5 @@
<<: *mysql_params <<: *mysql_params
name: acme name: acme
state: absent state: absent
- include: issue-28.yml

View file

@ -7,3 +7,6 @@ test_db: testdb
test_table1: test1 test_table1: test1
test_table2: test2 test_table2: test2
test_script_path: /tmp/test.sql test_script_path: /tmp/test.sql
user_name_1: 'db_user1'
user_password_1: 'gadfFDSdtTU^Sdfuj'

View file

@ -0,0 +1,78 @@
---
- vars:
mysql_parameters: &mysql_params
login_user: '{{ mysql_user }}'
login_password: '{{ mysql_password }}'
login_host: 127.0.0.1
login_port: '{{ mysql_primary_port }}'
block:
# ============================================================
- shell: pip show pymysql | awk '/Version/ {print $2}'
register: pymysql_version
- name: get server certificate
copy:
content: "{{ lookup('pipe', \"openssl s_client -starttls mysql -connect localhost:3307 -showcerts 2>/dev/null </dev/null | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p'\") }}"
dest: /tmp/cert.pem
delegate_to: localhost
- name: Drop mysql user if exists
mysql_user:
<<: *mysql_params
name: '{{ user_name_1 }}'
state: absent
ignore_errors: yes
- name: create user with ssl requirement
mysql_user:
<<: *mysql_params
name: "{{ user_name_1 }}"
password: "{{ user_password_1 }}"
tls_requires:
SSL:
- name: attempt connection with newly created user (expect failure)
mysql_query:
query: 'SHOW DATABASES'
login_user: '{{ user_name_1 }}'
login_password: '{{ user_password_1 }}'
login_host: 127.0.0.1
login_port: '{{ mysql_primary_port }}'
ca_cert: /tmp/cert.pem
register: result
ignore_errors: yes
- assert:
that:
- result is failed
when: pymysql_version.stdout != ""
- assert:
that:
- result is succeeded
when: pymysql_version.stdout == ""
- name: attempt connection with newly created user ignoring hostname
mysql_query:
query: 'SHOW DATABASES'
login_user: '{{ user_name_1 }}'
login_password: '{{ user_password_1 }}'
login_host: 127.0.0.1
login_port: '{{ mysql_primary_port }}'
ca_cert: /tmp/cert.pem
check_hostname: no
register: result
ignore_errors: yes
- assert:
that:
- result is succeeded or 'pymysql >= 0.7.11 is required' in result.msg
- name: Drop mysql user
mysql_user:
<<: *mysql_params
name: '{{ user_name_1 }}'
host: 127.0.0.1
state: absent

View file

@ -5,3 +5,5 @@
# mysql_query module initial CI tests # mysql_query module initial CI tests
- import_tasks: mysql_query_initial.yml - import_tasks: mysql_query_initial.yml
- include: issue-28.yml

View file

@ -12,3 +12,6 @@ replication_user: replication_user
replication_pass: replication_pass replication_pass: replication_pass
dump_path: /tmp/dump.sql dump_path: /tmp/dump.sql
test_channel: test_channel-1 test_channel: test_channel-1
user_name_1: 'db_user1'
user_password_1: 'gadfFDSdtTU^Sdfuj'

View file

@ -0,0 +1,79 @@
---
- vars:
mysql_parameters: &mysql_params
login_user: '{{ mysql_user }}'
login_password: '{{ mysql_password }}'
login_host: 127.0.0.1
login_port: '{{ mysql_primary_port }}'
block:
# ============================================================
- shell: pip show pymysql | awk '/Version/ {print $2}'
register: pymysql_version
- name: get server certificate
copy:
content: "{{ lookup('pipe', \"openssl s_client -starttls mysql -connect localhost:3307 -showcerts 2>/dev/null </dev/null | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p'\") }}"
dest: /tmp/cert.pem
delegate_to: localhost
- name: Drop mysql user if exists
mysql_user:
<<: *mysql_params
name: '{{ user_name_1 }}'
state: absent
ignore_errors: yes
- name: create user with ssl requirement
mysql_user:
<<: *mysql_params
name: "{{ user_name_1 }}"
password: "{{ user_password_1 }}"
priv: '*.*:ALL,GRANT'
tls_requires:
SSL:
- name: attempt connection with newly created user (expect failure)
mysql_replication:
mode: getmaster
login_user: '{{ user_name_1 }}'
login_password: '{{ user_password_1 }}'
login_host: 127.0.0.1
login_port: '{{ mysql_primary_port }}'
ca_cert: /tmp/cert.pem
register: result
ignore_errors: yes
- assert:
that:
- result is failed
when: pymysql_version.stdout != ""
- assert:
that:
- result is succeeded
when: pymysql_version.stdout == ""
- name: attempt connection with newly created user ignoring hostname
mysql_replication:
mode: getmaster
login_user: '{{ user_name_1 }}'
login_password: '{{ user_password_1 }}'
login_host: 127.0.0.1
login_port: '{{ mysql_primary_port }}'
ca_cert: /tmp/cert.pem
check_hostname: no
register: result
ignore_errors: yes
- assert:
that:
- result is succeeded or 'pymysql >= 0.7.11 is required' in result.msg
- name: Drop mysql user
mysql_user:
<<: *mysql_params
name: '{{ user_name_1 }}'
host: 127.0.0.1
state: absent

View file

@ -17,3 +17,5 @@
# Tests of resetmaster mode: # Tests of resetmaster mode:
- import_tasks: mysql_replication_resetmaster_mode.yml - import_tasks: mysql_replication_resetmaster_mode.yml
- include: issue-28.yml

View file

@ -165,6 +165,9 @@
that: that:
- slave_status.Exec_Master_Log_Pos != mysql_primary_status.Position - slave_status.Exec_Master_Log_Pos != mysql_primary_status.Position
- shell: pip show pymysql | awk '/Version/ {print $2}'
register: pymysql_version
- name: Start slave that is already running - name: Start slave that is already running
mysql_replication: mysql_replication:
<<: *mysql_params <<: *mysql_params
@ -176,6 +179,7 @@
- assert: - assert:
that: that:
- result is not changed - result is not changed
when: (pymysql_version.stdout | default('1000', true)) is version('0.9.3', '<=')
# Test stopslave mode: # Test stopslave mode:
- name: Stop slave - name: Stop slave
@ -202,3 +206,4 @@
- assert: - assert:
that: that:
- result is not changed - result is not changed
when: (pymysql_version.stdout | default('1000', true)) is version('0.9.3', '<=')

View file

@ -0,0 +1,86 @@
---
- vars:
mysql_parameters: &mysql_params
login_user: '{{ mysql_user }}'
login_password: '{{ mysql_password }}'
login_host: 127.0.0.1
login_port: '{{ mysql_primary_port }}'
block:
# ============================================================
- shell: pip show pymysql | awk '/Version/ {print $2}'
register: pymysql_version
- name: get server certificate
copy:
content: "{{ lookup('pipe', \"openssl s_client -starttls mysql -connect localhost:3307 -showcerts 2>/dev/null </dev/null | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p'\") }}"
dest: /tmp/cert.pem
delegate_to: localhost
- name: Drop mysql user if exists
mysql_user:
<<: *mysql_params
name: '{{ user_name_1 }}'
state: absent
ignore_errors: yes
- name: create user with ssl requirement
mysql_user:
<<: *mysql_params
name: "{{ user_name_1 }}"
password: "{{ user_password_1 }}"
priv: '*.*:ALL,GRANT'
tls_requires:
SSL:
- name: attempt connection with newly created user (expect failure)
mysql_user:
name: "{{ user_name_2 }}"
password: "{{ user_password_2 }}"
host: 127.0.0.1
login_user: '{{ user_name_1 }}'
login_password: '{{ user_password_1 }}'
login_host: 127.0.0.1
login_port: '{{ mysql_primary_port }}'
ca_cert: /tmp/cert.pem
register: result
ignore_errors: yes
- assert:
that:
- result is failed
when: pymysql_version.stdout != ""
- assert:
that:
- result is succeeded
when: pymysql_version.stdout == ""
- name: attempt connection with newly created user ignoring hostname
mysql_user:
name: "{{ user_name_2 }}"
password: "{{ user_password_2 }}"
host: 127.0.0.1
login_user: '{{ user_name_1 }}'
login_password: '{{ user_password_1 }}'
login_host: 127.0.0.1
login_port: '{{ mysql_primary_port }}'
ca_cert: /tmp/cert.pem
check_hostname: no
register: result
ignore_errors: yes
- assert:
that:
- result is succeeded or 'pymysql >= 0.7.11 is required' in result.msg
- name: Drop mysql user
mysql_user:
<<: *mysql_params
name: '{{ item }}'
host: 127.0.0.1
state: absent
with_items:
- "{{ user_name_1 }}"
- "{{ user_name_2 }}"

View file

@ -37,6 +37,8 @@
block: block:
- include: issue-28.yml
- include: create_user.yml user_name={{user_name_1}} user_password={{ user_password_1 }} - include: create_user.yml user_name={{user_name_1}} user_password={{ user_password_1 }}
- include: resource_limits.yml - include: resource_limits.yml

View file

@ -3,3 +3,6 @@
mysql_user: root mysql_user: root
mysql_password: msandbox mysql_password: msandbox
mysql_primary_port: 3307 mysql_primary_port: 3307
user_name_1: 'db_user1'
user_password_1: 'gadfFDSdtTU^Sdfuj'

View file

@ -0,0 +1,79 @@
---
- vars:
mysql_parameters: &mysql_params
login_user: '{{ mysql_user }}'
login_password: '{{ mysql_password }}'
login_host: 127.0.0.1
login_port: '{{ mysql_primary_port }}'
block:
# ============================================================
- shell: pip show pymysql | awk '/Version/ {print $2}'
register: pymysql_version
- name: get server certificate
copy:
content: "{{ lookup('pipe', \"openssl s_client -starttls mysql -connect localhost:3307 -showcerts 2>/dev/null </dev/null | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p'\") }}"
dest: /tmp/cert.pem
delegate_to: localhost
- name: Drop mysql user if exists
mysql_user:
<<: *mysql_params
name: '{{ user_name_1 }}'
state: absent
ignore_errors: yes
- name: create user with ssl requirement
mysql_user:
<<: *mysql_params
name: "{{ user_name_1 }}"
password: "{{ user_password_1 }}"
priv: '*.*:ALL,GRANT'
tls_requires:
SSL:
- name: attempt connection with newly created user (expect failure)
mysql_variables:
variable: '{{ set_name }}'
login_user: '{{ user_name_1 }}'
login_password: '{{ user_password_1 }}'
login_host: 127.0.0.1
login_port: '{{ mysql_primary_port }}'
ca_cert: /tmp/cert.pem
register: result
ignore_errors: yes
- assert:
that:
- result is failed
when: pymysql_version.stdout != ""
- assert:
that:
- result is succeeded
when: pymysql_version.stdout == ""
- name: attempt connection with newly created user ignoring hostname
mysql_variables:
variable: '{{ set_name }}'
login_user: '{{ user_name_1 }}'
login_password: '{{ user_password_1 }}'
login_host: 127.0.0.1
login_port: '{{ mysql_primary_port }}'
ca_cert: /tmp/cert.pem
check_hostname: no
register: result
ignore_errors: yes
- assert:
that:
- result is succeeded or 'pymysql >= 0.7.11 is required' in result.msg
- name: Drop mysql user
mysql_user:
<<: *mysql_params
name: '{{ user_name_1 }}'
host: 127.0.0.1
state: absent

View file

@ -4,3 +4,5 @@
#################################################################### ####################################################################
- import_tasks: mysql_variables.yml - import_tasks: mysql_variables.yml
- include: issue-28.yml

View file

@ -141,7 +141,7 @@
- name: set mysql variable value to an expression - name: set mysql variable value to an expression
mysql_variables: mysql_variables:
<<: *mysql_params <<: *mysql_params
variable: max_tmp_tables variable: max_connect_errors
value: "1024*4" value: "1024*4"
register: result register: result
ignore_errors: true ignore_errors: true
@ -151,15 +151,22 @@
# ============================================================ # ============================================================
# Verify mysql_variable fails when setting an incorrect value (out of range) # Verify mysql_variable fails when setting an incorrect value (out of range)
# #
- shell: pip show pymysql | awk '/Version/ {print $2}'
register: pymysql_version
- name: set mysql variable value to a number out of range - name: set mysql variable value to a number out of range
mysql_variables: mysql_variables:
<<: *mysql_params <<: *mysql_params
variable: max_tmp_tables variable: max_connect_errors
value: -1 value: -1
register: result register: oor_result
ignore_errors: true ignore_errors: true
- include: assert_fail_msg.yml output={{ result }} msg='Truncated incorrect' - include: assert_var.yml changed=true output={{ oor_result }} var_name=max_connect_errors var_value=1
when: pymysql_version.stdout == ""
- include: assert_fail_msg.yml output={{ oor_result }} msg='Truncated incorrect'
when: pymysql_version.stdout != ""
# ============================================================ # ============================================================
# Verify mysql_variable fails when setting an incorrect value (incorrect type) # Verify mysql_variable fails when setting an incorrect value (incorrect type)
@ -167,12 +174,12 @@
- name: set mysql variable value to a non-valid value number - name: set mysql variable value to a non-valid value number
mysql_variables: mysql_variables:
<<: *mysql_params <<: *mysql_params
variable: max_tmp_tables variable: max_connect_errors
value: TEST value: TEST
register: result register: nvv_result
ignore_errors: true ignore_errors: true
- include: assert_fail_msg.yml output={{ result }} msg='Incorrect argument type to variable' - include: assert_fail_msg.yml output={{ nvv_result }} msg='Incorrect argument type to variable'
# ============================================================ # ============================================================
# Verify mysql_variable fails when setting an unknown variable # Verify mysql_variable fails when setting an unknown variable