Compare commits

..

No commits in common. "main" and "3.11.0" have entirely different histories.
main ... 3.11.0

18 changed files with 70 additions and 474 deletions

View file

@ -13,7 +13,7 @@ on: # yamllint disable-line rule:truthy
- '.github/workflows/ansible-test-plugins.yml'
schedule:
- cron: '0 6 * * *'
workflow_dispatch:
jobs:
sanity:
@ -54,8 +54,8 @@ jobs:
db_engine_version:
- '8.0.38'
- '8.4.1'
- '10.5.25'
- '10.11.8'
- '11.4.5'
connector_name:
- pymysql
- mysqlclient
@ -87,10 +87,10 @@ jobs:
exclude:
- db_engine_name: mysql
db_engine_version: '10.11.8'
db_engine_version: '10.5.25'
- db_engine_name: mysql
db_engine_version: '11.4.5'
db_engine_version: '10.11.8'
- db_engine_name: mariadb
db_engine_version: '8.0.38'
@ -119,13 +119,13 @@ jobs:
- db_engine_version: '8.0.38'
ansible: stable-2.17
- db_engine_version: '10.11.8'
- db_engine_version: '10.5.25'
ansible: stable-2.17
- db_engine_version: '8.0.38'
ansible: devel
- db_engine_version: '10.11.8'
- db_engine_version: '10.5.25'
ansible: devel
- db_engine_version: '8.4.1'
@ -162,7 +162,7 @@ jobs:
db_engine_version: '8.0.38'
- connector_version: '1.1.1'
db_engine_version: '10.11.8'
db_engine_version: '10.5.25'
services:
db_primary:
@ -175,7 +175,7 @@ jobs:
# We write our own health-cmd because the mariadb container does not
# provide a healthcheck
options: >-
--health-cmd "${{ matrix.db_engine_name == 'mysql' && 'mysqladmin' || 'mariadb-admin' }} ping -P 3306 -pmsandbox |grep alive || exit 1"
--health-cmd "mysqladmin ping -P 3306 -pmsandbox |grep alive || exit 1"
--health-start-period 10s
--health-interval 10s
--health-timeout 5s
@ -189,7 +189,7 @@ jobs:
ports:
- 3308:3306
options: >-
--health-cmd "${{ matrix.db_engine_name == 'mysql' && 'mysqladmin' || 'mariadb-admin' }} ping -P 3306 -pmsandbox |grep alive || exit 1"
--health-cmd "mysqladmin ping -P 3306 -pmsandbox |grep alive || exit 1"
--health-start-period 10s
--health-interval 10s
--health-timeout 5s
@ -203,7 +203,7 @@ jobs:
ports:
- 3309:3306
options: >-
--health-cmd "${{ matrix.db_engine_name == 'mysql' && 'mysqladmin' || 'mariadb-admin' }} ping -P 3306 -pmsandbox |grep alive || exit 1"
--health-cmd "mysqladmin ping -P 3306 -pmsandbox |grep alive || exit 1"
--health-start-period 10s
--health-interval 10s
--health-timeout 5s

View file

@ -6,43 +6,6 @@ Community MySQL and MariaDB Collection Release Notes
This changelog describes changes after version 2.0.0.
v3.13.0
=======
Release Summary
---------------
This is a minor release of the ``community.mysql`` collection.
This changelog contains all changes to the modules and plugins in this
collection that have been made after the previous release.
Minor Changes
-------------
- Integration tests for MariaDB 11.4 have replaced those for 10.5. The previous version is now 10.11.
- mysql_user - add ``locked`` option to lock/unlock users, this is mainly used to have users that will act as definers on stored procedures.
Bugfixes
--------
- mysql_db - fix dump and import to find MariaDB binaries (mariadb and mariadb-dump) when MariaDB 11+ is used and symbolic links to MySQL binaries are absent.
v3.12.0
=======
Release Summary
---------------
This is a minor release of the ``community.mysql`` collection.
This changelog contains all changes to the modules and plugins in this
collection that have been made after the previous release.
Minor Changes
-------------
- mysql_db - added ``zstd`` (de)compression support for ``import``/``dump`` states (https://github.com/ansible-collections/community.mysql/issues/696).
- mysql_query - returns the ``execution_time_ms`` list containing execution time per query in milliseconds.
v3.11.0
=======
@ -50,7 +13,6 @@ Release Summary
---------------
This is a minor release of the ``community.mysql`` collection.
This changelog contains all changes to the modules and plugins in this
collection that have been made after the previous release.

View file

@ -11,17 +11,6 @@ ifdef continue_on_errors
_continue_on_errors = --continue-on-error
endif
# Set command variables based on database engine
# Required for MariaDB 11+ which no longer includes mysql named compatible
# executable symlinks
ifeq ($(db_engine_name),mysql)
_command = mysqld
_health_cmd = mysqladmin
else
_command = mariadbd
_health_cmd = mariadb-admin
endif
.PHONY: test-integration
test-integration:
@echo -n $(db_engine_name) > tests/integration/db_engine_name
@ -40,9 +29,9 @@ test-integration:
--env MYSQL_ROOT_PASSWORD=msandbox \
--network podman \
--publish 3307:3306 \
--health-cmd '$(_health_cmd) ping -P 3306 -pmsandbox | grep alive || exit 1' \
--health-cmd 'mysqladmin ping -P 3306 -pmsandbox | grep alive || exit 1' \
docker.io/library/$(db_engine_name):$(db_engine_version) \
$(_command)
mysqld
podman run \
--detach \
--replace \
@ -51,9 +40,9 @@ test-integration:
--env MYSQL_ROOT_PASSWORD=msandbox \
--network podman \
--publish 3308:3306 \
--health-cmd '$(_health_cmd) ping -P 3306 -pmsandbox | grep alive || exit 1' \
--health-cmd 'mysqladmin ping -P 3306 -pmsandbox | grep alive || exit 1' \
docker.io/library/$(db_engine_name):$(db_engine_version) \
$(_command)
mysqld
podman run \
--detach \
--replace \
@ -62,9 +51,9 @@ test-integration:
--env MYSQL_ROOT_PASSWORD=msandbox \
--network podman \
--publish 3309:3306 \
--health-cmd '$(_health_cmd) ping -P 3306 -pmsandbox | grep alive || exit 1' \
--health-cmd 'mysqladmin ping -P 3306 -pmsandbox | grep alive || exit 1' \
docker.io/library/$(db_engine_name):$(db_engine_version) \
$(_command)
mysqld
# Setup replication and restart containers using the same subshell to keep variables alive
db_ver=$(db_engine_version); \
maj="$${db_ver%.*.*}"; \

View file

@ -112,10 +112,10 @@ For MariaDB, only Long Term releases are tested. When multiple LTS are available
- mariadb:10.3.34 (collection version < 3.5.1)
- mariadb:10.4.24 (collection version >= 3.5.2, < 3.10.0)
- mariadb:10.5.18 (collection version >= 3.5.2, < 3.10.0)
- mariadb:10.5.25 (collection version >= 3.10.0, <3.13.0)
- mariadb:10.5.25 (collection version >= 3.10.0)
- mariadb:10.6.11 (collection version >= 3.5.2, < 3.10.0)
- mariadb:10.11.8 (collection version >= 3.10.0)
- mariadb:11.4.5 (collection version >= 3.13.0)
### Database connectors

View file

@ -65,8 +65,8 @@ The Makefile accept the following options
- Choices:
- "8.0.38" <- mysql
- "8.4.1" <- mysql (NOT WORKING YET, ansible-test uses Ubuntu 20.04 which is too old to install mysql-community-client 8.4)
- "10.5.25" <- mariadb
- "10.11.8" <- mariadb
- "11.4.5" <- mariadb
- Description: The tag of the container to use for the service containers that will host a primary database and two replicas. Do not use short version, like `mysql:8` (don't do that) because our tests expect a full version to filter tests precisely. For instance: `when: db_version is version ('8.0.22', '>')`. You can use any tag available on [hub.docker.com/_/mysql](https://hub.docker.com/_/mysql) and [hub.docker.com/_/mariadb](https://hub.docker.com/_/mariadb) but GitHub Action will only use the versions listed above.
- `connector_name`
@ -121,7 +121,7 @@ make ansible="stable-2.16" db_engine_name="mysql" db_engine_version="8.0.31" con
make ansible="stable-2.17" db_engine_name="mysql" db_engine_version="8.0.31" connector_name="mysqlclient" connector_version="2.0.3" target="test_mysql_query" keep_containers_alive=1 continue_on_errors=1
# If your system has an usupported version of Python:
make local_python_version="3.10" ansible="stable-2.17" db_engine_name="mariadb" db_engine_version="11.4.5" connector_name="pymysql" connector_version="1.0.2"
make local_python_version="3.10" ansible="stable-2.17" db_engine_name="mariadb" db_engine_version="10.6.11" connector_name="pymysql" connector_version="1.0.2"
```

View file

@ -230,43 +230,6 @@ releases:
- 591-mysql_info-db_tables_count.yml
- 671-modules_util_user.yml
release_date: '2024-11-19'
3.12.0:
changes:
minor_changes:
- mysql_db - added ``zstd`` (de)compression support for ``import``/``dump``
states (https://github.com/ansible-collections/community.mysql/issues/696).
- mysql_query - returns the ``execution_time_ms`` list containing execution
time per query in milliseconds.
release_summary: 'This is a minor release of the ``community.mysql`` collection.
This changelog contains all changes to the modules and plugins in this
collection that have been made after the previous release.'
fragments:
- 0-mysql_query-returns-exec-time-ms.yml
- 3.12.0.yml
- 696-mysql-db-add-zstd-support.yml
release_date: '2025-01-17'
3.13.0:
changes:
bugfixes:
- mysql_db - fix dump and import to find MariaDB binaries (mariadb and mariadb-dump)
when MariaDB 11+ is used and symbolic links to MySQL binaries are absent.
minor_changes:
- Integration tests for MariaDB 11.4 have replaced those for 10.5. The previous
version is now 10.11.
- mysql_user - add ``locked`` option to lock/unlock users, this is mainly used
to have users that will act as definers on stored procedures.
release_summary: 'This is a minor release of the ``community.mysql`` collection.
This changelog contains all changes to the modules and plugins in this
collection that have been made after the previous release.'
fragments:
- 3.13.0.yml
- 702-user_locking.yaml
- tests_mariadb_11_4.yml
release_date: '2025-03-21'
3.2.0:
changes:
bugfixes:

View file

@ -1,7 +1,7 @@
---
namespace: community
name: mysql
version: 3.13.0
version: 3.11.0
readme: README.md
authors:
- Ansible community

View file

@ -52,25 +52,6 @@ def user_exists(cursor, user, host, host_all):
return count[0] > 0
def user_is_locked(cursor, user, host):
cursor.execute("SHOW CREATE USER %s@%s", (user, host))
# Per discussions on irc:libera.chat:#maria the query may return up to 2 rows but "ACCOUNT LOCK" should always be in the first row.
result = cursor.fetchone()
# ACCOUNT LOCK does not have to be the last option in the CREATE USER query.
# Need to handle both DictCursor and non-DictCursor
if isinstance(result, tuple):
if result[0].find('ACCOUNT LOCK') > 0:
return True
elif isinstance(result, dict):
for res in result.values():
if res.find('ACCOUNT LOCK') > 0:
return True
return False
def sanitize_requires(tls_requires):
sanitized_requires = {}
if tls_requires:
@ -179,7 +160,7 @@ def get_existing_authentication(cursor, user, host=None):
def user_add(cursor, user, host, host_all, password, encrypted,
plugin, plugin_hash_string, plugin_auth_string, salt, new_priv,
attributes, tls_requires, reuse_existing_password, module,
password_expire, password_expire_interval, locked=False):
password_expire, password_expire_interval):
# If attributes are set, perform a sanity check to ensure server supports user attributes before creating user
if attributes and not get_attribute_support(cursor):
module.fail_json(msg="user attributes were specified but the server does not support user attributes")
@ -269,9 +250,6 @@ def user_add(cursor, user, host, host_all, password, encrypted,
cursor.execute("ALTER USER %s@%s ATTRIBUTE %s", (user, host, json.dumps(attributes)))
final_attributes = attributes_get(cursor, user, host)
if locked:
cursor.execute("ALTER USER %s@%s ACCOUNT LOCK", (user, host))
return {'changed': True, 'password_changed': not used_existing_password, 'attributes': final_attributes}
@ -286,7 +264,7 @@ def is_hash(password):
def user_mod(cursor, user, host, host_all, password, encrypted,
plugin, plugin_hash_string, plugin_auth_string, salt, new_priv,
append_privs, subtract_privs, attributes, tls_requires, module,
password_expire, password_expire_interval, locked=None, role=False, maria_role=False):
password_expire, password_expire_interval, role=False, maria_role=False):
changed = False
msg = "User unchanged"
grant_option = False
@ -558,22 +536,6 @@ def user_mod(cursor, user, host, host_all, password, encrypted,
if attribute_support:
final_attributes = attributes_get(cursor, user, host)
if not role and locked is not None and user_is_locked(cursor, user, host) != locked:
if not module.check_mode:
if locked:
cursor.execute("ALTER USER %s@%s ACCOUNT LOCK", (user, host))
msg = 'User locked'
else:
cursor.execute("ALTER USER %s@%s ACCOUNT UNLOCK", (user, host))
msg = 'User unlocked'
else:
if locked:
msg = 'User will be locked'
else:
msg = 'User will be unlocked'
changed = True
if role:
continue

View file

@ -46,8 +46,8 @@ options:
target:
description:
- Location, on the remote host, of the dump file to read from or write to.
- Uncompressed SQL files (C(.sql)) as well as bzip2 (C(.bz2)), gzip (C(.gz)),
xz (Added in 2.0) and zstd (C(.zst)) (Added in 3.12.0) compressed files are supported.
- Uncompressed SQL files (C(.sql)) as well as bzip2 (C(.bz2)), gzip (C(.gz)) and
xz (Added in 2.0) compressed files are supported.
type: path
single_transaction:
description:
@ -386,75 +386,67 @@ def db_dump(module, host, user, password, db_name, target, all_databases, port,
encoding=None, force=False, master_data=0, skip_lock_tables=False,
dump_extra_args=None, unsafe_password=False, restrict_config_file=False,
check_implicit_admin=False, pipefail=False):
cmd_str = 'mysqldump'
if server_implementation == 'mariadb' and LooseVersion(server_version) >= LooseVersion("10.4.6"):
cmd_str = 'mariadb-dump'
try:
cmd = [module.get_bin_path(cmd_str, True)]
except Exception as e:
return 1, "", "Error determining dump command: %s" % str(e)
cmd = module.get_bin_path('mysqldump', True)
# If defined, mysqldump demands --defaults-extra-file be the first option
if config_file:
if restrict_config_file:
cmd.append("--defaults-file=%s" % shlex_quote(config_file))
cmd += " --defaults-file=%s" % shlex_quote(config_file)
else:
cmd.append("--defaults-extra-file=%s" % shlex_quote(config_file))
cmd += " --defaults-extra-file=%s" % shlex_quote(config_file)
if check_implicit_admin:
cmd.append("--user=root --password=''")
cmd += " --user=root --password=''"
else:
if user is not None:
cmd.append("--user=%s" % shlex_quote(user))
cmd += " --user=%s" % shlex_quote(user)
if password is not None:
if not unsafe_password:
cmd.append("--password=%s" % shlex_quote(password))
cmd += " --password=%s" % shlex_quote(password)
else:
cmd.append("--password=%s" % password)
cmd += " --password=%s" % password
if ssl_cert is not None:
cmd.append("--ssl-cert=%s" % shlex_quote(ssl_cert))
cmd += " --ssl-cert=%s" % shlex_quote(ssl_cert)
if ssl_key is not None:
cmd.append("--ssl-key=%s" % shlex_quote(ssl_key))
cmd += " --ssl-key=%s" % shlex_quote(ssl_key)
if ssl_ca is not None:
cmd.append("--ssl-ca=%s" % shlex_quote(ssl_ca))
cmd += " --ssl-ca=%s" % shlex_quote(ssl_ca)
if force:
cmd.append("--force")
cmd += " --force"
if socket is not None:
cmd.append("--socket=%s" % shlex_quote(socket))
cmd += " --socket=%s" % shlex_quote(socket)
else:
cmd.append("--host=%s --port=%i" % (shlex_quote(host), port))
cmd += " --host=%s --port=%i" % (shlex_quote(host), port)
if all_databases:
cmd.append("--all-databases")
cmd += " --all-databases"
elif len(db_name) > 1:
cmd.append("--databases {0}".format(' '.join(db_name)))
cmd += " --databases {0}".format(' '.join(db_name))
else:
cmd.append("%s" % shlex_quote(' '.join(db_name)))
cmd += " %s" % shlex_quote(' '.join(db_name))
if skip_lock_tables:
cmd.append("--skip-lock-tables")
cmd += " --skip-lock-tables"
if (encoding is not None) and (encoding != ""):
cmd.append("--default-character-set=%s" % shlex_quote(encoding))
cmd += " --default-character-set=%s" % shlex_quote(encoding)
if single_transaction:
cmd.append("--single-transaction=true")
cmd += " --single-transaction=true"
if quick:
cmd.append("--quick")
cmd += " --quick"
if ignore_tables:
for an_ignored_table in ignore_tables:
cmd.append("--ignore-table={0}".format(an_ignored_table))
cmd += " --ignore-table={0}".format(an_ignored_table)
if hex_blob:
cmd.append("--hex-blob")
cmd += " --hex-blob"
if master_data:
if (server_implementation == 'mysql' and
LooseVersion(server_version) >= LooseVersion("8.2.0")):
cmd.append("--source-data=%s" % master_data)
cmd += " --source-data=%s" % master_data
else:
cmd.append("--master-data=%s" % master_data)
cmd += " --master-data=%s" % master_data
if dump_extra_args is not None:
cmd.append(dump_extra_args)
cmd += " " + dump_extra_args
path = None
if os.path.splitext(target)[-1] == '.gz':
@ -463,10 +455,6 @@ def db_dump(module, host, user, password, db_name, target, all_databases, port,
path = module.get_bin_path('bzip2', True)
elif os.path.splitext(target)[-1] == '.xz':
path = module.get_bin_path('xz', True)
elif os.path.splitext(target)[-1] == '.zst':
path = module.get_bin_path('zstd', True)
cmd = ' '.join(cmd)
if path:
cmd = '%s | %s > %s' % (cmd, path, shlex_quote(target))
@ -486,21 +474,13 @@ def db_dump(module, host, user, password, db_name, target, all_databases, port,
def db_import(module, host, user, password, db_name, target, all_databases, port, config_file,
server_implementation, server_version, socket=None, ssl_cert=None, ssl_key=None, ssl_ca=None,
encoding=None, force=False,
socket=None, ssl_cert=None, ssl_key=None, ssl_ca=None, encoding=None, force=False,
use_shell=False, unsafe_password=False, restrict_config_file=False,
check_implicit_admin=False):
if not os.path.exists(target):
return module.fail_json(msg="target %s does not exist on the host" % target)
cmd_str = 'mysql'
if server_implementation == 'mariadb' and LooseVersion(server_version) >= LooseVersion("10.4.6"):
cmd_str = 'mariadb'
try:
cmd = [module.get_bin_path(cmd_str, True)]
except Exception as e:
return 1, "", "Error determining mysql/mariadb command: %s" % str(e)
cmd = [module.get_bin_path('mysql', True)]
# --defaults-file must go first, or errors out
if config_file:
if restrict_config_file:
@ -546,8 +526,6 @@ def db_import(module, host, user, password, db_name, target, all_databases, port
comp_prog_path = module.get_bin_path('bzip2', required=True)
elif os.path.splitext(target)[-1] == '.xz':
comp_prog_path = module.get_bin_path('xz', required=True)
elif os.path.splitext(target)[-1] == '.zst':
comp_prog_path = module.get_bin_path('zstd', required=True)
if comp_prog_path:
# The line below is for returned data only:
executed_commands.append('%s -dc %s | %s' % (comp_prog_path, target, cmd))
@ -790,8 +768,8 @@ def main():
rc, stdout, stderr = db_import(module, login_host, login_user,
login_password, db, target,
all_databases,
login_port, config_file, server_implementation,
server_version, socket, ssl_cert, ssl_key, ssl_ca,
login_port, config_file,
socket, ssl_cert, ssl_key, ssl_ca,
encoding, force, use_shell, unsafe_login_password,
restrict_config_file, check_implicit_admin)
if rc != 0:

View file

@ -4,7 +4,6 @@
# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) <andrew.a.klychkov@gmail.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
@ -319,7 +318,6 @@ from ansible_collections.community.mysql.plugins.module_utils.user import (
get_resource_limits,
get_existing_authentication,
get_user_implementation,
user_is_locked,
)
from ansible.module_utils.six import iteritems
from ansible.module_utils._text import to_native
@ -654,10 +652,8 @@ class MySQL_Info(object):
if authentications:
output_dict.update(authentications[0])
if line.get('is_role') and line['is_role'] == 'N':
output_dict['locked'] = user_is_locked(self.cursor, user, host)
# TODO password_option
# TODO lock_option
# but both are not supported by mysql_user atm. So no point yet.
output.append(output_dict)

View file

@ -62,6 +62,7 @@ author:
- Andrew Klychkov (@Andersson007)
extends_documentation_fragment:
- community.mysql.mysql
'''
EXAMPLES = r'''
@ -116,18 +117,8 @@ rowcount:
returned: changed
type: list
sample: [5, 1]
execution_time_ms:
description:
- A list containing execution time per query in milliseconds.
- The measurements are done right before and after passing
the query to the driver for execution.
returned: success
type: list
sample: [7104, 85]
version_added: '3.12.0'
'''
import time
import warnings
from ansible.module_utils.basic import AnsibleModule
@ -148,18 +139,6 @@ DDL_QUERY_KEYWORDS = ('CREATE', 'DROP', 'ALTER', 'RENAME', 'TRUNCATE')
# Module execution.
#
def execute_and_return_time(cursor, query, args):
# Measure query execution time in milliseconds
start_time = time.perf_counter()
cursor.execute(query, args)
# Calculate the execution time rounding it to 4 decimal places
exec_time_ms = round((time.perf_counter() - start_time) * 1000, 4)
return cursor, exec_time_ms
def main():
argument_spec = mysql_common_argument_spec()
argument_spec.update(
@ -234,7 +213,6 @@ def main():
query_result = []
executed_queries = []
rowcount = []
execution_time_ms = []
already_exists = False
for q in query:
@ -245,8 +223,7 @@ def main():
category=mysql_driver.Warning)
try:
cursor, exec_time_ms = execute_and_return_time(cursor, q, arguments)
execution_time_ms.append(exec_time_ms)
cursor.execute(q, arguments)
except mysql_driver.Warning:
# When something is run with IF NOT EXISTS
# and there's "already exists" MySQL warning,
@ -303,7 +280,6 @@ def main():
'executed_queries': executed_queries,
'query_result': query_result,
'rowcount': rowcount,
'execution_time_ms': execution_time_ms,
}
# Exit:

View file

@ -284,6 +284,7 @@ EXAMPLES = r'''
community.mysql.mysql_replication:
mode: changeprimary
fail_on_error: true
'''
RETURN = r'''

View file

@ -930,12 +930,11 @@ class Role():
set_default_role_all=set_default_role_all)
if privs:
result = user_mod(cursor=self.cursor, user=self.name, host=self.host,
host_all=None, password=None, encrypted=None, plugin=None,
plugin_auth_string=None, plugin_hash_string=None, salt=None,
new_priv=privs, append_privs=append_privs, subtract_privs=subtract_privs,
attributes=None, tls_requires=None, module=self.module, password_expire=None,
password_expire_interval=None, role=True, maria_role=self.is_mariadb)
result = user_mod(self.cursor, self.name, self.host,
None, None, None, None, None, None, None,
privs, append_privs, subtract_privs, None, None,
self.module, None, None, role=True,
maria_role=self.is_mariadb)
changed = result['changed']
if admin:

View file

@ -189,15 +189,6 @@ options:
fields names in privileges.
type: bool
version_added: '3.8.0'
locked:
description:
- Lock account to prevent connections using it.
- This is primarily used for creating a user that will act as a DEFINER on stored procedures.
- If not specified leaves the lock state as is (for a new user creates unlocked).
type: bool
version_added: '3.13.0'
attributes:
description:
- "Create, update, or delete user attributes (arbitrary 'key: value' comments) for the user."
@ -234,7 +225,6 @@ author:
- Lukasz Tomaszkiewicz (@tomaszkiewicz)
- kmarse (@kmarse)
- Laurent Indermühle (@laurent-indermuehle)
- E.S. Rosenberg (@Keeper-of-the-Keys)
extends_documentation_fragment:
- community.mysql.mysql
@ -279,7 +269,7 @@ EXAMPLES = r'''
priv: '*.*:ALL,GRANT'
state: present
session_vars:
wsrep_on: 'off'
wsrep_on: off
- name: Create user with password, all database privileges and 'WITH GRANT OPTION' in db1 and db2
community.mysql.mysql_user:
@ -410,13 +400,6 @@ EXAMPLES = r'''
priv:
'db1.*': DELETE
- name: Create locked user to act as a definer on procedures
community.mysql.mysql_user:
name: readonly_procedures_locked
locked: true
priv:
db1.*: SELECT
# Example .my.cnf file for setting the root password
# [client]
# user=root
@ -487,7 +470,6 @@ def main():
column_case_sensitive=dict(type='bool', default=None), # TODO 4.0.0 add default=True
password_expire=dict(type='str', choices=['now', 'never', 'default', 'interval'], no_log=True),
password_expire_interval=dict(type='int', required_if=[('password_expire', 'interval', True)], no_log=True),
locked=dict(type='bool'),
)
module = AnsibleModule(
argument_spec=argument_spec,
@ -528,7 +510,6 @@ def main():
column_case_sensitive = module.params["column_case_sensitive"]
password_expire = module.params["password_expire"]
password_expire_interval = module.params["password_expire_interval"]
locked = module.boolean(module.params['locked'])
if priv and not isinstance(priv, (str, dict)):
module.fail_json(msg="priv parameter must be str or dict but %s was passed" % type(priv))
@ -596,15 +577,13 @@ def main():
result = user_mod(cursor, user, host, host_all, password, encrypted,
plugin, plugin_hash_string, plugin_auth_string, salt,
priv, append_privs, subtract_privs, attributes, tls_requires, module,
password_expire, password_expire_interval, locked=locked)
password_expire, password_expire_interval)
else:
result = user_mod(cursor=cursor, user=user, host=host, host_all=host_all, password=None,
encrypted=encrypted, plugin=None, plugin_hash_string=None, plugin_auth_string=None,
salt=None, new_priv=priv, append_privs=append_privs, subtract_privs=subtract_privs,
attributes=attributes, tls_requires=tls_requires, module=module,
password_expire=password_expire, password_expire_interval=password_expire_interval,
locked=locked)
result = user_mod(cursor, user, host, host_all, None, encrypted,
None, None, None, None,
priv, append_privs, subtract_privs, attributes, tls_requires, module,
password_expire, password_expire_interval)
changed = result['changed']
msg = result['msg']
password_changed = result['password_changed']
@ -622,7 +601,7 @@ def main():
result = user_add(cursor, user, host, host_all, password, encrypted,
plugin, plugin_hash_string, plugin_auth_string, salt,
priv, attributes, tls_requires, reuse_existing_password, module,
password_expire, password_expire_interval, locked=locked)
password_expire, password_expire_interval)
changed = result['changed']
password_changed = result['password_changed']
final_attributes = result['attributes']

View file

@ -261,7 +261,6 @@
resource_limits: "{{ item.resource_limits | default(omit) }}"
column_case_sensitive: true
state: present
locked: "{{ item.locked | default(omit) }}"
loop: "{{ result.users_info }}"
loop_control:
label: "{{ item.name }}@{{ item.host }}"
@ -276,7 +275,6 @@
- item.name != 'mariadb.sys'
- item.name != 'mysql.sys'
- item.name != 'mysql.infoschema'
- item.name != 'mysql.session'
# ================================== Cleanup ============================

View file

@ -35,7 +35,6 @@
that:
- result is changed
- result.executed_queries == ['CREATE TABLE {{ test_table1 }} (id int)']
- result.execution_time_ms[0] > 0
- name: Insert test data
mysql_query:
@ -53,8 +52,6 @@
- result is changed
- result.rowcount == [2, 1]
- result.executed_queries == ['INSERT INTO {{ test_table1 }} VALUES (1), (2)', 'INSERT INTO {{ test_table1 }} VALUES (3)']
- result.execution_time_ms[0] > 0
- result.execution_time_ms[1] > 0
- name: Check data in {{ test_table1 }}
mysql_query:

View file

@ -305,7 +305,3 @@
- name: Mysql_user - test update_password
ansible.builtin.import_tasks:
file: test_update_password.yml
- name: Mysql_user - test user_locking
ansible.builtin.import_tasks:
file: test_user_locking.yml

View file

@ -1,200 +0,0 @@
---
- vars:
mysql_parameters: &mysql_params
login_user: '{{ mysql_user }}'
login_password: '{{ mysql_password }}'
login_host: '{{ mysql_host }}'
login_port: '{{ mysql_primary_port }}'
block:
# ========================= Prepare =======================================
- name: Mysql_user Lock user | Create a test database
community.mysql.mysql_db:
<<: *mysql_params
name: mysql_lock_user_test
state: present
# ========================== Tests ========================================
- name: Mysql_user Lock user | create locked | Create test user
community.mysql.mysql_user:
<<: *mysql_params
name: mysql_locked_user
password: 'msandbox'
locked: true
priv:
'mysql_lock_user_test.*': 'SELECT'
- name: Mysql_user Lock user | create locked | Assert that test user is locked
community.mysql.mysql_query:
<<: *mysql_params
query:
- SHOW CREATE USER 'mysql_locked_user'@'localhost'
register: locked_user_creation
failed_when:
- locked_user_creation.query_result[0][0] is not search('ACCOUNT LOCK')
- name: 'Mysql_user Lock user | create locked | Idempotence check'
check_mode: true
community.mysql.mysql_user:
<<: *mysql_params
name: mysql_locked_user
locked: true
priv:
'mysql_lock_user_test.*': 'SELECT'
register: idempotence_check
failed_when: idempotence_check is changed
- name: 'Mysql_user Lock user | create locked | Check that absense of locked does not unlock the user'
check_mode: true
community.mysql.mysql_user:
<<: *mysql_params
name: mysql_locked_user
priv:
'mysql_lock_user_test.*': 'SELECT'
register: idempotence_check
failed_when: idempotence_check is changed
- name: 'Mysql_user Lock user | create locked | Unlock test user check_mode: true'
check_mode: true
community.mysql.mysql_user:
<<: *mysql_params
name: mysql_locked_user
locked: false
priv:
'mysql_lock_user_test.*': 'SELECT'
- name: Mysql_user Lock user | create locked | Assert that test user is locked
community.mysql.mysql_query:
<<: *mysql_params
query:
- SHOW CREATE USER 'mysql_locked_user'@'localhost'
register: locked_user_creation
failed_when:
- locked_user_creation.query_result[0][0] is not search('ACCOUNT LOCK')
- name: Mysql_user Lock user | create locked | Unlock test user
community.mysql.mysql_user:
<<: *mysql_params
name: mysql_locked_user
locked: false
priv:
'mysql_lock_user_test.*': 'SELECT'
- name: Mysql_user Lock user | create locked | Assert that test user is not locked
community.mysql.mysql_query:
<<: *mysql_params
query:
- SHOW CREATE USER 'mysql_locked_user'@'localhost'
register: locked_user_creation
failed_when:
- locked_user_creation.query_result[0][0] is search('ACCOUNT LOCK')
- name: Mysql_user Lock user | create locked | Remove test user
community.mysql.mysql_user:
<<: *mysql_params
name: mysql_locked_user
state: absent
- name: Mysql_user Lock user | create unlocked | Create test user
community.mysql.mysql_user:
<<: *mysql_params
name: mysql_locked_user
password: 'msandbox'
locked: false
priv:
'mysql_lock_user_test.*': 'SELECT'
- name: Mysql_user Lock user | create unlocked | Assert that test user is not locked
community.mysql.mysql_query:
<<: *mysql_params
query:
- SHOW CREATE USER 'mysql_locked_user'@'localhost'
register: locked_user_creation
failed_when:
- locked_user_creation.query_result[0][0] is search('ACCOUNT LOCK')
- name: 'Mysql_user Lock user | create unlocked | Idempotence check'
check_mode: true
community.mysql.mysql_user:
<<: *mysql_params
name: mysql_locked_user
locked: false
priv:
'mysql_lock_user_test.*': 'SELECT'
register: idempotence_check
failed_when: idempotence_check is changed
- name: 'Mysql_user Lock user | create unlocked | Lock test user check_mode: true'
check_mode: true
community.mysql.mysql_user:
<<: *mysql_params
name: mysql_locked_user
locked: true
priv:
'mysql_lock_user_test.*': 'SELECT'
- name: Mysql_user Lock user | create unlocked | Assert that test user is not locked
community.mysql.mysql_query:
<<: *mysql_params
query:
- SHOW CREATE USER 'mysql_locked_user'@'localhost'
register: locked_user_creation
failed_when:
- locked_user_creation.query_result[0][0] is search('ACCOUNT LOCK')
- name: Mysql_user Lock user | create unlocked | Lock test user
community.mysql.mysql_user:
<<: *mysql_params
name: mysql_locked_user
locked: true
priv:
'mysql_lock_user_test.*': 'SELECT'
- name: Mysql_user Lock user | create unlocked | Assert that test user is locked
community.mysql.mysql_query:
<<: *mysql_params
query:
- SHOW CREATE USER 'mysql_locked_user'@'localhost'
register: locked_user_creation
failed_when:
- locked_user_creation.query_result[0][0] is not search('ACCOUNT LOCK')
- name: Mysql_user Lock user | create unlocked | Remove test user
community.mysql.mysql_user:
<<: *mysql_params
name: mysql_locked_user
state: absent
- name: Mysql_user Lock user | create default | Create test user
community.mysql.mysql_user:
<<: *mysql_params
name: mysql_locked_user
password: 'msandbox'
priv:
'mysql_lock_user_test.*': 'SELECT'
- name: Mysql_user Lock user | create default | Assert that test user is not locked
community.mysql.mysql_query:
<<: *mysql_params
query:
- SHOW CREATE USER 'mysql_locked_user'@'localhost'
register: locked_user_creation
failed_when:
- locked_user_creation.query_result[0][0] is search('ACCOUNT LOCK')
- name: Mysql_user Lock user | create default | Remove test user
community.mysql.mysql_user:
<<: *mysql_params
name: mysql_locked_user
state: absent
# ========================= Teardown ======================================
- name: Mysql_user Lock user | Delete test database
community.mysql.mysql_db:
<<: *mysql_params
name: mysql_lock_user_test
state: absent