Compare commits

..

91 commits
main ... 2.4.2

Author SHA1 Message Date
Andrew Klychkov
6e2310d356
Release 2.4.2 commit (#607) 2024-01-22 11:12:34 +01:00
Andrew Klychkov
b894d28957
Version 2.*.* is EOL (#606) 2024-01-22 11:06:33 +01:00
Laurent Indermühle
368c9b4b65
[PR #599/81ab18d backport][stable-2] chore[CI]: fix conditional statements should not include jinja 2 templating (#601)
* chore: fix conditional statements should not include jinja 2 templating (#599)

Thanks to @tompal3 for your contribution

(cherry picked from commit 81ab18d56c)

* fix query for stable-2
2023-11-30 16:41:34 +01:00
patchback[bot]
534a6fa1bc
Document MySQL and MariaDB don't store roles with same manner (#584) (#585)
(cherry picked from commit 0dbedf57cb)

Co-authored-by: Laurent Indermühle <laurent.indermuehle@epfl.ch>
2023-10-27 14:08:30 +02:00
Laurent Indermühle
37b105c526
cut ansible devel from the test matrix (#587) 2023-10-27 13:21:35 +02:00
patchback[bot]
57d9ba03eb
README: Define project's mission statement (#561) (#562)
* README: Define project's mission statement

* Add suggestion

(cherry picked from commit 94392826e1)

Co-authored-by: Andrew Klychkov <aklychko@redhat.com>
2023-07-20 12:30:42 +02:00
patchback[bot]
89d1637c32
Doc Attributes (#555) (#557)
* Added Attributes section

* Added Attributes section

(cherry picked from commit b79fd94d51)

Co-authored-by: Pavel Rabel <128324708+elpavel@users.noreply.github.com>
2023-05-29 11:02:20 +02:00
Laurent Indermühle
bbbd38855b
[stable-2] Revert announcement of EOL (#545)
* docs: revert announcement of EOL for stable-2

stable-2 is still support until 2023-12-01. My bad.
2023-05-17 14:15:00 +02:00
patchback[bot]
aa16129737
MAINTAINERS: add new maintainer (#548) (#550)
(cherry picked from commit bd90ce7cc6)

Co-authored-by: Andrew Klychkov <aklychko@redhat.com>
2023-05-10 15:25:46 +02:00
Laurent Indermühle
fbb22a62f6
Release 2.4.1 commit (#542)
* Release 2.4.1 commit
2023-05-05 12:50:24 +02:00
patchback[bot]
9670218a6a
Fix the Makefile for integration tests not using the Python Venv (#532) (#534)
* Fix venv not being used by keeping the same shell

Also fix "-set -x" command not found.

* Fix missing option in the command usage documentation

* Document connector-version relationship

* Fix missing option in the command usage documentation

* Rephrase commands descriptions

* Document that you need to kill the ansible-test container yourself

(cherry picked from commit 426084a131)

Co-authored-by: Laurent Indermühle <laurent.indermuehle@pm.me>
2023-04-27 13:30:38 +02:00
patchback[bot]
095df1e8af
Added formatting behaviour to documentation (#516) (#524)
* Added formatting behaviour to documentation

* Update plugins/modules/mysql_query.py

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

---------

Co-authored-by: Andrew Klychkov <aaklychkov@mail.ru>
(cherry picked from commit 754387c7e5)

Co-authored-by: IBims1NicerTobi <54948543+IBims1NicerTobi@users.noreply.github.com>
2023-03-31 20:25:05 +02:00
patchback[bot]
423a2272aa
Add filter to prevent rebuild container on push on stable-(1|2) (#522) (#526)
(cherry picked from commit 21e42b5777)

Co-authored-by: Laurent Indermühle <laurent.indermuehle@pm.me>
2023-03-31 17:52:56 +02:00
Laurent Indermühle
c30a2e5c99
[PR #514/e2aa655 backport][stable-2] Fix issues and documentation with integration tests after merge of #490 (#521)
* Fix issues and documentation with integration tests after merge of #490. (#514)

* Fix explanation about containers images
* Add definitive URI to the containers images
* Document that new images must be set as public
* Document makefile options possible values
* Document that any mysql and mariadb tag can be use
* Add computation of docker_image path
* Refactor pre-command to separate commands for cleaner GHA output
* Refactor to use GHA test matrix
* Cut docker_image from documentation since it's now automatic
* Document how to use run_all_test.py to display the test matrix

(cherry picked from commit e2aa655762)

* Add changelog fragment
2023-03-30 08:29:21 +02:00
Laurent Indermühle
530e6c3d9c
[PR #490/6970aef backport][stable-2] Integrations tests : Use containers (#519)
* Integrations tests : Use containers for more control and verify that versions match expectation (#490)

* Add healthcheck to MariaDB before starting the tests
This prevent the first test to fail because the db isn't ready yet.

* Add % instead of the default 'localhost' since we use remote connection

Previously, everything was on localhost. Now ansible-test is in a
venv and the db is in a container. The db see the IP address from the
podman host (10.88.0.2)

* Add mysql_client to the controller

* Prepare controller with Podman/Docker Network

We use the Podman/Docker network gateway address to communicate between
container. I haven't tested Docker. I would have preferred to use a pod
but only Podman support it and ansible-test only support the
--docker-network option.

* Generalize mysql and mariadb version based on container name

This way we can split db_engine and db_version and simplify tests.
Also this is mandatory to use the matrix.db_engine_version as the
image name for our services containers.

* Fix replication due to usage of gateway_addr instead of localhost

* Refactor setup_mysql into setup_controller

* Fix server_id in GHA

GHA lack a way to pass option to docker's command. Also server_id is
not read as a environment variable. So I'm forced to use a config file.

* Refactor test_mysql_user to work with other host than localhost

* Refactor way tests info are passed from sed to file with lookup

The idea is to avoid modifying test targets from the workflow to prevent
ansible-test to think every tests needs to be run.

* Refactor test to use the db_version from setup_controller

* Add temporary files to .gitignore

* Add back docker healthcheck on services

I saw in the GHA logs that it perform an healtcheck ! So I hope this
will work.

* Refactor the way server_id is set for replicas

The simple way is to add '--server-id 2' after the name of the image of
the container. But GHA doesn't let us do that. The idea of mount a file
from our repo doesn't work because the repo is check out later in the
workflow and I failed to find a pre-job hook. Then I realized that this
MySQL option is dynamic! So we will set that in the test target!

* Add IF NOT EXISTS to prevent misleading error on retry

* Cut python 3.11 not supported by ansible-test yet

* Add option to run only a single target

* Disable replication with channel tests entirely for MySQL 5.7

* Activate Mysql 8 and Mariadb into GitHub Action Workflow

* Document run_all_tests.py

* Fix replication settings

sh don't know 'echo -e', so we use bash instead.
Also, we need to wait for the container to be healthy before trying to
restart it. Otherwise that could corrupt it.

* Add more descriptive tests names

* Use mysql_host var name instead of gateway_addr in tests

* Refactor user@<gateway ip addr> into user@%

* Workaround for plugin role that fails with any MariaDB versions

* Ensure replicas are healthy before rebooting them

* Add a virtualenv for ansible-test used locally

* Cut column-statistics disabling

Thanks to our test-container, we now use the correspond mysql-client.
So to test mysql 5.7 we use mysql-client-5.7 and to test mysql 8 we
use mysql-client-8.

* Fix test matrix

Python version should be quoted, otherwise 3.10 become 3.1
We can skip 2.14 and devel with Python3.8
We can skip devel with Python 3.9
We can skip MariaDB 10.4 with mysql-client-10.6
Add tests for MariaDB 10.6, 10.7 and 10.8

* Fix queries for roles

* Add filter for issues resolved in newer version of mysqlclient

* Add names to tests

* Cut tests for incompatible MySQL 8 and pymysql 0.7.11

* Fix assertion for older mysqlclient than 2.0.1 with mysql (mariadb ok)

* Change docker-image workflow to work on all images using matrix

* Add support for version of mysqlclient

* Fix verify database version

Sometimes, version_full contains trailing information (-log). To prevent
issues it's best to concatenate major and minor version.

* Cut filter for tests now that the right connector is used

* Add clean up in "always" phase of the block

Because our tests use --retry-on-error, and the first thing the test
does is to try to create the database. We must cleanup otherwise if
there is a retry, it will throw a misleading "database already exists"
error.

* Disable tests using pymysql 1.0.2

Many tests are failing but this must be fixed in the plugins in a future
PR.

* Cut test MySQL 8 with incompatible pymysql 0.7.11

It fails to connect with error about cryptography unsupported

* Fix missing cffi package to connect to MySQL 8 using Python 3.9

* Split Docker image workflow to rebuild only changed Dockerfile

My goal is not to save the planet but to make it work. Currently
docker/setup-buildx-action@v2 often fails. You have to rerun the
workflow multiple times until it succeed. When you do that with the
matrix with 15 containers, you never get to the point where they all
built successfully. Having separate workflows makes rerun the failing
build easier.

* Add option to let containers alive at end of testing

* Migrate tests documentations in their own file

* Document usage of continue_on_errors

* Add support for systems with unsupported python set as default

* Add create podman network for system missing it.

We saw that on a Fedora 33 with Podman 3.3.1, an old system. I didn't
find in which release the default network changed and maybe it's
defined in the Linux distribution. So in doubt I always attempt to
create the network.

* Add full path to image to prevent podman asking which registry to use

* Add options to enforce recreate containers even if already exists

* Add deletion of anonymous volumes associated with the container

* Change shebang from python to python3 to avoid confusion with python2

This script is a python3 script.

* Add disk and RAM requirements

* Cut the 3 from python command to follow shebang recommendations

https://docs.ansible.com/ansible-core/devel/dev_guide/testing/sanity/shebang.html

* Reformat file path

Co-authored-by: Jorge Rodriguez (A.K.A. Tiriel) <jorge.rodriguez@futurice.com>

* Move utility task files in their own folder

* Add called workflow file in the GHA hooks

Without this, the containers are not rebuilt when you modify the file
built-docker-image.yml.

* Rollback to github.repository in container image name

This time I think I understood. We publish in the
github.repository_owner's namespace. In my case it's laurent-indermuehle
and in case of upstream it's ansible-collections. A proof of that:
https://github.com/orgs/ansible-collections/packages <- here there is
one attempt I did in february to push my branch to the upstream.
So, our tests containers will be visible to the whole community, not
just community.mysql.

---------

Co-authored-by: Jorge Rodriguez (A.K.A. Tiriel) <jorge.rodriguez@futurice.com>
(cherry picked from commit 6970aef8f6)

* Add changelog fragment

* Disable tests that doesn't work on stable-1

It's shameful to disable tests, but they didn't makes much sense
anyway.

* Fix error message being different on stable-1 than v3

* Disable tests against MariaDB 10.5 and 10.6

Does version have never been tested on stable-1 and many tests fails.
2023-03-29 08:56:52 +02:00
patchback[bot]
b02c5b411a
[PR #508/9b8455c2 backport][stable-2] Fix sanity (#509)
* Fix sanity (#508)

* Fix sanity

* Remove as unnecessary

(cherry picked from commit 9b8455c2e6)

* Update tests/unit/plugins/module_utils/test_mysql_user.py

---------

Co-authored-by: Andrew Klychkov <aklychko@redhat.com>
2023-02-14 14:33:36 +01:00
Markus Bergholz
1243d85f65
prepare community.mysql 2.4.0 (#506)
* prepare 2.3.11

* 2.4.0
2023-02-08 10:34:31 +01:00
patchback[bot]
ff94dcdf0f
[PR #497/a5f3296d backport][stable-2] mysql_info - Add connector_name and connector_version to returned value (#499)
* mysql_info - Add connector_name and connector_version to returned value (#497)

* Add methods to retrieve connector name and version
* Document that mysqlclient is also named MySQLdb
* Document version_added
* Add connector name and version in the returned block
* Cut condition to display any name that is return

In case of MySQLdb is renamed in mysqlclient. In that case, the
integration tests will catch this the day we update the connector
version.

Co-authored-by: Andrew Klychkov <aaklychkov@mail.ru>
(cherry picked from commit a5f3296d73)

* Cut fragment not relevant to the collection usage

* Update version_added for stable-2

---------

Co-authored-by: Laurent Indermühle <laurent.indermuehle@pm.me>
2023-02-01 09:49:13 +01:00
patchback[bot]
b16e57ddbc
README: improve Matrix badge (#494) (#496)
* README: improve Matrix badge

* Add text

(cherry picked from commit 3229ce4e55)

Co-authored-by: Andrew Klychkov <aklychko@redhat.com>
2023-01-26 09:46:19 +01:00
Andrew Klychkov
c9dc6cd4eb
491-CI-fix-tarball-download (#491) (#492)
* Fix mariadb test setup

* Update mysql src URL

* Add changelog fragment

* Update 491_fix_download_url.yaml

Sanity test failed because minor_changes in not an element of a list.

* Fix casing

Co-authored-by: Laurent Indermühle <laurent.indermuehle@pm.me>
(cherry picked from commit 00fa058a18)

Co-authored-by: Jorge Rodriguez (A.K.A. Tiriel) <jorge.rodriguez@futurice.com>
2023-01-25 10:57:28 +01:00
patchback[bot]
090cab98a4
README: update Matrix badge (#485) (#486)
(cherry picked from commit 4ad71775a6)

Co-authored-by: Andrew Klychkov <aklychko@redhat.com>
2023-01-16 13:26:39 +01:00
Andrew Klychkov
e4fc4bdf06
Docs: change yes/no to true/false (#480) (#481)
(cherry picked from commit 3ff1fad5f3)
2023-01-03 12:56:27 +01:00
patchback[bot]
335ce76f82
CI: add PR change detection (#473) (#476)
(cherry picked from commit eade7ec1f0)

Co-authored-by: Andrew Klychkov <aklychko@redhat.com>
2022-12-09 16:48:12 +01:00
patchback[bot]
2b6adcd58a
Add fixed version of Ubuntu (#470) (#472)
This is because ubuntu-latest link to ubuntu-22.04 which includes
cgroup-v2. I thinks our tests fails because of that. See
https://github.com/ansible-collections/news-for-maintainers/issues/28
for more information.

(cherry picked from commit 4dac66382a)

Co-authored-by: Laurent Indermühle <laurent.indermuehle@pm.me>
2022-12-07 16:46:14 +01:00
Laurent Indermühle
8949c68929
Backport/stable 2/pr 452 (#463)
* Sync GHA workflow w/ the collection template (#452)

* Sync GHA workflow w/ the collection template

* Drop the trailing pre-cmd semicolon

* Recover missing `-e` flag of `sed`

* Use relative paths for version configs

* Unquote `env.connector_version_file`

* Use string formatting to fix the substitution problem

(cherry picked from commit 8107530744)

* Backport mysql_version_parts variable assignation

(cherry picked from commit 79046a88cb)

* Add changelog fragment

* Backport flags and variables to differentiate MariaDB from MySQL setup

* Backport issue-28 check for tls support

* Backport tls_requirements simplified and deduplified tests

Co-authored-by: Sviatoslav Sydorenko <wk.cvs.github@sydorenko.org.ua>
2022-11-10 14:57:58 +01:00
patchback[bot]
1f42f39d92
README: Add matrix room + badge (#459) (#461)
* README: Add matrix room + badge

* improve

(cherry picked from commit 09e02320fd)

Co-authored-by: Andrew Klychkov <aklychko@redhat.com>
2022-11-01 13:28:02 +01:00
patchback[bot]
826e9c7c89
* add socket option suggestion in documentation (#437) (#455)
* * add `socket` option suggestion in documentation

* white space fix

* * move first two at the end

(cherry picked from commit b9a6ec4f7d)

Co-authored-by: Gabriel PREDA <eRadical@users.noreply.github.com>
2022-10-04 14:35:05 +02:00
patchback[bot]
d8a1284e3d
CI: add stable-2.14 to test matrix (#449) (#450)
(cherry picked from commit b8e2c02e89)

Co-authored-by: Andrew Klychkov <aklychko@redhat.com>
2022-09-23 07:06:07 +02:00
patchback[bot]
b5a0b3aa62
Fix: devel requires python 3.9 in roles CI (#444) (#448)
* Fix: devel requires python 3.9

Package 'ansible-core' requires a different Python: 3.8.13 not in '>=3.9'

* Exclude older version of Ansible when testing Python 3.9

(cherry picked from commit 2cd29207f3)

Co-authored-by: R.Sicart <roger.sicart@gmail.com>
2022-09-16 16:15:40 +02:00
patchback[bot]
b4f03b01ef
Combine REVIEW_CHECKLIST.md and CONTRIBUTING.md and fix links (#432) (#439)
(cherry picked from commit ea73d408c3)

Co-authored-by: Maxwell G <9920591+gotmax23@users.noreply.github.com>
2022-09-06 09:48:43 +02:00
Laurent Indermühle
58b850f217
[PR #427/0a68bb27 backport][stable-2] CI is changed (#429)
* Is changed (#427)

* Refactor tests to use "is" and "is not" changed

* Refactor tests to use is succeeded or is failed

* Reformat indentation

* Add filter "bool" to prevent issues

(cherry picked from commit 0a68bb270f)

* Fix error message verification

I don't know why this works on main, but in stable-1, the error message
is "invalid privileges string: Invalid privileges specified:
frozenset({'INVALID'})"

* Add filter for test that won't work with mariadb

* Add test databases cleanup

* Backport small diff from main
2022-08-29 10:20:13 +02:00
patchback[bot]
4aa166ec13
Docs: add info how to cope with a protocol-related connection error using login_unix_socket argument (#420) (#430)
(cherry picked from commit f1d63e3fc8)

Co-authored-by: Andrew Klychkov <aklychko@redhat.com>
2022-08-26 13:08:42 +02:00
Laurent Indermühle
8968f51533
Port stable 1 ci changes (#423) (#424)
* Add changes from stable-1 integrations tests (PR 418)

* Refactor to use connectors' info declared in setup_mysql

* Fix 2nd replication stop marked changed by mysqlclient

(cherry picked from commit 61586ae4cc)
2022-08-17 11:30:34 +02:00
patchback[bot]
13a8a4ebff
MAINTAINERS file: add a new maintainer (#419) (#421)
(cherry picked from commit 057f817111)

Co-authored-by: Andrew Klychkov <aklychko@redhat.com>
2022-08-13 21:37:22 +02:00
patchback[bot]
912995da76
Fix ci python requirements (#416) (#417)
* Add matrix for python and ansible-core versions for sanity tests

* Add python 3.9 to integrations tests

* Add python 3.9 to unit tests

* Reformat sort by python version first

(cherry picked from commit 97318559e5)

Co-authored-by: Laurent Indermühle <laurent.indermuehle@pm.me>
2022-08-09 08:17:00 +02:00
Laurent Indermühle
98d4828e44
Update galaxy.yml to the next expected version (#413) 2022-08-03 10:30:06 +02:00
Laurent Indermühle
b642aedc25
Release 2.3.9 commit (#411) 2022-08-02 10:24:25 +02:00
patchback[bot]
a444d0a356
README: update MariaDB versions we test against (#404) (#405)
(cherry picked from commit 6f87620d9b)

Co-authored-by: Andrew Klychkov <aklychko@redhat.com>
2022-06-24 15:07:20 +02:00
patchback[bot]
cac58cfa49
Note added regarding the default config file, ~/.my.cnf (#400) (#401)
* Note added for https://github.com/ansible-collections/community.mysql/issues/394

* Update config file notes as discussed

* Update plugins/doc_fragments/mysql.py

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

Co-authored-by: Andrew Klychkov <aaklychkov@mail.ru>
(cherry picked from commit 0df46e0e67)

Co-authored-by: Chris Croome <chris@webarchitects.co.uk>
2022-06-17 08:27:08 +02:00
patchback[bot]
404ffbb3b1
Update licensing information (#390) (#391)
(cherry picked from commit 2a3f8f6506)

Co-authored-by: Andrew Klychkov <aklychko@redhat.com>
2022-06-03 16:28:20 +02:00
Andrew Klychkov
71c7275fef
Release 2.3.8 commit (#388) 2022-06-02 08:18:36 +02:00
patchback[bot]
9ca52b3aa5
Changed += to append because cmd is a list (#377) (#385)
Using += on a list cause some problems druing creation of mysql command:
/usr/bin/mysql   - - u s e r = r o o t   - - p a s s w o r d = ' ' --socket=/run/mysqld/mysqld.sock

(cherry picked from commit 2e9d50f274)

Co-authored-by: Maciej <moledzki@users.noreply.github.com>
2022-06-01 07:57:56 +02:00
Andrew Klychkov
0a40c13aa8
mysql_user: prevent password getting set for existing users on on_cre… (#342) (#382)
Co-authored-by: Andrew Klychkov <aaklychkov@mail.ru>
(cherry picked from commit 51a38840d9)

Co-authored-by: hubiongithub <79990207+hubiongithub@users.noreply.github.com>
2022-05-31 14:32:23 +02:00
patchback[bot]
0011798382
mysql_query: fix false change reports when IF NOT EXISTS clause is used (#322) (#374)
* mysql_query: fix false change reports when IF NOT EXISTS clause is used

* Fix

* Fix doc, add fragment

* Improve doc

(cherry picked from commit 647461010d)

Co-authored-by: Andrew Klychkov <aklychko@redhat.com>
2022-05-25 19:58:15 +02:00
Andrew Klychkov
46dc308e28
mysql_role - don't add members to a role when creating the role and detach_members true is set (#372)
Co-authored-by: betanummeric <40263343+betanummeric@users.noreply.github.com>
2022-05-25 16:22:39 +02:00
patchback[bot]
e34cfba109
mysql_role: fix and simplify role member detection (#368) (#371)
* mysql_role: fix and simplify role membership detection

* add changelog fragment

* Update changelogs/fragments/368-mysql_role-fix-member-detection.yml

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

Co-authored-by: Felix Hamme <felix.hamme@ionos.com>
Co-authored-by: Andrew Klychkov <aaklychkov@mail.ru>
(cherry picked from commit 07a72865f7)

Co-authored-by: betanummeric <40263343+betanummeric@users.noreply.github.com>
2022-05-25 14:36:08 +02:00
Andrew Klychkov
c02e46e7b0
Release 2.3.7 commit (#362) 2022-05-17 12:15:07 +02:00
Felix Fontein
cf1c95b64d
Add PSF-license.txt file (#356) (#360)
* Add PSF-license.txt file.

* Update with actual CPython 3.9.5 license.

(cherry picked from commit b2e476cb1a)
2022-05-17 08:24:28 +02:00
Andrew Klychkov
65b1773ed0
Release 2.3.6 commit (#354) 2022-05-13 09:16:09 +02:00
Andrew Klychkov
f854fc96db
Drop support for Ansible 2.9 and ansible-base 2.10 (#343) (#348)
* Drop support for Ansible 2.9 and ansible-base 2.10

* Improve README

(cherry picked from commit eff87f952b)
2022-05-12 14:31:07 +02:00
Andrew Klychkov
cc78846f22
mysql_user: fix parsing privs when a user has roles assigned to it (#345) 2022-05-12 14:20:44 +02:00
patchback[bot]
65eb142960
Move CHANGELOG.rst at top level (#349) (#350)
(cherry picked from commit f57ed38beb)

Co-authored-by: Andrew Klychkov <aklychko@redhat.com>
2022-05-12 11:55:02 +02:00
patchback[bot]
d61d442861
mysql_user: added flush privileges to write dynamic privs into db (#338) (#339)
* added flush privileges to write dynamic privs into db
Fixes https://github.com/ansible-collections/community.mysql/issues/120

* added changelog fragment

* Update changelogs/fragments/338-mysql_user_fix_missing_dynamic_privileges.yml

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

Co-authored-by: Andrew Klychkov <aaklychkov@mail.ru>
(cherry picked from commit 1dcc5ec086)

Co-authored-by: bigo8525 <53953606+bigo8525@users.noreply.github.com>
2022-04-29 16:29:33 +02:00
patchback[bot]
d38813d418
mysql_role: remove redundant connection closing (fixes #329) (#330) (#332)
* mysql_role: remove redundant connection closing (fixes #329)

* add changelog fragment for pull request #330

Co-authored-by: Felix Hamme <felix.hamme@ionos.com>
(cherry picked from commit 641894e6e8)

Co-authored-by: betanummeric <40263343+betanummeric@users.noreply.github.com>
2022-04-05 11:31:38 +02:00
Andrew Klychkov
9857a16cb5
CI: add testing against ansible-core 2.13 (#327)
Co-authored-by: R.Sicart <roger.sicart@gmail.com>
2022-04-03 08:10:35 +02:00
Andrew Klychkov
2de30ed7cb
[stable-2] Backports (#324)
* Clarified error message about missing python modules (#279)

* Clarified error message about missing python modules, and tweak documentation to suggest overriding interpreter.

* Mention mysqlclient as another option

* Correct mysqlclient suggestions from python2 to python3

Co-authored-by: Matthew Exon <git.mexon@spamgourmet.com>
(cherry picked from commit 82baf7508c)

* Copy ignore-2.13.txt to ignore-2.14.txt (#323)

* Copy ignore-2.13.txt to ignore-2.14.txt

* Fix sanity

(cherry picked from commit c16b2428e8)

Co-authored-by: Matthew Exon <github2.mexon@neverbox.com>
2022-04-02 17:33:46 +02:00
patchback[bot]
74ab876064
mysql_user: clarify behavior of priv parameter (#319) (#321)
(cherry picked from commit 5afae459dc)

Co-authored-by: Andrew Klychkov <aklychko@redhat.com>
2022-03-25 14:14:44 +01:00
patchback[bot]
203338024f
Fix roles CI (#316) (#317)
(cherry picked from commit e6e661b87f)

Co-authored-by: Andrew Klychkov <aklychko@redhat.com>
2022-03-24 06:54:56 +01:00
patchback[bot]
82cde26b11
Setup patchback bot config file (resolves #310) (#311) (#312)
(cherry picked from commit 55458f5b0b)

Co-authored-by: R.Sicart <roger.sicart@gmail.com>
2022-03-17 09:51:14 +01:00
Andrew Klychkov
66c340e951
Add IF EXISTS clause to DROP USER statement (#307) (#308)
* Add IF EXISTS clause to DROP USER statement

* Add a changelog fragment

* Fix exception

(cherry picked from commit 3a452faeb0)
2022-03-15 15:30:23 +01:00
Andrew Klychkov
fecc9866e3
Release 2.3.5 commit (#303) 2022-03-14 15:17:21 +01:00
Andrew Klychkov
95c649cce2
Fix the collection to work with mysqlclient connector (#301) 2022-03-14 14:41:38 +01:00
Andrew Klychkov
a516c1a6ad
Update requirements (#300) 2022-03-14 12:01:18 +01:00
Andrew Klychkov
fc16243349
Fix role integration tests for mariadb (#299)
Co-authored-by: R.Sicart <roger.sicart@gmail.com>
2022-03-14 11:47:28 +01:00
Andrew Klychkov
cb960ef182
Enable and fix integration tests for devel (#297) 2022-03-14 11:13:17 +01:00
Andrew Klychkov
d7c6bddefa
Release 2.3.4 commit (#288) 2022-02-16 11:24:17 +01:00
Andrew Klychkov
8b2c418538
[stable-2] Backport from main to stable-2 (#287)
* Update CONTRIBUTORS file (#278)

(cherry picked from commit cbdf51234a)

* Honor the set_default_role_all parameter (#282)

The set_default_role_all parameter is documented, but does nothing. This PR fixes this.

(cherry picked from commit f547b66d35)

* Add a changelog fragment to PR 282 (#283)

(cherry picked from commit 952e1666d8)

Co-authored-by: Benoit Garret <benoit.garret_github@gadz.org>
2022-02-16 11:11:55 +01:00
Andrew Klychkov
9d9ea46603
Release 2.3.3 commit (#276) 2022-01-18 11:04:04 +01:00
Andrew Klychkov
9c76f1a566
[stable-2] Use vendored version of distutils.version (#269) (#273)
* Prepare for distutils.version being removed in Python 3.12 (#267)

* Prepare for distutils.version being removed in Python 3.12

* Update plugins/module_utils/version.py

Co-authored-by: Felix Fontein <felix@fontein.de>

Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit d9e12b85ad)

* Use vendored version of distutils.version (#269)

* Use vendored version of distutils.version

* Correct fragment

* Update plugins/module_utils/version.py

Co-authored-by: Felix Fontein <felix@fontein.de>

Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit 9c575b4762)
2022-01-18 10:39:22 +01:00
Andrew Klychkov
a2c0bf4f0a
Release 2.3.2 commit (#253) 2021-11-29 15:52:22 +01:00
Andrew Klychkov
7aab5cc04f
Backport of PRs to stable-2 (#251)
* Allow the "%" character in database name (#227)

The naming rules for MySQL/MariaDB identifiers, when quoted, allow the
`%` character.

However, currently, the use of the `%` character in database names
results in mismatch or missing databases.

- Rewrite query to identify the databases in the catalog using
  `information_schema` instead of `SHOW DATABASES LIKE`
- Escape the `%` character in `CREATE DATABASE` query.

Signed-off-by: Nicolas Payart <npayart@gmail.com>
(cherry picked from commit 6b12435b2b)

* mysql_db: Fix assert in tests suite (#239)

Signed-off-by: Nicolas Payart <npayart@gmail.com>
(cherry picked from commit 5522e45284)

* mysql_db: Improve tests (#240)

- Define variables "db_names" and "db_formats" in defaults
- Use of the "vars" option in includes instead of default parameters
  that might be overridden by a previous task
- Use of the "loop" option in includes instead of duplicating include
  tasks
- Use a nested loop on db_names and db_formats in state_dump_import test

Signed-off-by: Nicolas Payart <npayart@gmail.com>
(cherry picked from commit e4de13aabe)

* MAINTAINERS file: Add new maintainer (#248)

(cherry picked from commit d411a8e216)

Co-authored-by: Nicolas PAYART <koleo@users.noreply.github.com>
2021-11-29 11:50:08 +01:00
Andrew Klychkov
1cb39cce0a
Release 2.3.1 commit (#236) 2021-10-19 12:41:22 +02:00
Andrew Klychkov
55a8ecd64e
[stable-2] Backport stable 2 5 (#235)
* Copy ignore-2.12.txt to ignore-2.13.txt (#225)

(cherry picked from commit 4f205ef540)

* CI matrix update (#226)

* CI matrix update

* Fix test_mysql_user

* Fix CI

* Fix CI

* Fix CI

* Fix CI

* Fix CI

(cherry picked from commit fc984b28aa)

* integration tests: remove superfluous debug task (#228)

* integration tests: remove superfluous debug task

* Turn off integration tests against devel

(cherry picked from commit f47d4635f1)

* mysql_user: fix broken compatibility for priviledge aliases (#233)

* mysql_user: fix broken compatibility for priviledge aliases

* add changelog fragment

* fix changelog fragment

* Improve formatting

(cherry picked from commit bb3e9fd3fa)
2021-10-19 12:20:30 +02:00
Andrew Klychkov
a6bacefc41
Release 2.3.0 commit (#220) 2021-09-23 13:39:01 +02:00
Andrew Klychkov
c5676ff0c9
[stable-2] Backport stable 2 4 (#219)
* Fix wrong impl for mysql (#210)

If 'mariadb' in version info, the db instance should be mariadb(reverse in code) rather than mysql.

(cherry picked from commit 663590689f)

* Update README.md (#216)

(cherry picked from commit 4de0e25ea0)

* mysql_user: replace VALID_PRIVS by get_valid_privs() function (#217)

* mysql_user: replace VALID_PRIVS by get_valid_privs() function

* Add EXTRA_PRIVS in case we need to add more privs in the future

* Add changelog fragment

(cherry picked from commit 0ce1fa1634)

Co-authored-by: int32bit <krystism@gmail.com>
Co-authored-by: R.Sicart <roger.sicart@gmail.com>
2021-09-23 12:51:32 +02:00
Andrew Klychkov
0c462c84ff
Release 2.2.0 commit (#218) 2021-09-23 11:32:06 +02:00
Andrew Klychkov
77bd3bfa2e
[stable-2] mysql_info: fix TypeError failure when there are databases that do not contain tables (#205) (#208)
* mysql_info: fix TypeError failure when there are databases that do not contain tables (#205)

* mysql_info: fix TypeError failure when there are databases that do not contain tables

* Add changelog fragment

(cherry picked from commit a1f419d541)

* Fix sanity errors (#206)

(cherry picked from commit 8a17e43eae)
2021-08-20 09:50:41 +02:00
Andrew Klychkov
2508c420ce
Update galaxy.yml after release (#202) 2021-08-11 11:12:34 +02:00
Andrew Klychkov
04c0f9f049
Release 2.2.0-a1 commit (#201) 2021-08-11 10:20:05 +02:00
Andrew Klychkov
a8e2c5290b
mysql_role: new module (#189) (#200)
* mysql_role: new module

* fixes

* fixes

* Add the role class

* Check if role exists

* role.add()

* role.__get_members

* tmp

* tmp

* Change tests

* Fix

* Fix

* add_members()

* get_privs()

* tmp

* __extract_grants() filler version

* Before big work

* tmp

* drop()

* tmp

* tmp

* Big changes

* Fix

* append_members, detach_members, append_privs

* tmp

* admin option

* Add tests

* Add tests

* Fix tests

* Remove debug warning

* Fix tests

* Add documentation

* Fix MariaDB case

* Fix MariaDB

* Fix MariaDB

* Fix MariaDB

* Fix MariaDB

* Fix MariaDB

* Fix

* Fix

* Remove debug warning

* Add try-except block

* tmp

* tmp

* tmp

* Fix

* Add err handling

* Add user check

* Check admin in db

* Fix CI

* Fix CI

* Fix CI

* Fix CI

* Fix

* Add mutually exclusive options

* Small refactoring, documenting

* Documenting, refactoring

* Change docs

* Refactoring

* Refactoring

* Refactoring

* Add unit tests

* Update README.md

(cherry picked from commit ce2b269f84)
2021-08-11 09:46:50 +02:00
Andrew Klychkov
fa62fd30d8
Release 2.1.1 commit (#199) 2021-08-11 08:47:16 +02:00
Andrew Klychkov
0c261b76d6
mysql_query: correctly reflect changed status in replace statements (#193) (#195)
* mysql_query: correctly reflect changed status in replace statements.

* Fix the wrong indent.

(cherry picked from commit 9055bb4c8c)

Co-authored-by: Tong He <68936428+unnecessary-username@users.noreply.github.com>
2021-08-10 14:25:29 +02:00
Andrew Klychkov
5d7451aed6
[stable-2] Doc PRs backport (#194)
* Update README.md (#183)

* Update README.md

* Change IRC ref

(cherry picked from commit 69012a2eb9)

* README: fix link (#184)

(cherry picked from commit 8ab6ea7714)

* README: fix the channel name (#185)

(cherry picked from commit cd759924fd)

* CONTRIBUTING.md: replace the content with a link to Ansible contributing guidelines (#187)

(cherry picked from commit 56a214885a)

* Update README (#186)

* Update README

* Fix

* Fix

* Fix

* Fix

* Add Libera.Chat link

(cherry picked from commit adb201a795)

* fix typo (#190)

"optoin" -> "option"

(cherry picked from commit 596ba0cedb)

* Update README.md (#191)

* Update README.md

* Update README.md

(cherry picked from commit 6f02cb266a)

* Add MAINTAINING.md, update README.md (#192)

(cherry picked from commit 0fabb2b77a)

Co-authored-by: Baptiste Mille-Mathias <baptiste.millemathias@gmail.com>
2021-08-10 14:15:49 +02:00
Andrew Klychkov
2e748efb02
[stable-2] Backport of all the doc PRs merged to main since the last backporting (#182)
* Add CONTRIBUTORS file (#166)

(cherry picked from commit ac927fdb08)

* Add documentation for privs with functions and procedures (#169)

(cherry picked from commit 6bce48e771)

* Update README.md (#168)

* Update README.md

* Fix

* Add MAINTAINERS file

(cherry picked from commit 479edd81d1)

* Improve wording in README (#170)

* Improve wording in README

* Update README.md

Co-authored-by: Jorge Rodriguez (A.K.A. Tiriel) <jorge.rodriguez@tiriel.eu>

Co-authored-by: Jorge Rodriguez (A.K.A. Tiriel) <jorge.rodriguez@tiriel.eu>
(cherry picked from commit c909aa2182)

* Update REVIEW_CHECKLIST.md (#171)

(cherry picked from commit 2236110bae)

* README: add a note how people can complain (#172)

* README: add a note how people can complain

* Change

* Improve

* Update README.md

Co-authored-by: Jorge Rodriguez (A.K.A. Tiriel) <jorge.rodriguez@tiriel.eu>

Co-authored-by: Jorge Rodriguez (A.K.A. Tiriel) <jorge.rodriguez@tiriel.eu>
(cherry picked from commit be4e84a92a)

* README: fix typos (#174)

(cherry picked from commit 2a80c301a6)

* README.md: Add link to IRC (#175)

(cherry picked from commit 3335a95ba5)

* README.md, CONTRIBUTING.md: add links to the Maintainer guidelines (#179)

(cherry picked from commit 8fad3f85b8)

* Update README (#181)

(cherry picked from commit 6d9288d19b)

Co-authored-by: Alexander Skiba <ghostlyrics@gmail.com>
2021-06-09 10:38:17 +02:00
Andrew Klychkov
a68c83f7b5
Update next expected release version in galaxy.yml (#158) 2021-04-23 16:07:29 +02:00
Andrew Klychkov
b32380a9e1
Release 2.1.0 commit (#157) 2021-04-23 15:11:57 +02:00
Andrew Klychkov
34a300d5f0
mysql: revert changes made in PR 116 (#153) (#155)
* mysql: revert changes made in PR 116

* Add changelog fragment

* Fix CI

* Fix CI

* Fix CI

* Update CI

* Fix CI

(cherry picked from commit 738343d64c)
2021-04-23 14:08:36 +02:00
Andrew Klychkov
d9651f37d3
mysql_replication: Add aliases to "master" options, add alternatives to "master" state options, add announcement (#150) (#152)
* mysql_replication: Add aliases, add alternatives for the state option, announce major changes

* Change tests

* Add changelog fragment

* Fix changelog

* Update plugins/modules/mysql_replication.py

Co-authored-by: Jorge Rodriguez (A.K.A. Tiriel) <jorge.rodriguez@tiriel.eu>

* Update plugins/modules/mysql_replication.py

Co-authored-by: Jorge Rodriguez (A.K.A. Tiriel) <jorge.rodriguez@tiriel.eu>

Co-authored-by: Jorge Rodriguez (A.K.A. Tiriel) <jorge.rodriguez@tiriel.eu>
(cherry picked from commit c8f9b1cd3f)
2021-04-23 07:31:02 +02:00
Andrew Klychkov
da7e73ef6e
mysql_replication: add deprecation warning about future replacement of Is_Slave and Is_Master return values, add alternatives (#147) (#149)
* mysql_replication: add deprecation warning about future replacement of Is_Slave and Is_Master return values, add alternatives

* Add changelog fragment

(cherry picked from commit 853db5a2a4)
2021-04-16 07:23:53 +02:00
Andrew Klychkov
5fbac22486 Update collection version to the next expected in galaxy.yml 2021-04-15 10:48:56 +02:00
Andrew Klychkov
900899740b Release 2.0.0 commit 2021-04-15 10:21:17 +02:00
134 changed files with 2840 additions and 7486 deletions

View file

@ -1,6 +1,6 @@
--- ---
name: Plugins CI name: Plugins CI
on: # yamllint disable-line rule:truthy on:
push: push:
paths: paths:
- 'plugins/**' - 'plugins/**'
@ -13,21 +13,19 @@ on: # yamllint disable-line rule:truthy
- '.github/workflows/ansible-test-plugins.yml' - '.github/workflows/ansible-test-plugins.yml'
schedule: schedule:
- cron: '0 6 * * *' - cron: '0 6 * * *'
workflow_dispatch:
jobs: jobs:
sanity: sanity:
name: "Sanity (${{ matrix.ansible }})" name: "Sanity (Ansible: ${{ matrix.ansible }})"
runs-on: ubuntu-22.04 runs-on: ubuntu-20.04
strategy: strategy:
matrix: matrix:
ansible: ansible:
- stable-2.16 - stable-2.12
- stable-2.17 - stable-2.13
- stable-2.18 - stable-2.14
- devel
steps: steps:
# https://github.com/ansible-community/ansible-test-gh-action
- name: Perform sanity testing - name: Perform sanity testing
uses: ansible-community/ansible-test-gh-action@release/v1 uses: ansible-community/ansible-test-gh-action@release/v1
with: with:
@ -35,134 +33,149 @@ jobs:
testing-type: sanity testing-type: sanity
pull-request-change-detection: true pull-request-change-detection: true
# Use this to chose which version of Python vs Ansible to test:
# https://docs.ansible.com/ansible/latest/reference_appendices/release_and_maintenance.html#ansible-core-control-node-python-support
integration: integration:
name: "Integration (${{ matrix.ansible }}, DB: ${{ matrix.db_engine_name }} ${{ matrix.db_engine_version }}, connector: ${{ matrix.connector_name }} ${{ matrix.connector_version }})" name: "Integration (Python: ${{ matrix.python }}, Ansible: ${{ matrix.ansible }}, DB: ${{ matrix.db_engine_name }} ${{ matrix.db_engine_version }}, connector: ${{ matrix.connector_name }} ${{ matrix.connector_version }})"
runs-on: ubuntu-22.04 runs-on: ubuntu-20.04
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
ansible: ansible:
- stable-2.16 - stable-2.12
- stable-2.17 - stable-2.13
- stable-2.18 - stable-2.14
- devel
db_engine_name: db_engine_name:
- mysql - mysql
- mariadb - mariadb
db_engine_version: db_engine_version:
- '8.0.38' - 5.7.40
- '8.4.1' - 8.0.31
- '10.11.8' - 10.4.27
- '11.4.5' - 10.5.18
- 10.6.11
python:
- '3.8'
- '3.9'
- '3.10'
connector_name: connector_name:
- pymysql - pymysql
- mysqlclient - mysqlclient
connector_version: connector_version:
- '0.9.3' - 0.7.11
- '1.0.2' - 0.9.3
- '1.1.1' # Before we can activate test with pymysql 1.0.2 we should debug the
- '2.0.1' # following plugins:
- '2.0.3' #
- '2.1.1' # mysql_query:
# test "Assert that create table IF NOT EXISTS is not changed with pymysql" failed
include: #
# mysql_replication:
# RHEL8 context # test "Assert that startreplica is not changed" failed
- connector_name: pymysql # - 1.0.2
connector_version: '0.10.1' - 2.0.1
ansible: stable-2.16 - 2.0.3
db_engine_name: mariadb - 2.1.1
db_engine_version: '10.11.8'
# RHEL9 context
# - connector_name: pymysql
# connector_version: '1.1.1'
# ansible: stable-2.17
# db_engine_name: mariadb
# db_engine_version: '10.11.8'
# This tests is already included in the matrix, no need repeating
exclude: exclude:
- db_engine_name: mysql
db_engine_version: 10.4.27
- db_engine_name: mysql - db_engine_name: mysql
db_engine_version: '10.11.8' db_engine_version: 10.5.18
- db_engine_name: mysql - db_engine_name: mysql
db_engine_version: '11.4.5' db_engine_version: 10.6.11
- db_engine_name: mariadb - db_engine_name: mariadb
db_engine_version: '8.0.38' db_engine_version: 5.7.40
- db_engine_name: mariadb - db_engine_name: mariadb
db_engine_version: '8.4.1' db_engine_version: 8.0.31
- connector_name: pymysql - connector_name: pymysql
connector_version: '2.0.1' connector_version: 2.0.1
- connector_name: pymysql - connector_name: pymysql
connector_version: '2.0.3' connector_version: 2.0.3
- connector_name: pymysql - connector_name: pymysql
connector_version: '2.1.1' connector_version: 2.1.1
- connector_name: mysqlclient - connector_name: mysqlclient
connector_version: '0.9.3' connector_version: 0.7.11
- connector_name: mysqlclient - connector_name: mysqlclient
connector_version: '1.0.2' connector_version: 0.9.3
- connector_name: mysqlclient - connector_name: mysqlclient
connector_version: '1.1.1' connector_version: 1.0.2
- db_engine_version: '8.0.38' - db_engine_name: mariadb
ansible: stable-2.17 connector_version: 0.7.11
- db_engine_version: '10.11.8' - db_engine_version: 5.7.40
ansible: stable-2.17 python: '3.9'
- db_engine_version: '8.0.38' - db_engine_version: 5.7.40
ansible: devel python: '3.10'
- db_engine_version: '10.11.8' - db_engine_version: 5.7.40
ansible: devel ansible: stable-2.13
- db_engine_version: '8.4.1' - db_engine_version: 5.7.40
connector_version: '0.9.3' ansible: stable-2.14
- db_engine_version: '8.4.1' - db_engine_version: 8.0.31
connector_version: '1.0.2' python: '3.8'
- db_engine_version: '8.4.1' - db_engine_version: 8.0.31
connector_version: '2.0.1' python: '3.8'
- db_engine_version: '8.4.1' - db_engine_version: 10.4.27
connector_version: '2.0.3' python: '3.10'
- db_engine_version: '10.11.8' - db_engine_version: 10.6.11
connector_version: '0.9.3' python: '3.8'
- db_engine_version: '10.11.8' - db_engine_version: 10.6.11
connector_version: '1.0.2' python: '3.9'
- db_engine_version: '10.11.8' - python: '3.8'
connector_version: '2.0.1' connector_version: 2.0.3
- db_engine_version: '10.11.8' - python: '3.8'
connector_version: '2.0.1' connector_version: 2.1.1
- db_engine_version: '10.11.8' - python: '3.9'
ansible: stable-2.15 connector_version: 0.7.11
- db_engine_version: '8.4.1' - python: '3.9'
ansible: stable-2.15 connector_version: 2.0.1
- connector_version: '1.1.1' - python: '3.9'
db_engine_version: '8.0.38' connector_version: 2.1.1
- connector_version: '1.1.1' - python: '3.10'
db_engine_version: '10.11.8' connector_version: 0.7.11
- python: '3.10'
connector_version: 0.9.3
- python: '3.10'
connector_version: 2.0.1
- python: '3.10'
connector_version: 2.0.3
- python: '3.8'
ansible: stable-2.13
- python: '3.8'
ansible: stable-2.14
- python: '3.9'
ansible: stable-2.12
- python: '3.10'
ansible: stable-2.12
services: services:
db_primary: db_primary:
@ -175,7 +188,7 @@ jobs:
# We write our own health-cmd because the mariadb container does not # We write our own health-cmd because the mariadb container does not
# provide a healthcheck # provide a healthcheck
options: >- 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-start-period 10s
--health-interval 10s --health-interval 10s
--health-timeout 5s --health-timeout 5s
@ -189,7 +202,7 @@ jobs:
ports: ports:
- 3308:3306 - 3308:3306
options: >- 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-start-period 10s
--health-interval 10s --health-interval 10s
--health-timeout 5s --health-timeout 5s
@ -203,7 +216,7 @@ jobs:
ports: ports:
- 3309:3306 - 3309:3306
options: >- 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-start-period 10s
--health-interval 10s --health-interval 10s
--health-timeout 5s --health-timeout 5s
@ -215,22 +228,9 @@ jobs:
- name: Restart MySQL server with settings for replication - name: Restart MySQL server with settings for replication
run: | run: |
db_ver="${{ matrix.db_engine_version }}" docker exec ${{ job.services.db_primary.id }} bash -c 'echo -e [mysqld]\\nserver-id=1\\nlog-bin=/var/lib/mysql/primary-bin > /etc/mysql/conf.d/replication.cnf'
maj="${db_ver%.*.*}" docker exec ${{ job.services.db_replica1.id }} bash -c 'echo -e [mysqld]\\nserver-id=2\\nlog-bin=/var/lib/mysql/replica1-bin > /etc/mysql/conf.d/replication.cnf'
maj_min="${db_ver%.*}" docker exec ${{ job.services.db_replica2.id }} bash -c 'echo -e [mysqld]\\nserver-id=3\\nlog-bin=/var/lib/mysql/replica2-bin > /etc/mysql/conf.d/replication.cnf'
min="${maj_min#*.}"
if [[ "${{ matrix.db_engine_name }}" == "mysql" && "$maj" -eq 8 && "$min" -ge 2 ]]; then
prima_conf='[mysqld]\\nserver-id=1\\nlog-bin=/var/lib/mysql/primary-bin\\nmysql-native-password=1'
repl1_conf='[mysqld]\\nserver-id=2\\nlog-bin=/var/lib/mysql/replica1-bin\\nmysql-native-password=1'
repl2_conf='[mysqld]\\nserver-id=3\\nlog-bin=/var/lib/mysql/replica2-bin\\nmysql-native-password=1'
else
prima_conf='[mysqld]\\nserver-id=1\\nlog-bin=/var/lib/mysql/primary-bin'
repl1_conf='[mysqld]\\nserver-id=2\\nlog-bin=/var/lib/mysql/replica1-bin'
repl2_conf='[mysqld]\\nserver-id=3\\nlog-bin=/var/lib/mysql/replica2-bin'
fi
docker exec -e cnf=$prima_conf ${{ job.services.db_primary.id }} bash -c 'echo -e ${cnf//\\n/\n} > /etc/mysql/conf.d/replication.cnf'
docker exec -e cnf=$repl1_conf ${{ job.services.db_replica1.id }} bash -c 'echo -e ${cnf//\\n/\n} > /etc/mysql/conf.d/replication.cnf'
docker exec -e cnf=$repl2_conf ${{ job.services.db_replica2.id }} bash -c 'echo -e ${cnf//\\n/\n} > /etc/mysql/conf.d/replication.cnf'
docker restart -t 30 ${{ job.services.db_primary.id }} docker restart -t 30 ${{ job.services.db_primary.id }}
docker restart -t 30 ${{ job.services.db_replica1.id }} docker restart -t 30 ${{ job.services.db_replica1.id }}
docker restart -t 30 ${{ job.services.db_replica2.id }} docker restart -t 30 ${{ job.services.db_replica2.id }}
@ -242,13 +242,48 @@ jobs:
${{ job.services.db_primary.id }} ${{ job.services.db_primary.id }}
| grep healthy && [[ "$SECONDS" -lt 120 ]]; do sleep 1; done | grep healthy && [[ "$SECONDS" -lt 120 ]]; do sleep 1; done
- name: Compute docker_image - Set python_version_flat
run: >
echo "python_version_flat=$(echo ${{ matrix.python }}
| tr -d '.')" >> $GITHUB_ENV
- name: Compute docker_image - Set connector_version_flat
run: >
echo "connector_version_flat=$(echo ${{ matrix.connector_version }}
|tr -d .)" >> $GITHUB_ENV
- name: Compute docker_image - Set db_engine_version_flat
run: >
echo "db_engine_version_flat=$(echo ${{ matrix.db_engine_version }}
| awk -F '.' '{print $1 $2}')" >> $GITHUB_ENV
- name: Compute docker_image - Set db_client
run: >
if [[ ${{ env.db_engine_version_flat }} == 57 ]]; then
echo "db_client=my57" >> $GITHUB_ENV;
else
echo "db_client=$(echo ${{ matrix.db_engine_name }})" >> $GITHUB_ENV;
fi
- name: Set docker_image
run: >
docker_image_multiline=("
ghcr.io/ansible-collections/community.mysql\
/test-container-${{ env.db_client }}\
-py${{ env.python_version_flat }}\
-${{ matrix.connector_name }}${{ env.connector_version_flat }}\
:latest")
echo "docker_image=$(printf '%s' $docker_image_multiline)"
>> $GITHUB_ENV
- name: >- - name: >-
Perform integration testing against Perform integration testing against
Ansible version ${{ matrix.ansible }} Ansible version ${{ matrix.ansible }}
under Python ${{ matrix.python }}
uses: ansible-community/ansible-test-gh-action@release/v1 uses: ansible-community/ansible-test-gh-action@release/v1
with: with:
ansible-core-version: ${{ matrix.ansible }} ansible-core-version: ${{ matrix.ansible }}
docker-image: ubuntu2204
pre-test-cmd: >- pre-test-cmd: >-
echo Setting db_engine_name to "${{ matrix.db_engine_name }}"...; echo Setting db_engine_name to "${{ matrix.db_engine_name }}"...;
echo -n "${{ matrix.db_engine_name }}" echo -n "${{ matrix.db_engine_name }}"
@ -267,66 +302,44 @@ jobs:
echo -n "${{ matrix.connector_version }}" echo -n "${{ matrix.connector_version }}"
> tests/integration/connector_version; > tests/integration/connector_version;
echo Setting Python version to "${{ matrix.python }}"...;
echo -n "${{ matrix.python }}"
> tests/integration/python;
echo Setting Ansible version to "${{ matrix.ansible }}"...; echo Setting Ansible version to "${{ matrix.ansible }}"...;
echo -n "${{ matrix.ansible }}" echo -n "${{ matrix.ansible }}"
> tests/integration/ansible > tests/integration/ansible
docker-image: ${{ env.docker_image }}
target-python-version: ${{ matrix.python }}
testing-type: integration testing-type: integration
integration-retry-on-error: false
units: units:
runs-on: ubuntu-22.04 runs-on: ubuntu-20.04
name: Units (Ⓐ${{ matrix.ansible }}, Python${{ matrix.python }}) name: Units (Ⓐ${{ matrix.ansible }})
strategy: strategy:
# As soon as the first unit test fails, # As soon as the first unit test fails,
# cancel the others to free up the CI queue # cancel the others to free up the CI queue
fail-fast: true fail-fast: true
matrix: matrix:
ansible: ansible:
- stable-2.16 - stable-2.12
- stable-2.17 - stable-2.13
- stable-2.18 - stable-2.14
- devel
python: python:
- '3.8' - 3.8
- '3.9' - 3.9
- '3.10'
- '3.11'
exclude: exclude:
- python: '3.8' - python: '3.8'
ansible: stable-2.16 ansible: stable-2.13
- python: '3.8' - python: '3.8'
ansible: stable-2.17 ansible: stable-2.14
- python: '3.8'
ansible: devel
- python: '3.9' - python: '3.9'
ansible: stable-2.15 ansible: stable-2.12
- python: '3.9'
ansible: stable-2.17
- python: '3.9'
ansible: devel
- python: '3.10'
ansible: stable-2.15
- python: '3.10'
ansible: stable-2.16
- python: '3.11'
ansible: stable-2.15
- python: '3.11'
ansible: stable-2.16
steps: steps:
- name: >- - name: >-
Perform unit testing against Perform unit testing against
Ansible version ${{ matrix.ansible }} and Ansible version ${{ matrix.ansible }}
python version ${{ matrix.python }}
uses: ansible-community/ansible-test-gh-action@release/v1 uses: ansible-community/ansible-test-gh-action@release/v1
with: with:
ansible-core-version: ${{ matrix.ansible }} ansible-core-version: ${{ matrix.ansible }}

View file

@ -1,6 +1,6 @@
--- ---
name: Roles CI name: Roles CI
on: # yamllint disable-line rule:truthy on:
push: push:
paths: paths:
- 'roles/**' - 'roles/**'
@ -15,7 +15,7 @@ on: # yamllint disable-line rule:truthy
jobs: jobs:
molecule: molecule:
name: "Molecule (Python: ${{ matrix.python }}, Ansible: ${{ matrix.ansible }}, MySQL: ${{ matrix.mysql }})" name: "Molecule (Python: ${{ matrix.python }}, Ansible: ${{ matrix.ansible }}, MySQL: ${{ matrix.mysql }})"
runs-on: ubuntu-22.04 runs-on: ubuntu-20.04
env: env:
PY_COLORS: 1 PY_COLORS: 1
ANSIBLE_FORCE_COLOR: 1 ANSIBLE_FORCE_COLOR: 1
@ -24,36 +24,36 @@ jobs:
mysql: mysql:
- 2.0.12 - 2.0.12
ansible: ansible:
- stable-2.15 - stable-2.11
- stable-2.16 - stable-2.12
- stable-2.17 - stable-2.13
- devel
python: python:
- '3.8' - 3.6
- '3.9' - 3.8
- '3.10' - 3.9
exclude: exclude:
- python: 3.6
ansible: stable-2.12
- python: 3.6
ansible: stable-2.13
- python: 3.8 - python: 3.8
ansible: stable-2.17 ansible: stable-2.11
- python: 3.9
ansible: stable-2.17
- python: 3.8 - python: 3.8
ansible: devel ansible: stable-2.13
- python: 3.9 - python: 3.9
ansible: devel ansible: stable-2.11
- python: 3.9
ansible: stable-2.12
steps: steps:
- name: Check out code - name: Check out code
uses: actions/checkout@v3 uses: actions/checkout@v2
with: with:
path: ansible_collections/community/mysql path: ansible_collections/community/mysql
- name: Set up Python ${{ matrix.python }} - name: Set up Python ${{ matrix.python }}
uses: actions/setup-python@v4 uses: actions/setup-python@v2
with: with:
python-version: ${{ matrix.python }} python-version: ${{ matrix.python }}

View file

@ -0,0 +1,67 @@
---
name: Build Docker Image for ansible-test
on:
workflow_call:
inputs:
registry:
required: true
type: string
image_name:
required: true
type: string
context:
required: true
type: string
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
# Requirement to use 'context' in docker/build-push-action@v3
- name: Checkout repository
uses: actions/checkout@v3
# https://github.com/docker/login-action
- name: Log into registry ${{ inputs.registry }}
uses: docker/login-action@v2
with:
registry: ${{ inputs.registry }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
# https://github.com/docker/metadata-action
- name: Extract Docker metadata (tags, labels)
id: meta
uses: docker/metadata-action@v4
with:
images:
"${{ inputs.registry }}\
/${{ github.repository }}\
/${{ inputs.image_name }}"
tags: latest
# Setting up Docker Buildx with docker-container driver is required
# at the moment to be able to use a subdirectory with Git context
#
# https://github.com/docker/setup-buildx-action
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
# https://github.com/docker/build-push-action
- name: Build and push Docker image with Buildx
id: build-and-push
uses: docker/build-push-action@v3
with:
context: ${{ inputs.context }}
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max

View file

@ -0,0 +1,21 @@
---
name: Docker Image CI mariadb-py310-mysqlclient211
on:
push:
paths:
- 'test-containers/mariadb-py310-mysqlclient211/**'
- '.github/workflows/docker-image-mariadb-py310-mysqlclient211.yml'
- '.github/workflows/build-docker-image.yml'
branches-ignore:
- stable-*
jobs:
call-workflow-passing-data:
uses: ./.github/workflows/build-docker-image.yml
secrets: inherit
with:
registry: ghcr.io
image_name: test-container-mariadb-py310-mysqlclient211
context: test-containers/mariadb-py310-mysqlclient211

View file

@ -0,0 +1,21 @@
---
name: Docker Image CI mariadb-py310-pymysql102
on:
push:
paths:
- 'test-containers/mariadb-py310-pymysql102/**'
- '.github/workflows/docker-image-mariadb-py310-pymysql102.yml'
- '.github/workflows/build-docker-image.yml'
branches-ignore:
- stable-*
jobs:
call-workflow-passing-data:
uses: ./.github/workflows/build-docker-image.yml
secrets: inherit
with:
registry: ghcr.io
image_name: test-container-mariadb-py310-pymysql102
context: test-containers/mariadb-py310-pymysql102

View file

@ -0,0 +1,21 @@
---
name: Docker Image CI mariadb-py38-mysqlclient201
on:
push:
paths:
- 'test-containers/mariadb-py38-mysqlclient201/**'
- '.github/workflows/docker-image-mariadb-py38-mysqlclient201.yml'
- '.github/workflows/build-docker-image.yml'
branches-ignore:
- stable-*
jobs:
call-workflow-passing-data:
uses: ./.github/workflows/build-docker-image.yml
secrets: inherit
with:
registry: ghcr.io
image_name: test-container-mariadb-py38-mysqlclient201
context: test-containers/mariadb-py38-mysqlclient201

View file

@ -0,0 +1,21 @@
---
name: Docker Image CI mariadb-py38-pymysql093
on:
push:
paths:
- 'test-containers/mariadb-py38-pymysql093/**'
- '.github/workflows/docker-image-mariadb-py38-pymysql093.yml'
- '.github/workflows/build-docker-image.yml'
branches-ignore:
- stable-*
jobs:
call-workflow-passing-data:
uses: ./.github/workflows/build-docker-image.yml
secrets: inherit
with:
registry: ghcr.io
image_name: test-container-mariadb-py38-pymysql093
context: test-containers/mariadb-py38-pymysql093

View file

@ -0,0 +1,21 @@
---
name: Docker Image CI mariadb-py39-mysqlclient203
on:
push:
paths:
- 'test-containers/mariadb-py39-mysqlclient203/**'
- '.github/workflows/docker-image-mariadb-py39-mysqlclient203.yml'
- '.github/workflows/build-docker-image.yml'
branches-ignore:
- stable-*
jobs:
call-workflow-passing-data:
uses: ./.github/workflows/build-docker-image.yml
secrets: inherit
with:
registry: ghcr.io
image_name: test-container-mariadb-py39-mysqlclient203
context: test-containers/mariadb-py39-mysqlclient203

View file

@ -0,0 +1,21 @@
---
name: Docker Image CI mariadb-py39-pymysql093
on:
push:
paths:
- 'test-containers/mariadb-py39-pymysql093/**'
- '.github/workflows/docker-image-mariadb-py39-pymysql093.yml'
- '.github/workflows/build-docker-image.yml'
branches-ignore:
- stable-*
jobs:
call-workflow-passing-data:
uses: ./.github/workflows/build-docker-image.yml
secrets: inherit
with:
registry: ghcr.io
image_name: test-container-mariadb-py39-pymysql093
context: test-containers/mariadb-py39-pymysql093

View file

@ -0,0 +1,21 @@
---
name: Docker Image CI my57-py38-mysqlclient201
on:
push:
paths:
- 'test-containers/my57-py38-mysqlclient201/**'
- '.github/workflows/docker-image-my57-py38-mysqlclient201.yml'
- '.github/workflows/build-docker-image.yml'
branches-ignore:
- stable-*
jobs:
call-workflow-passing-data:
uses: ./.github/workflows/build-docker-image.yml
secrets: inherit
with:
registry: ghcr.io
image_name: test-container-my57-py38-mysqlclient201
context: test-containers/my57-py38-mysqlclient201

View file

@ -0,0 +1,21 @@
---
name: Docker Image CI my57-py38-pymysql0711
on:
push:
paths:
- 'test-containers/my57-py38-pymysql0711/**'
- '.github/workflows/docker-image-my57-py38-pymysql0711.yml'
- '.github/workflows/build-docker-image.yml'
branches-ignore:
- stable-*
jobs:
call-workflow-passing-data:
uses: ./.github/workflows/build-docker-image.yml
secrets: inherit
with:
registry: ghcr.io
image_name: test-container-my57-py38-pymysql0711
context: test-containers/my57-py38-pymysql0711

View file

@ -0,0 +1,21 @@
---
name: Docker Image CI my57-py38-pymysql093
on:
push:
paths:
- 'test-containers/my57-py38-pymysql093/**'
- '.github/workflows/docker-image-my57-py38-pymysql093.yml'
- '.github/workflows/build-docker-image.yml'
branches-ignore:
- stable-*
jobs:
call-workflow-passing-data:
uses: ./.github/workflows/build-docker-image.yml
secrets: inherit
with:
registry: ghcr.io
image_name: test-container-my57-py38-pymysql093
context: test-containers/my57-py38-pymysql093

View file

@ -0,0 +1,21 @@
---
name: Docker Image CI mysql-py310-mysqlclient211
on:
push:
paths:
- 'test-containers/mysql-py310-mysqlclient211/**'
- '.github/workflows/docker-image-mysql-py310-mysqlclient211.yml'
- '.github/workflows/build-docker-image.yml'
branches-ignore:
- stable-*
jobs:
call-workflow-passing-data:
uses: ./.github/workflows/build-docker-image.yml
secrets: inherit
with:
registry: ghcr.io
image_name: test-container-mysql-py310-mysqlclient211
context: test-containers/mysql-py310-mysqlclient211

View file

@ -0,0 +1,21 @@
---
name: Docker Image CI mysql-py310-pymysql102
on:
push:
paths:
- 'test-containers/mysql-py310-pymysql102/**'
- '.github/workflows/docker-image-mysql-py310-pymysql102.yml'
- '.github/workflows/build-docker-image.yml'
branches-ignore:
- stable-*
jobs:
call-workflow-passing-data:
uses: ./.github/workflows/build-docker-image.yml
secrets: inherit
with:
registry: ghcr.io
image_name: test-container-mysql-py310-pymysql102
context: test-containers/mysql-py310-pymysql102

View file

@ -0,0 +1,21 @@
---
name: Docker Image CI mysql-py38-mysqlclient201
on:
push:
paths:
- 'test-containers/mysql-py38-mysqlclient201/**'
- '.github/workflows/docker-image-mysql-py38-mysqlclient201.yml'
- '.github/workflows/build-docker-image.yml'
branches-ignore:
- stable-*
jobs:
call-workflow-passing-data:
uses: ./.github/workflows/build-docker-image.yml
secrets: inherit
with:
registry: ghcr.io
image_name: test-container-mysql-py38-mysqlclient201
context: test-containers/mysql-py38-mysqlclient201

View file

@ -0,0 +1,21 @@
---
name: Docker Image CI mysql-py38-pymysql093
on:
push:
paths:
- 'test-containers/mysql-py38-pymysql093/**'
- '.github/workflows/docker-image-mysql-py38-pymysql093.yml'
- '.github/workflows/build-docker-image.yml'
branches-ignore:
- stable-*
jobs:
call-workflow-passing-data:
uses: ./.github/workflows/build-docker-image.yml
secrets: inherit
with:
registry: ghcr.io
image_name: test-container-mysql-py38-pymysql093
context: test-containers/mysql-py38-pymysql093

View file

@ -0,0 +1,21 @@
---
name: Docker Image CI mysql-py39-mysqlclient203
on:
push:
paths:
- 'test-containers/mysql-py39-mysqlclient203/**'
- '.github/workflows/docker-image-mysql-py39-mysqlclient203.yml'
- '.github/workflows/build-docker-image.yml'
branches-ignore:
- stable-*
jobs:
call-workflow-passing-data:
uses: ./.github/workflows/build-docker-image.yml
secrets: inherit
with:
registry: ghcr.io
image_name: test-container-mysql-py39-mysqlclient203
context: test-containers/mysql-py39-mysqlclient203

View file

@ -0,0 +1,21 @@
---
name: Docker Image CI mysql-py39-pymysql093
on:
push:
paths:
- 'test-containers/mysql-py39-pymysql093/*'
- '.github/workflows/docker-image-mysql-py39-pymysql093.yml'
- '.github/workflows/build-docker-image.yml'
branches-ignore:
- stable-*
jobs:
call-workflow-passing-data:
uses: ./.github/workflows/build-docker-image.yml
secrets: inherit
with:
registry: ghcr.io
image_name: test-container-mysql-py39-pymysql093
context: test-containers/mysql-py39-pymysql093

View file

@ -1,258 +1,38 @@
==================================================== ========================================
Community MySQL and MariaDB Collection Release Notes Community MySQL Collection Release Notes
==================================================== ========================================
.. contents:: Topics .. contents:: Topics
This changelog describes changes after version 2.0.0.
v3.13.0 v2.4.2
=======
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
=======
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_info - adds the count of tables for each database to the returned values. It is possible to exclude this new field using the ``db_table_count`` exclusion filter. (https://github.com/ansible-collections/community.mysql/pull/691)
Bugfixes
--------
- mysql_user,mysql_role - The sql_mode ANSI_QUOTES affects how the modules mysql_user and mysql_role compare the existing privileges with the configured privileges, as well as decide whether double quotes or backticks should be used in the GRANT statements. Pointing out in issue 671, the modules mysql_user and mysql_role allow users to enable/disable ANSI_QUOTES in session variable (within a DB session, the session variable always overwrites the global one). But due to the issue, the modules do not check for ANSI_MODE in the session variable, instead, they only check in the GLOBAL one.That behavior is not only limiting the users' flexibility, but also not allowing users to explicitly disable ANSI_MODE to work around such bugs like https://bugs.mysql.com/bug.php?id=115953. (https://github.com/ansible-collections/community.mysql/issues/671)
v3.10.3
=======
Release Summary
---------------
This is a bugfix 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.
Bugfixes
--------
- mysql_user - add correct ``ed25519`` auth plugin handling when creating a user (https://github.com/ansible-collections/community.mysql/pull/676).
v3.10.2
=======
Release Summary
---------------
This is a bugfix 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.
Bugfixes
--------
- mysql_user - add correct ``ed25519`` auth plugin handling when creating a user (https://github.com/ansible-collections/community.mysql/issues/672).
v3.10.1
=======
Release Summary
---------------
This is a patch release of the ``community.mysql`` collection.
Besides a bugfix, it contains an important upcoming breaking-change information.
Deprecated Features
-------------------
- mysql_user - the ``user`` alias of the ``name`` argument has been deprecated and will be removed in collection version 5.0.0. Use the ``name`` argument instead.
Bugfixes
--------
- mysql_user - module makes changes when is executed with ``plugin_auth_string`` parameter and check mode.
v3.10.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_info - Add ``tls_requires`` returned value for the ``users_info`` filter (https://github.com/ansible-collections/community.mysql/pull/628).
- mysql_info - return a database server engine used (https://github.com/ansible-collections/community.mysql/issues/644).
- mysql_replication - Adds support for `CHANGE REPLICATION SOURCE TO` statement (https://github.com/ansible-collections/community.mysql/issues/635).
- mysql_replication - Adds support for `SHOW BINARY LOG STATUS` and `SHOW BINLOG STATUS` on getprimary mode.
- mysql_replication - Improve detection of IsReplica and IsPrimary by inspecting the dictionary returned from the SQL query instead of relying on variable types. This ensures compatibility with changes in the connector or the output of SHOW REPLICA STATUS and SHOW MASTER STATUS, allowing for easier maintenance if these change in the future.
- mysql_user - Add salt parameter to generate static hash for `caching_sha2_password` and `sha256_password` plugins.
Deprecated Features
-------------------
- collection - support of mysqlclient connector is deprecated - use PyMySQL connector instead! We will stop testing against it in collection version 4.0.0 and remove the related code in 5.0.0 (https://github.com/ansible-collections/community.mysql/issues/654).
- mysql_info - The ``users_info`` filter returned variable ``plugin_auth_string`` contains the hashed password and it's misleading, it will be removed from community.mysql 4.0.0. Use the `plugin_hash_string` return value instead (https://github.com/ansible-collections/community.mysql/pull/629).
Bugfixes
--------
- mysql_info - Add ``plugin_hash_string`` to ``users_info`` filter's output. The existing ``plugin_auth_string`` contained the hashed password and thus is missleading, it will be removed from community.mysql 4.0.0. (https://github.com/ansible-collections/community.mysql/pull/629).
- mysql_user - Added a warning to update_password's on_new_username option if multiple accounts with the same username but different passwords exist (https://github.com/ansible-collections/community.mysql/pull/642).
- mysql_user - Fix ``tls_requires`` not removing ``SSL`` and ``X509`` when sets as empty (https://github.com/ansible-collections/community.mysql/pull/628).
- mysql_user - Fix idempotence when using variables from the ``users_info`` filter of ``mysql_info`` as an input (https://github.com/ansible-collections/community.mysql/pull/628).
- mysql_user - Fixed an IndexError in the update_password functionality introduced in PR https://github.com/ansible-collections/community.mysql/pull/580 and released in community.mysql 3.8.0. If you used this functionality, please avoid versions 3.8.0 to 3.9.0 (https://github.com/ansible-collections/community.mysql/pull/642).
- mysql_user - add correct ``ed25519`` auth plugin handling (https://github.com/ansible-collections/community.mysql/issues/6).
- mysql_variables - fix the module always changes on boolean values (https://github.com/ansible-collections/community.mysql/issues/652).
v3.9.0
====== ======
Release Summary Release Summary
--------------- ---------------
This is a minor release of the ``community.mysql`` collection. Maintenance release of ``community.mysql`` to inform users that the collection version 2 is EOL. There were no functional changes made in this release.
This changelog contains all changes to the modules and plugins in this
collection that have been made after the previous release.
Major Changes Major Changes
------------- -------------
- Collection version 2.*.* is EOL, no more bugfixes will be backported. Please consider upgrading to the latest version. - Collection version 2.*.* is EOL, no more bugfixes will be backported. Please consider upgrading to the latest version.
Minor Changes v2.4.1
-------------
- mysql_user - add the ``password_expire`` and ``password_expire_interval`` arguments to implement the password expiration management for mysql user (https://github.com/ansible-collections/community.mysql/pull/598).
- mysql_user - add user attribute support via the ``attributes`` parameter and return value (https://github.com/ansible-collections/community.mysql/pull/604).
Bugfixes
--------
- mysql_info - the ``slave_status`` filter was returning an empty list on MariaDB with multiple replication channels. It now returns all channels by running ``SHOW ALL SLAVES STATUS`` for MariaDB servers (https://github.com/ansible-collections/community.mysql/issues/603).
v3.8.0
====== ======
Release Summary Release Summary
--------------- ---------------
This is the minor release of the ``community.mysql`` collection. Maintenance release of ``community.mysql`` major version 2. Only contains changes to the CI. There is no functional difference between 2.4.0 and 2.4.1.
This changelog contains all changes to the modules and plugins in this
collection that have been made after the previous release.
Major Changes v2.4.0
-------------
- The community.mysql collection no longer supports ``ansible-core 2.12`` and ``ansible-core 2.13``. While we take no active measures to prevent usage and there are no plans to introduce incompatible code to the modules, we will stop testing those versions. Both are or will soon be End of Life and if you are still using them, you should consider upgrading to the ``latest Ansible / ansible-core 2.15 or later`` as soon as possible (https://github.com/ansible-collections/community.mysql/pull/574).
- mysql_role - the ``column_case_sensitive`` argument's default value will be changed to ``true`` in community.mysql 4.0.0. If your playbook expected the column to be automatically uppercased for your roles privileges, you should set this to false explicitly (https://github.com/ansible-collections/community.mysql/issues/578).
- mysql_user - the ``column_case_sensitive`` argument's default value will be changed to ``true`` in community.mysql 4.0.0. If your playbook expected the column to be automatically uppercased for your users privileges, you should set this to false explicitly (https://github.com/ansible-collections/community.mysql/issues/577).
Minor Changes
-------------
- mysql_info - add filter ``users_info`` (https://github.com/ansible-collections/community.mysql/pull/580).
- mysql_role - add ``column_case_sensitive`` option to prevent field names from being uppercased (https://github.com/ansible-collections/community.mysql/pull/569).
- mysql_user - add ``column_case_sensitive`` option to prevent field names from being uppercased (https://github.com/ansible-collections/community.mysql/pull/569).
v3.7.2
====== ======
Release Summary Release Summary
--------------- ---------------
This is a patch release of the community.mysql collection. This is a bugfix 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.
Bugfixes
--------
- mysql module utils - use the connection arguments ``db`` instead of ``database`` and ``passwd`` instead of ``password`` when running with MySQLdb < 2.0.0 (https://github.com/ansible-collections/community.mysql/pull/553).
v3.7.1
======
Release Summary
---------------
This is a patch 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.
Bugfixes
--------
- mysql module utils - use the connection arguments ``db`` instead of ``database`` and ``passwd`` instead of ``password`` when running with older mysql drivers (MySQLdb < 2.1.0 or PyMySQL < 1.0.0) (https://github.com/ansible-collections/community.mysql/pull/551).
v3.7.0
======
Release Summary
---------------
This is the 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 module utils - change deprecated connection parameters ``passwd`` and ``db`` to ``password`` and ``database`` (https://github.com/ansible-collections/community.mysql/pull/177).
- mysql_user - add ``MAX_STATEMENT_TIME`` support for mariadb to the ``resource_limits`` argument (https://github.com/ansible-collections/community.mysql/issues/211).
v3.6.0
======
Release Summary
---------------
This is the minor release of the ``community.mysql`` collection.
This changelog contains all changes to the modules and plugins in this collection This changelog contains all changes to the modules and plugins in this collection
that have been made after the previous release. that have been made after the previous release.
@ -260,98 +40,31 @@ Minor Changes
------------- -------------
- mysql_info - add ``connector_name`` and ``connector_version`` to returned values (https://github.com/ansible-collections/community.mysql/pull/497). - mysql_info - add ``connector_name`` and ``connector_version`` to returned values (https://github.com/ansible-collections/community.mysql/pull/497).
- mysql_role - enable auto_commit to avoid MySQL metadata table lock (https://github.com/ansible-collections/community.mysql/issues/479).
- mysql_user - add plugin_auth_string as optional parameter to use a specific pam service if pam/auth_pam plugin is used (https://github.com/ansible-collections/community.mysql/pull/445).
- mysql_user - add the ``session_vars`` argument to set session variables at the beginning of module execution (https://github.com/ansible-collections/community.mysql/issues/478).
- mysql_user - display a more informative invalid privilege exception. Changes the exception handling of the granting permission logic to show the query executed , params and the exception message granting privileges fails` (https://github.com/ansible-collections/community.mysql/issues/465).
- mysql_user - enable auto_commit to avoid MySQL metadata table lock (https://github.com/ansible-collections/community.mysql/issues/479).
- setup_mysql - update MySQL tarball URL (https://github.com/ansible-collections/community.mysql/pull/491).
Bugfixes v2.3.9
--------
- mysql_user - when revoke privs consists only of ``GRANT``, a 2nd revoke query is executed with empty privs to revoke that ended in an SQL exception (https://github.com/ansible-collections/community.mysql/pull/503).
- mysql_variables - add uppercase character pattern to regex to allow GLOBAL variables containing uppercase characters. This recognizes variable names used in Galera, for example, ``wsrep_OSU_method``, which breaks the normal pattern of all lowercase characters (https://github.com/ansible-collections/community.mysql/pull/501).
v3.5.1
====== ======
Release Summary Release Summary
--------------- ---------------
This is the patch release of the ``community.mysql`` collection. This is the patch 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.
Bugfixes
--------
- mysql_user, mysql_role - mysql/mariadb recent versions translate 'ALL PRIVILEGES' to a list of specific privileges. That caused a change every time we modified user privileges. This fix compares privs before and after user modification to avoid this infinite change (https://github.com/ansible-collections/community.mysql/issues/77).
v3.5.0
======
Release Summary
---------------
This is the minor release of the ``community.mysql`` collection.
This changelog contains all changes to the modules in this collection This changelog contains all changes to the modules in this collection
that have been added after the release of ``community.mysql`` 3.4.0. that have been added after the release of ``community.mysql`` 2.3.8.
Minor Changes
-------------
- mysql_replication - add a new option: ``primary_ssl_verify_server_cert`` (https://github.com//pull/435).
Bugfixes
--------
- mysql_user - grant option was revoked accidentally when modifying users. This fix revokes grant option only when privs are setup to do that (https://github.com/ansible-collections/community.mysql/issues/77#issuecomment-1209693807).
v3.4.0
======
Release Summary
---------------
This is the minor release of the ``community.mysql`` collection.
This changelog contains all changes to the modules in this collection
that have been added after the release of ``community.mysql`` 3.3.0.
Major Changes
-------------
- mysql_db - the ``pipefail`` argument's default value will be changed to ``true`` in community.mysql 4.0.0. If your target machines do not use ``bash`` as a default interpreter, set ``pipefail`` to ``false`` explicitly. However, we strongly recommend setting up ``bash`` as a default and ``pipefail=true`` as it will protect you from getting broken dumps you don't know about (https://github.com/ansible-collections/community.mysql/issues/407).
Minor Changes
-------------
- mysql_db - add the ``chdir`` argument to avoid failings when a dump file contains relative paths (https://github.com/ansible-collections/community.mysql/issues/395).
- mysql_db - add the ``pipefail`` argument to avoid broken dumps when ``state`` is ``dump`` and compression is used (https://github.com/ansible-collections/community.mysql/issues/256).
Bugfixes Bugfixes
-------- --------
- Include ``simplified_bsd.txt`` license file for various module utils. - Include ``simplified_bsd.txt`` license file for various module utils.
- mysql_db - Using compression masks errors messages from mysql_dump. By default the fix is inactive to ensure retro-compatibility with system without bash. To activate the fix, use the module option ``pipefail=true`` (https://github.com/ansible-collections/community.mysql/issues/256).
- mysql_replication - when the ``primary_ssl`` argument is set to ``no``, the module will turn off SSL (https://github.com/ansible-collections/community.mysql/issues/393).
v3.3.0 v2.3.8
====== ======
Release Summary Release Summary
--------------- ---------------
This is the minor release of the ``community.mysql`` collection. This is the patch release of the ``community.mysql`` collection.
This changelog contains all changes to the modules in this collection This changelog contains all changes to the modules in this collection
that have been added after the release of ``community.mysql`` 3.2.1. that have been added after the release of ``community.mysql`` 2.3.7.
Minor Changes
-------------
- mysql_role - add the argument ``members_must_exist`` (boolean, default true). The assertion that the users supplied in the ``members`` argument exist is only executed when the new argument ``members_must_exist`` is ``true``, to allow opt-out (https://github.com/ansible-collections/community.mysql/pull/369).
- mysql_user - Add the option ``on_new_username`` to argument ``update_password`` to reuse the password (plugin and authentication_string) when creating a new user if some user with the same name already exists. If the existing user with the same name have varying passwords, the password from the arguments is used like with ``update_password: always`` (https://github.com/ansible-collections/community.mysql/pull/365).
- mysql_user - Add the result field ``password_changed`` (boolean). It is true, when the user got a new password. When the user was created with ``update_password: on_new_username`` and an existing password was reused, ``password_changed`` is false (https://github.com/ansible-collections/community.mysql/pull/365).
Bugfixes Bugfixes
-------- --------
@ -361,7 +74,7 @@ Bugfixes
- mysql_role - in some cases (when "SHOW GRANTS" did not use backticks for quotes), no unwanted members were detached from the role (and redundant "GRANT" statements were executed for wanted members). This is fixed by querying the existing role members from the mysql.role_edges (MySQL) or mysql.roles_mapping (MariaDB) tables instead of parsing the "SHOW GRANTS" output (https://github.com/ansible-collections/community.mysql/pull/368). - mysql_role - in some cases (when "SHOW GRANTS" did not use backticks for quotes), no unwanted members were detached from the role (and redundant "GRANT" statements were executed for wanted members). This is fixed by querying the existing role members from the mysql.role_edges (MySQL) or mysql.roles_mapping (MariaDB) tables instead of parsing the "SHOW GRANTS" output (https://github.com/ansible-collections/community.mysql/pull/368).
- mysql_user - fix logic when ``update_password`` is set to ``on_create`` for users using ``plugin*`` arguments (https://github.com/ansible-collections/community.mysql/issues/334). The ``on_create`` sets ``password`` to None for old mysql_native_authentication but not for authentiation methods which uses the ``plugin*`` arguments. This PR changes this so ``on_create`` also exchange ``plugin``, ``plugin_hash_string``, ``plugin_auth_string`` to None in the list of arguments to change - mysql_user - fix logic when ``update_password`` is set to ``on_create`` for users using ``plugin*`` arguments (https://github.com/ansible-collections/community.mysql/issues/334). The ``on_create`` sets ``password`` to None for old mysql_native_authentication but not for authentiation methods which uses the ``plugin*`` arguments. This PR changes this so ``on_create`` also exchange ``plugin``, ``plugin_hash_string``, ``plugin_auth_string`` to None in the list of arguments to change
v3.2.1 v2.3.7
====== ======
Release Summary Release Summary
@ -369,87 +82,156 @@ Release Summary
This is the patch release of the ``community.mysql`` collection. This is the patch release of the ``community.mysql`` collection.
This changelog contains all changes to the modules in this collection This changelog contains all changes to the modules in this collection
that have been added after the release of ``community.mysql`` 3.2.0. that have been added after the release of ``community.mysql`` 2.3.6.
Bugfixes Bugfixes
-------- --------
- Include ``PSF-license.txt`` file for ``plugins/module_utils/_version.py``. - Include ``PSF-license.txt`` file for ``plugins/module_utils/_version.py``.
v3.2.0 v2.3.6
====== ======
Release Summary Release Summary
--------------- ---------------
This is the minor release of the ``community.mysql`` collection. This is the patch release of the ``community.mysql`` collection.
This changelog contains all changes to the modules in this collection This changelog contains all changes to the modules in this collection
that have been added after the release of ``community.mysql`` 3.1.3. that have been added after the release of ``community.mysql`` 2.3.5.
Major Changes Major Changes
------------- -------------
- The community.mysql collection no longer supports ``Ansible 2.9`` and ``ansible-base 2.10``. While we take no active measures to prevent usage and there are no plans to introduce incompatible code to the modules, we will stop testing against ``Ansible 2.9`` and ``ansible-base 2.10``. Both will very soon be End of Life and if you are still using them, you should consider upgrading to the ``latest Ansible / ansible-core 2.11 or later`` as soon as possible (https://github.com/ansible-collections/community.mysql/pull/343). - The community.mysql collection no longer supports ``Ansible 2.9`` and ``ansible-base 2.10``. While we take no active measures to prevent usage and there are no plans to introduce incompatible code to the modules, we will stop testing against ``Ansible 2.9`` and ``ansible-base 2.10``. Both will very soon be End of Life and if you are still using them, you should consider upgrading to the ``latest Ansible / ansible-core 2.11 or later`` as soon as possible (https://github.com/ansible-collections/community.mysql/pull/343).
Minor Changes
-------------
- mysql_user and mysql_role: Add the argument ``subtract_privs`` (boolean, default false, mutually exclusive with ``append_privs``). If set, the privileges given in ``priv`` are revoked and existing privileges are kept (https://github.com/ansible-collections/community.mysql/pull/333).
Bugfixes Bugfixes
-------- --------
- mysql_role - remove redundant connection closing (https://github.com/ansible-collections/community.mysql/pull/330).
- mysql_user - fix missing dynamic privileges after revoke and grant privileges to user (https://github.com/ansible-collections/community.mysql/issues/120). - mysql_user - fix missing dynamic privileges after revoke and grant privileges to user (https://github.com/ansible-collections/community.mysql/issues/120).
- mysql_user - fix parsing privs when a user has roles assigned (https://github.com/ansible-collections/community.mysql/issues/231). - mysql_user - fix parsing privs when a user has roles assigned (https://github.com/ansible-collections/community.mysql/issues/231).
v3.1.3
======
Release Summary
---------------
This is the patch release of the ``community.mysql`` collection.
This changelog contains all changes to the modules in this collection
that have been added after the release of ``community.mysql`` 3.1.2.
Bugfixes
--------
- mysql_replication - fails when using the `primary_use_gtid` option with `slave_pos` or `replica_pos` (https://github.com/ansible-collections/community.mysql/issues/335).
- mysql_role - remove redundant connection closing (https://github.com/ansible-collections/community.mysql/pull/330).
- mysql_user - fix the possibility for a race condition that breaks certain (circular) replication configurations when ``DROP USER`` is executed on multiple nodes in the replica set. Adding ``IF EXISTS`` avoids the need to use ``sql_log_bin: no`` making the statement always replication safe (https://github.com/ansible-collections/community.mysql/pull/287). - mysql_user - fix the possibility for a race condition that breaks certain (circular) replication configurations when ``DROP USER`` is executed on multiple nodes in the replica set. Adding ``IF EXISTS`` avoids the need to use ``sql_log_bin: no`` making the statement always replication safe (https://github.com/ansible-collections/community.mysql/pull/287).
v3.1.2 v2.3.5
====== ======
Release Summary Release Summary
--------------- ---------------
This is the patch release of the ``community.mysql`` collection. This is the patch release of the ``community.mysql`` collection. This changelog contains all changes to the modules in this collection that have been added after the release of ``community.mysql`` 2.3.4.
This changelog contains all changes to the modules in this collection
that have been added after the release of ``community.mysql`` 3.1.1.
Bugfixes Bugfixes
-------- --------
- Collection core functions - fixes related to the mysqlclient Python connector (https://github.com/ansible-collections/community.mysql/issues/292). - Collection core functions - fixes related to the mysqlclient Python connector (https://github.com/ansible-collections/community.mysql/issues/292).
v3.1.1 v2.3.4
====== ======
Release Summary Release Summary
--------------- ---------------
This is the patch release of the ``community.mysql`` collection. This is the patch release of the ``community.mysql`` collection. This changelog contains all changes to the modules in this collection that have been added after the release of ``community.mysql`` 2.3.3.
This changelog contains all changes to the modules in this collection
that have been added after the release of ``community.mysql`` 3.1.0.
Bugfixes Bugfixes
-------- --------
- mysql_role - make the ``set_default_role_all`` parameter actually working (https://github.com/ansible-collections/community.mysql/pull/282). - mysql_role - make the ``set_default_role_all`` parameter actually working (https://github.com/ansible-collections/community.mysql/pull/282).
v3.1.0 v2.3.3
======
Release Summary
---------------
This is the patch release of the ``community.mysql`` collection. This changelog contains all changes to the modules in this collection that have been added after the release of ``community.mysql`` 2.3.2.
Bugfixes
--------
- Collection core functions - use vendored version of ``distutils.version`` instead of the deprecated Python standard library ``distutils`` (https://github.com/ansible-collections/community.mysql/pull/269).
v2.3.2
======
Release Summary
---------------
This is the patch release of the ``community.mysql`` collection.
This changelog contains all changes to the modules in this collection that
have been added after the release of ``community.mysql`` 2.3.1.
Bugfixes
--------
- mysql_db - Fix mismatch when database name contains a ``%`` character (https://github.com/ansible-collections/community.mysql/pull/227).
v2.3.1
======
Release Summary
---------------
This is the patch release of the ``community.mysql`` collection.
This changelog contains all changes to the modules in this collection that
have been added after the release of ``community.mysql`` 2.3.0.
Bugfixes
--------
- mysql_user - Fix crash reporting ``Invalid privileges specified`` when passing privileges that became aliases (https://github.com/ansible-collections/community.mysql/issues/232).
v2.3.0
======
Release Summary
---------------
This is the minor release of the ``community.mysql`` collection.
This changelog contains all changes to the modules in this collection that
have been added after the release of ``community.mysql`` 2.2.0.
Minor Changes
-------------
- mysql_user - replace VALID_PRIVS constant by get_valid_privs() function (https://github.com/ansible-collections/community.mysql/pull/217).
Bugfixes
--------
- mysql_info - fix TypeError failure when there are databases that do not contain tables (https://github.com/ansible-collections/community.mysql/issues/204).
v2.2.0
======
Release Summary
---------------
This is the minor release of the ``community.mysql`` collection.
This changelog contains all changes to the modules in this collection that
have been added after the release of ``community.mysql`` 2.1.1
New Modules
-----------
- mysql_role - Adds, removes, or updates a MySQL role
v2.1.1
======
Release Summary
---------------
This is the patch release of the ``community.mysql`` collection.
This changelog contains all changes to the modules in this collection that
have been added after the release of ``community.mysql`` 2.1.0.
Minor Changes
-------------
- mysql_query - correctly reflect changed status in replace statements (https://github.com/ansible-collections/community.mysql/pull/193).
v2.1.0
====== ======
Release Summary Release Summary
@ -457,33 +239,251 @@ Release Summary
This is the minor release of the ``community.mysql`` collection. This is the minor release of the ``community.mysql`` collection.
This changelog contains all changes to the modules in this collection This changelog contains all changes to the modules in this collection
that have been added after the release of ``community.mysql`` 3.0.0. that have been added after the release of ``community.mysql`` 2.0.0.
Major Changes
-------------
- mysql_replication - add deprecation warning that the ``Is_Slave`` and ``Is_Master`` return values will be replaced with ``Is_Primary`` and ``Is_Replica`` in ``community.mysql`` 3.0.0 (https://github.com/ansible-collections/community.mysql/pull/147).
- mysql_replication - the choices of the ``state`` option containing ``master`` will be finally replaced with the alternative ``primary`` choices in ``community.mysql`` 3.0.0, add deprecation warnings (https://github.com/ansible-collections/community.mysql/pull/150).
Minor Changes Minor Changes
------------- -------------
- Added explicit description of the supported versions of databases and connectors. Changes to the collection are **NOT** tested against database versions older than `mysql 5.7.31` and `mariadb 10.2.37` or connector versions older than `pymysql 0.7.10` and `mysqlclient 2.0.1`. (https://github.com/ansible-collections/community.mysql/discussions/141) - mysql_replication - add alternative (``primary``) choices to the ``state`` option choices containing ``master`` (https://github.com/ansible-collections/community.mysql/pull/150).
- mysql_user - added the ``force_context`` boolean option to set the default database context for the queries to be the ``mysql`` database. This way replication/binlog filters can catch the statements (https://github.com/ansible-collections/community.mysql/issues/265). - mysql_replication - add the ``Is_Primary`` and ``Is_Replica`` alternatives to the ``Is_Slave`` and ``Is_Master`` return values as a preparation for replacement in ``community.mysql`` 3.0.0 (https://github.com/ansible-collections/community.mysql/pull/147).
- mysql_replication - change ``master_`` options to ``primary_`` options, add aliases to keep compatibility (https://github.com/ansible-collections/community.mysql/pull/150).
Bugfixes Bugfixes
-------- --------
- Collection core functions - use vendored version of ``distutils.version`` instead of the deprecated Python standard library ``distutils`` (https://github.com/ansible-collections/community.mysql/pull/269). - mysql - revert changes of connector arguments made in pull request 116 causing the invalid keyword argument error (https://github.com/ansible-collections/community.mysql/pull/116).
v3.0.0 v2.0.0
====== ======
Release Summary Release Summary
--------------- ---------------
This is the major release of the ``community.mysql`` collection. This is release 2.0.0 of the ``community.mysql`` collection, released on 2021-04-15.
This changelog contains all breaking changes to the modules in this collection
that have been added after the release of ``community.mysql`` 2.3.2.
Breaking Changes / Porting Guide Major Changes
-------------------------------- -------------
- mysql_replication - remove ``Is_Slave`` and ``Is_Master`` return values (were replaced with ``Is_Primary`` and ``Is_Replica`` (https://github.com/ansible-collections/community.mysql/issues/145). - mysql_replication - the return value ``Is_Slave`` and ``Is_Master`` will be replaced with ``Is_Replica`` and ``Is_Primary`` in ``community.mysql`` 3.0.0 (https://github.com/ansible-collections/community.mysql/issues/145).
- mysql_replication - remove the mode options values containing ``master``/``slave`` and the master_use_gtid option ``slave_pos`` (were replaced with corresponding ``primary``/``replica`` values) (https://github.com/ansible-collections/community.mysql/issues/145). - mysql_replication - the word ``master`` in messages returned by the module will be replaced with ``primary`` in ``community.mysql`` 3.0.0 (https://github.com/ansible-collections/community.mysql/issues/145).
- mysql_user - remove support for the `REQUIRESSL` special privilege as it has ben superseded by the `tls_requires` option (https://github.com/ansible-collections/community.mysql/discussions/121). - mysql_replication - the word ``slave`` in messages returned by the module replaced with ``replica`` (https://github.com/ansible-collections/community.mysql/issues/98).
- mysql_user - validate privileges using database engine directly (https://github.com/ansible-collections/community.mysql/issues/234 https://github.com/ansible-collections/community.mysql/pull/243). Do not validate privileges in this module anymore. - mysql_user - the ``REQUIRESSL`` is an alias for the ``ssl`` key in the ``tls_requires`` option in ``community.mysql`` 2.0.0 and support will be dropped altogether in ``community.mysql`` 3.0.0 (https://github.com/ansible-collections/community.mysql/issues/121).
Minor Changes
-------------
- mysql module utils - change deprecated connection parameters ``passwd`` and ``db`` to ``password`` and ``database`` (https://github.com/ansible-collections/community.mysql/pull/116).
- mysql_collection - introduce codebabse split to handle divergences between MySQL and MariaDB (https://github.com/ansible-collections/community.mysql/pull/103).
- mysql_info - add `version.full` and `version.suffix` return values (https://github.com/ansible-collections/community.mysql/issues/114).
- mysql_user - deprecate the ``REQUIRESSL`` privilege (https://github.com/ansible-collections/community.mysql/issues/101).
Bugfixes
--------
- mysql_user - add support for ``REPLICA MONITOR`` privilege (https://github.com/ansible-collections/community.mysql/issues/105).
v1.3.0
======
Release Summary
---------------
This is the minor release of the ``community.mysql`` collection.
This changelog contains all changes to the modules in this collection
that have been added after the release of ``community.mysql`` 1.2.0.
Major Changes
-------------
- mysql_replication - the mode options values ``getslave``, ``startslave``, ``stopslave``, ``resetslave``, ``resetslaveall` and the master_use_gtid option ``slave_pos`` are deprecated (see the alternative values) and will be removed in ``community.mysql`` 3.0.0 (https://github.com/ansible-collections/community.mysql/pull/97).
- mysql_replication - the word ``SLAVE`` in messages returned by the module will be changed to ``REPLICA`` in ``community.mysql`` 2.0.0 (https://github.com/ansible-collections/community.mysql/issues/98).
Minor Changes
-------------
- mysql_replication - deprecate offending terminology, add alternative choices to the ``mode`` option (https://github.com/ansible-collections/community.mysql/issues/78).
Bugfixes
--------
- mysql_user - fix handling of INSERT, UPDATE, REFERENCES on columns (https://github.com/ansible-collections/community.mysql/issues/106).
- mysql_user - the module is not idempotent when SELECT on columns granted (https://github.com/ansible-collections/community.mysql/issues/99).
v1.2.0
======
Release Summary
---------------
This is the minor release of the ``community.mysql`` collection.
This changelog contains all changes to the modules in this collection
that have been added after the release of ``community.mysql`` 1.1.2.
Minor Changes
-------------
- mysql_user - refactor to reduce cursor.execute() calls in preparation for adding query logging (https://github.com/ansible-collections/community.mysql/pull/76).
Bugfixes
--------
- mysql_user - add ``SHOW_ROUTINE`` privilege support (https://github.com/ansible-collections/community.mysql/issues/86).
- mysql_user - fixed creating user with encrypted password in MySQL 8.0 (https://github.com/ansible-collections/community.mysql/pull/79).
v1.1.2
======
Release Summary
---------------
This is the patch release of the ``community.mysql`` collection.
This changelog contains all changes to the modules in this collection that
have been added after the release of ``community.mysql`` 1.1.1.
Minor Changes
-------------
- mysql_query - simple refactoring of query type check (https://github.com/ansible-collections/community.mysql/pull/58).
- mysql_user - simple refactoring of priv type check (https://github.com/ansible-collections/community.mysql/pull/58).
Bugfixes
--------
- mysql_db - fix false warning related to ``unsafe_login_password`` option (https://github.com/ansible-collections/community.mysql/issues/33).
- mysql_replication - fix crashes of mariadb >= 10.5.1 and mysql >= 8.0.22 caused by using deprecated terminology (https://github.com/ansible-collections/community.mysql/issues/70).
- mysql_user - fixed change detection when using append_privs (https://github.com/ansible-collections/community.mysql/pull/72).
v1.1.1
======
Release Summary
---------------
This is the patch release of the ``community.mysql`` collection.
This changelog contains all changes to the modules in this collection that
have been added after the release of ``community.mysql`` 1.1.0.
Bugfixes
--------
- mysql_query - fix failing when single-row query contains commas (https://github.com/ansible-collections/community.mysql/issues/51).
v1.1.0
======
Release Summary
---------------
This is the minor release of the ``community.mysql`` collection.
This changelog contains all changes to the modules in this collection that have been added after the release of ``community.mysql`` 1.0.2.
Minor Changes
-------------
- mysql modules - add the ``check_hostname`` option (https://github.com/ansible-collections/community.mysql/issues/28).
- mysql modules - patch the ``Connection`` class to add a destructor that ensures connections to the server are explicitly closed (https://github.com/ansible-collections/community.mysql/pull/44).
Bugfixes
--------
- mysql modules - fix crash when ``!includedir`` option is in config file (https://github.com/ansible-collections/community.mysql/issues/46).
v1.0.2
======
Release Summary
---------------
This is the patch release of the ``community.mysql`` collection.
This changelog contains all changes to the modules in this collection that have been added after the release of ``community.mysql`` 1.0.1.
Bugfixes
--------
- mysql_user - fix module's crash when modifying a user with ``host_all`` (https://github.com/ansible-collections/community.mysql/issues/39).
v1.0.1
======
Release Summary
---------------
This is the patch release of the ``community.mysql`` collection.
This changelog contains all changes to the modules in this collection that have been added after the release of ``community.mysql`` 1.0.0.
Bugfixes
--------
- mysql_db - fix false warning related to ``unsafe_login_password`` option (https://github.com/ansible-collections/community.mysql/issues/33).
- mysql_user - added tests to verify that TLS requirements are removed with an empty ``tls_requires`` option (https://github.com/ansible-collections/community.mysql/issues/20).
- mysql_user - correct procedure to check existing TLS requirements (https://github.com/ansible-collections/community.mysql/pull/26).
- mysql_user - minor syntax fixes (https://github.com/ansible-collections/community.mysql/pull/26).
v1.0.0
======
Release Summary
---------------
This is the first proper release of the ``community.mysql`` collection.
This changelog contains all changes to the modules in this collection that were added after the release of Ansible 2.9.0.
Minor Changes
-------------
- mysql_db - add ``master_data`` parameter (https://github.com/ansible/ansible/pull/66048).
- mysql_db - add ``skip_lock_tables`` option (https://github.com/ansible/ansible/pull/66688).
- mysql_db - add the ``check_implicit_admin`` parameter (https://github.com/ansible/ansible/issues/24418).
- mysql_db - add the ``dump_extra_args`` parameter (https://github.com/ansible/ansible/pull/67747).
- mysql_db - add the ``executed_commands`` returned value (https://github.com/ansible/ansible/pull/65498).
- mysql_db - add the ``force`` parameter (https://github.com/ansible/ansible/pull/65547).
- mysql_db - add the ``restrict_config_file`` parameter (https://github.com/ansible/ansible/issues/34488).
- mysql_db - add the ``unsafe_login_password`` parameter (https://github.com/ansible/ansible/issues/63955).
- mysql_db - add the ``use_shell`` parameter (https://github.com/ansible/ansible/issues/20196).
- mysql_info - add ``exclude_fields`` parameter (https://github.com/ansible/ansible/issues/63319).
- mysql_info - add ``global_status`` filter parameter option and return (https://github.com/ansible/ansible/pull/63189).
- mysql_info - add ``return_empty_dbs`` parameter to list empty databases (https://github.com/ansible/ansible/issues/65727).
- mysql_replication - add ``channel`` parameter (https://github.com/ansible/ansible/issues/29311).
- mysql_replication - add ``connection_name`` parameter (https://github.com/ansible/ansible/issues/46243).
- mysql_replication - add ``fail_on_error`` parameter (https://github.com/ansible/ansible/pull/66252).
- mysql_replication - add ``master_delay`` parameter (https://github.com/ansible/ansible/issues/51326).
- mysql_replication - add ``master_use_gtid`` parameter (https://github.com/ansible/ansible/pull/62648).
- mysql_replication - add ``queries`` return value (https://github.com/ansible/ansible/pull/63036).
- mysql_replication - add support of ``resetmaster`` choice to ``mode`` parameter (https://github.com/ansible/ansible/issues/42870).
- mysql_user - ``priv`` parameter can be string or dictionary (https://github.com/ansible/ansible/issues/57533).
- mysql_user - add TLS REQUIRES parameters (https://github.com/ansible-collections/community.mysql/pull/9).
- mysql_user - add ``plugin_auth_string`` parameter (https://github.com/ansible/ansible/pull/44267).
- mysql_user - add ``plugin_hash_string`` parameter (https://github.com/ansible/ansible/pull/44267).
- mysql_user - add ``plugin`` parameter (https://github.com/ansible/ansible/pull/44267).
- mysql_user - add the resource_limits parameter (https://github.com/ansible-collections/community.general/issues/133).
- mysql_variables - add ``mode`` parameter (https://github.com/ansible/ansible/issues/60119).
Bugfixes
--------
- mysql - dont mask ``mysql_connect`` function errors from modules (https://github.com/ansible/ansible/issues/64560).
- mysql_db - fix Broken pipe error appearance when state is import and the target file is compressed (https://github.com/ansible/ansible/issues/20196).
- mysql_db - fix bug in the ``db_import`` function introduced by https://github.com/ansible/ansible/pull/56721 (https://github.com/ansible/ansible/issues/65351).
- mysql_info - add parameter for __collect to get only what are wanted (https://github.com/ansible-collections/community.general/pull/136).
- mysql_replication - allow to pass empty values to parameters (https://github.com/ansible/ansible/issues/23976).
- mysql_user - Fix idempotence when long grant lists are used (https://github.com/ansible/ansible/issues/68044)
- mysql_user - Remove false positive ``no_log`` warning for ``update_password`` option
- mysql_user - add ``INVOKE LAMBDA`` privilege support (https://github.com/ansible-collections/community.general/issues/283).
- mysql_user - add missed privileges to support (https://github.com/ansible-collections/community.general/issues/617).
- mysql_user - fix ``host_all`` arguments conversion string formatting error (https://github.com/ansible/ansible/issues/29644).
- mysql_user - fix overriding password to the same (https://github.com/ansible-collections/community.general/issues/543).
- mysql_user - fix support privileges with underscore (https://github.com/ansible/ansible/issues/66974).
- mysql_user - fix the error No database selected (https://github.com/ansible/ansible/issues/68070).
- mysql_user - make sure current_pass_hash is a string before using it in comparison (https://github.com/ansible/ansible/issues/60567).
- mysql_variable - fix the module doesn't support variables name with dot (https://github.com/ansible/ansible/issues/54239).

View file

@ -1,80 +1,5 @@
# Contributing to this project # Contributing
In this guide, you will find information relevant for code contributions, though any other kinds of contribution mentioned in the [Ansible Contributing guidelines](https://docs.ansible.com/ansible/devel/community/index.html) are equally appreciated and valuable. Refer to the [Ansible Contributing guidelines](https://docs.ansible.com/ansible/devel/community/index.html) to learn how to contribute to this collection.
If you have any questions after reading, please contact the community via one or more of the [available channels](https://github.com/ansible-collections/community.mysql#communication). Any feedback on this guide is very welcome. Refer to the [review checklist](https://docs.ansible.com/ansible/devel/community/collection_contributors/collection_reviewing.html) when triaging issues or reviewing PRs.
## Reviewing open issue and pull requests
Refer to the [review checklist](https://docs.ansible.com/ansible/devel/community/collection_contributors/collection_reviewing.html) when triaging issues or reviewing pull requests (hereinafter PRs).
Most important things to pay attention to:
- Do not let major/breaking changes sneak into a minor/bugfix release! All such changes should be discussed in a dedicated issue, added to a corresponding milestone (which can be found or created in the project's Issues), and merged right before the major release. Take a look at similar issues to see what needs to be done and reflect on the steps you did/need to do in the issue.
- Every PR (except doc, refactoring, test-related, or a PR containing a new module/plugin) contains a [changelog fragment](https://docs.ansible.com/ansible/latest/community/development_process.html#creating-a-changelog-fragment). Let's give users a chance to know about the changes.
- Every new module `DOCUMENTATION` section contains the `version_added: 'x.y.z'` field. Besides the informative purpose, it is used by the changelog-generating tool to add a corresponding entry to the changelog. As the project follows SemVer, it is typically a next minor (x.y.0) version.
- Every new module argument contains the `version_added: 'x.y.z'` field. As the project follows SemVer, it is typically a next minor (x.y.0) version.
- Non-refactoring code changes (bugfixes, new features) are covered with, at least, integration tests! There can be exceptions but generally it is a requirement.
## Code contributions
If you want to submit a bugfix or new feature, refer to the [Quick-start development guide](https://docs.ansible.com/ansible/devel/community/create_pr_quick_start.html) first.
## Project-specific info
We assume you have read the [Quick-start development guide](https://docs.ansible.com/ansible/devel/community/create_pr_quick_start.html).
In order for any submitted PR to get merged, this project requires sanity, unit, and integration tests to pass.
Codecov job is there but not required.
We use the GitHub Actions platform to run the tests.
You can see the result in the bottom of every PR in the box listing the jobs and their results:
- Green checkmark: the test has been passed, no more action is needed.
- Red cross: the test has failed. You can see the reason by clicking the ``Details`` link. Fix them locally and push the commit.
Generally, all jobs must be green.
Sometimes, there can be failures unrelated to a PR, for example, when a test container is unavailable or there is another part of the code that does not satisfy recently introduced additional sanity checks.
If you think the failure does not relate to your changes, put a comment about it.
## CI testing
The jobs are launched automatically by GitHub Actions in every PR based on the [matrix](https://github.com/ansible-collections/community.mysql/blob/main/.github/workflows/ansible-test-plugins.yml).
As the project is included in `ansible` community package, it is a requirement for us to test against all supported `ansible-core` versions and corresponding Python versions.
To keep the matrix relevant, we are subscribed to the [news-for-maintainers](https://github.com/ansible-collections/news-for-maintainers) repository and the [Collection maintainers & contributors](https://forum.ansible.com/g/CollectionMaintainer) forum group to track announcements affecting CI.
If our matrix is permanently outdated, for example, when supported `ansible-core` versions are missed, the collections can get excluded from the package, so keep it updated!
Read more about our CI implementation in the [TESTING.md](https://github.com/ansible-collections/community.mysql/blob/main/TESTING.md) file.
## Adding tests
If you are new here, read the [Quick-start development guide](https://docs.ansible.com/ansible/devel/community/create_pr_quick_start.html) first.
When fixing a bug, first reproduce it by adding a task as reported to a suitable file under the ``tests/integration/targets/<module_name>/tasks/`` directory and run the integration tests as described below. The same is relevant for new features.
It is not necessary but if you want you can also add unit tests to a suitable file under the ``tests/units/`` directory and run them as described below.
## Checking your code locally
It will make your and other people's life a bit easier if you run the tests locally and fix all failures before pushing. If you're unable to run the tests locally, please create your PR as a **draft** to avoid reviewers being added automatically.
If you are new here, read the [Quick-start development guide](https://docs.ansible.com/ansible/devel/community/create_pr_quick_start.html) first.
We assume you [prepared your local environment](https://docs.ansible.com/ansible/devel/community/create_pr_quick_start.html#prepare-your-environment) as described in the guide before running the following commands. Otherwise, the command will fail.
### Sanity tests
``` console
$ ansible-test sanity path/to/changed_file.py --docker -v
```
### Integration tests
See the [TESTING.md](https://github.com/ansible-collections/community.mysql/blob/main/TESTING.md) file to learn how to run integration tests against different server/connector versions.
### Unit tests
``` console
$ ansible-test units tests/unit/plugins/unit_test_file.py --docker
```

View file

@ -17,11 +17,9 @@ amitk79
amree amree
Andersson007 Andersson007
andrewhowdencom andrewhowdencom
aneustroev
ansibot ansibot
anthonyxpalermo anthonyxpalermo
antonioribeiro antonioribeiro
Aohzan
apollo13 apollo13
aquach aquach
arcmop arcmop
@ -35,9 +33,6 @@ baldpale
banyek banyek
BarbzYHOOL BarbzYHOOL
Berbe Berbe
betanummeric
bigo8525
bizmate
bjne bjne
bmalynovytch bmalynovytch
bmildren bmildren
@ -50,7 +45,6 @@ candeira
caphrim007 caphrim007
cdalbergue cdalbergue
checkphi checkphi
chriscroome
chrismeyersfsu chrismeyersfsu
ChristopherGAndrews ChristopherGAndrews
cmodijk cmodijk
@ -61,14 +55,13 @@ CormacBracken
cosmix cosmix
cptMikky cptMikky
crashes crashes
d-lee
d-rupp
dagwieers dagwieers
damianmoore damianmoore
Davidffry Davidffry
denisemauldin denisemauldin
dennisurtubia
diclophis diclophis
d-lee
d-rupp
dmp1ce dmp1ce
dnelson dnelson
dramaley dramaley
@ -78,11 +71,9 @@ DSpeichert
dungdm93 dungdm93
dwagelaar dwagelaar
dylanjbarth dylanjbarth
E-M
einarc einarc
elpavel E-M
eowin eowin
eRadical
Ernest0x Ernest0x
esamattis esamattis
Everspace Everspace
@ -90,30 +81,24 @@ F21
faitno faitno
felixfontein felixfontein
flatrocks flatrocks
FlorianPerrot
fourjay fourjay
fraff fraff
francescsanjuanmrf
g00fy- g00fy-
geerlingguy geerlingguy
georgeOsdDev georgeOsdDev
ghjm ghjm
ghost ghost
GhostLyrics
giacmir giacmir
giorgio-v giorgio-v
gkoller gkoller
gotmax23
gottwald gottwald
gstorme gstorme
gundalow gundalow
hansbaer hansbaer
hchargois hchargois
hluaces hluaces
hubiongithub
hwali hwali
hyperfocus1338 hyperfocus1338
IBims1NicerTobi
igormukhingmailcom igormukhingmailcom
imjoseangel imjoseangel
infigoKriti infigoKriti
@ -155,7 +140,6 @@ kalaisubbiah
kenichi-ogawa-1988 kenichi-ogawa-1988
kkeane kkeane
klingac klingac
kmarse
koleo koleo
kotso kotso
kuntalFreshBooks kuntalFreshBooks
@ -166,7 +150,6 @@ ldesgrange
leeadh leeadh
LeonB LeonB
leucos leucos
lkthomas
loomsen loomsen
lorin lorin
lowwalker lowwalker
@ -178,8 +161,8 @@ markdorison
markotitel markotitel
marktheunissen marktheunissen
markuman markuman
matt-horwood-mayden
mattclay mattclay
matt-horwood-mayden
mavimo mavimo
maxamillion maxamillion
maxbube maxbube
@ -198,15 +181,11 @@ mkrizek
mmoya mmoya
mohag mohag
mohsenSy mohsenSy
moledzki
mpdehaan mpdehaan
MRMegaNova
MRwangyd MRwangyd
mstinsky
mverwijs mverwijs
mvgrimes mvgrimes
mysqlbox mysqlbox
n-cc
netmonk netmonk
nhojpatrick nhojpatrick
nicolas-g nicolas-g
@ -220,9 +199,7 @@ organman91
p53 p53
pakal pakal
paulbadcock paulbadcock
paulcampbell-ayroc
pennycoders pennycoders
perlun
petoju petoju
petracvv petracvv
pgrenaud pgrenaud
@ -243,14 +220,12 @@ richlv
riupie riupie
rndmh3ro rndmh3ro
robertdebock robertdebock
robertsilen
robpblake robpblake
rokka-n rokka-n
Roxyrob Roxyrob
roysmith roysmith
rsicart rsicart
rthouvenin rthouvenin
rujschafer
ruudk ruudk
samccann samccann
samdoran samdoran
@ -264,7 +239,6 @@ shrikeh
sivel sivel
skalfyfan skalfyfan
skoriy88 skoriy88
SoledaD208
sperantus sperantus
spoyd spoyd
steverweber steverweber
@ -285,22 +259,19 @@ time-palominodb
timorunge timorunge
Tomasthanes Tomasthanes
tomdymond tomdymond
tompal3
Tronde Tronde
tuhoanganh tuhoanganh
tvlooy tvlooy
tyll tyll
UncertaintyP UncertaintyP
unnecessary-username unnecessary-username
v-zhuravlev
vamshi8 vamshi8
vanne vanne
vdboor vdboor
vmahadev vmahadev
webknjaz v-zhuravlev
webmat webmat
wedi wedi
wfelipew
whysthatso whysthatso
willthames willthames
windowsansiblernew windowsansiblernew

View file

@ -1,3 +1,6 @@
betanummeric betanummeric
bmalynovytch
Jorge-Rodriguez
rsicart
laurent-indermuehle laurent-indermuehle
Andersson007 Andersson007 (andersson007_ in #ansible-community IRC/Matrix)

View file

@ -8,26 +8,33 @@ endif
# This match what GitHub Action will do. Disabled by default. # This match what GitHub Action will do. Disabled by default.
ifdef continue_on_errors ifdef continue_on_errors
_continue_on_errors = --continue-on-error _continue_on_errors = --retry-on-error --continue-on-error
endif endif
# Set command variables based on database engine
# Required for MariaDB 11+ which no longer includes mysql named compatible db_ver_tuple := $(subst ., , $(db_engine_version))
# executable symlinks db_engine_version_flat := $(word 1, $(db_ver_tuple))$(word 2, $(db_ver_tuple))
ifeq ($(db_engine_name),mysql)
_command = mysqld con_ver_tuple := $(subst ., , $(connector_version))
_health_cmd = mysqladmin connector_version_flat := $(word 1, $(con_ver_tuple))$(word 2, $(con_ver_tuple))$(word 3, $(con_ver_tuple))
py_ver_tuple := $(subst ., , $(python))
python_version_flat := $(word 1, $(py_ver_tuple))$(word 2, $(py_ver_tuple))
ifeq ($(db_engine_version_flat), 57)
db_client := my57
else else
_command = mariadbd db_client := $(db_engine_name)
_health_cmd = mariadb-admin
endif endif
.PHONY: test-integration .PHONY: test-integration
test-integration: test-integration:
@echo -n $(db_engine_name) > tests/integration/db_engine_name @echo -n $(db_engine_name) > tests/integration/db_engine_name
@echo -n $(db_engine_version) > tests/integration/db_engine_version @echo -n $(db_engine_version) > tests/integration/db_engine_version
@echo -n $(connector_name) > tests/integration/connector_name @echo -n $(connector_name) > tests/integration/connector_name
@echo -n $(connector_version) > tests/integration/connector_version @echo -n $(connector_version) > tests/integration/connector_version
@echo -n $(python) > tests/integration/python
@echo -n $(ansible) > tests/integration/ansible @echo -n $(ansible) > tests/integration/ansible
# Create podman network for systems missing it. Error can be ignored # Create podman network for systems missing it. Error can be ignored
@ -40,9 +47,9 @@ test-integration:
--env MYSQL_ROOT_PASSWORD=msandbox \ --env MYSQL_ROOT_PASSWORD=msandbox \
--network podman \ --network podman \
--publish 3307:3306 \ --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) \ docker.io/library/$(db_engine_name):$(db_engine_version) \
$(_command) mysqld
podman run \ podman run \
--detach \ --detach \
--replace \ --replace \
@ -51,9 +58,9 @@ test-integration:
--env MYSQL_ROOT_PASSWORD=msandbox \ --env MYSQL_ROOT_PASSWORD=msandbox \
--network podman \ --network podman \
--publish 3308:3306 \ --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) \ docker.io/library/$(db_engine_name):$(db_engine_version) \
$(_command) mysqld
podman run \ podman run \
--detach \ --detach \
--replace \ --replace \
@ -62,26 +69,13 @@ test-integration:
--env MYSQL_ROOT_PASSWORD=msandbox \ --env MYSQL_ROOT_PASSWORD=msandbox \
--network podman \ --network podman \
--publish 3309:3306 \ --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) \ docker.io/library/$(db_engine_name):$(db_engine_version) \
$(_command) mysqld
# Setup replication and restart containers using the same subshell to keep variables alive # Setup replication and restart containers
db_ver=$(db_engine_version); \ podman exec primary bash -c 'echo -e [mysqld]\\nserver-id=1\\nlog-bin=/var/lib/mysql/primary-bin > /etc/mysql/conf.d/replication.cnf'
maj="$${db_ver%.*.*}"; \ podman exec replica1 bash -c 'echo -e [mysqld]\\nserver-id=2\\nlog-bin=/var/lib/mysql/replica1-bin > /etc/mysql/conf.d/replication.cnf'
maj_min="$${db_ver%.*}"; \ podman exec replica2 bash -c 'echo -e [mysqld]\\nserver-id=3\\nlog-bin=/var/lib/mysql/replica2-bin > /etc/mysql/conf.d/replication.cnf'
min="$${maj_min#*.}"; \
if [[ "$(db_engine_name)" == "mysql" && "$$maj" -eq 8 && "$$min" -ge 2 ]]; then \
prima_conf='[mysqld]\\nserver-id=1\\nlog-bin=/var/lib/mysql/primary-bin\\nmysql-native-password=1'; \
repl1_conf='[mysqld]\\nserver-id=2\\nlog-bin=/var/lib/mysql/replica1-bin\\nmysql-native-password=1'; \
repl2_conf='[mysqld]\\nserver-id=3\\nlog-bin=/var/lib/mysql/replica2-bin\\nmysql-native-password=1'; \
else \
prima_conf='[mysqld]\\nserver-id=1\\nlog-bin=/var/lib/mysql/primary-bin'; \
repl1_conf='[mysqld]\\nserver-id=2\\nlog-bin=/var/lib/mysql/replica1-bin'; \
repl2_conf='[mysqld]\\nserver-id=3\\nlog-bin=/var/lib/mysql/replica2-bin'; \
fi; \
podman exec -e cnf="$$prima_conf" primary bash -c 'echo -e "$${cnf//\\n/\n}" > /etc/mysql/conf.d/replication.cnf'; \
podman exec -e cnf="$$repl1_conf" replica1 bash -c 'echo -e "$${cnf//\\n/\n}" > /etc/mysql/conf.d/replication.cnf'; \
podman exec -e cnf="$$repl2_conf" replica2 bash -c 'echo -e "$${cnf//\\n/\n}" > /etc/mysql/conf.d/replication.cnf'
# Don't restart a container unless it is healthy # Don't restart a container unless it is healthy
while ! podman healthcheck run primary && [[ "$$SECONDS" -lt 120 ]]; do sleep 1; done while ! podman healthcheck run primary && [[ "$$SECONDS" -lt 120 ]]; do sleep 1; done
podman restart -t 30 primary podman restart -t 30 primary
@ -100,8 +94,9 @@ test-integration:
https://github.com/ansible/ansible/archive/$(ansible).tar.gz; \ https://github.com/ansible/ansible/archive/$(ansible).tar.gz; \
set -x; \ set -x; \
ansible-test integration $(target) -v --color --coverage --diff \ ansible-test integration $(target) -v --color --coverage --diff \
--docker ubuntu2204 \ --docker ghcr.io/ansible-collections/community.mysql/test-container\
--docker-network podman $(_continue_on_errors) $(_keep_containers_alive); \ -$(db_client)-py$(python_version_flat)-$(connector_name)$(connector_version_flat):latest \
--docker-network podman $(_continue_on_errors) $(_keep_containers_alive) --python $(python); \
set +x set +x
# End of venv # End of venv
@ -109,6 +104,7 @@ test-integration:
rm tests/integration/db_engine_version rm tests/integration/db_engine_version
rm tests/integration/connector_name rm tests/integration/connector_name
rm tests/integration/connector_version rm tests/integration/connector_version
rm tests/integration/python
rm tests/integration/ansible rm tests/integration/ansible
ifndef keep_containers_alive ifndef keep_containers_alive
podman stop --time 0 --ignore primary replica1 replica2 podman stop --time 0 --ignore primary replica1 replica2

View file

@ -1,5 +1,5 @@
# MySQL and MariaDB collection for Ansible # MySQL collection for Ansible
[![Plugins CI](https://github.com/ansible-collections/community.mysql/workflows/Plugins%20CI/badge.svg?event=push)](https://github.com/ansible-collections/community.mysql/actions?query=workflow%3A"Plugins+CI") [![Codecov](https://img.shields.io/codecov/c/github/ansible-collections/community.mysql)](https://codecov.io/gh/ansible-collections/community.mysql) [![Discuss on Matrix at #mysql:ansible.com](https://img.shields.io/matrix/mysql:ansible.com.svg?server_fqdn=ansible-accounts.ems.host&label=Discuss%20on%20Matrix%20at%20%23mysql:ansible.com&logo=matrix)](https://matrix.to/#/#mysql:ansible.com) [![Plugins CI](https://github.com/ansible-collections/community.mysql/workflows/Plugins%20CI/badge.svg?event=push)](https://github.com/ansible-collections/community.mysql/actions?query=workflow%3A"Plugins+CI") [![Roles CI](https://github.com/ansible-collections/community.mysql/workflows/Roles%20CI/badge.svg?event=push)](https://github.com/ansible-collections/community.mysql/actions?query=workflow%3A"Roles+CI") [![Codecov](https://img.shields.io/codecov/c/github/ansible-collections/community.mysql)](https://codecov.io/gh/ansible-collections/community.mysql) [![Discuss on Matrix at #mysql:ansible.com](https://img.shields.io/matrix/mysql:ansible.com.svg?server_fqdn=ansible-accounts.ems.host&label=Discuss%20on%20Matrix%20at%20%23mysql:ansible.com&logo=matrix)](https://matrix.to/#/#mysql:ansible.com)
This collection is a part of the Ansible package. This collection is a part of the Ansible package.
@ -15,22 +15,6 @@ We follow the [Ansible Code of Conduct](https://docs.ansible.com/ansible/latest/
If you encounter abusive behavior violating the [Ansible Code of Conduct](https://docs.ansible.com/ansible/latest/community/code_of_conduct.html), please refer to the [policy violations](https://docs.ansible.com/ansible/latest/community/code_of_conduct.html#policy-violations) section of the Code of Conduct for information on how to raise a complaint. If you encounter abusive behavior violating the [Ansible Code of Conduct](https://docs.ansible.com/ansible/latest/community/code_of_conduct.html), please refer to the [policy violations](https://docs.ansible.com/ansible/latest/community/code_of_conduct.html#policy-violations) section of the Code of Conduct for information on how to raise a complaint.
## Communication
* Join the Ansible forum:
* [Get Help](https://forum.ansible.com/c/help/6): get help or help others.
* [Posts tagged with 'mysql'](https://forum.ansible.com/tag/mysql): leverage tags to narrow the scope.
* [MySQL Team](https://forum.ansible.com/g/MySQLTeam): by joining the team you will automatically get subscribed to the posts tagged with [mysql](https://forum.ansible.com/tag/mysql).
* [Social Spaces](https://forum.ansible.com/c/chat/4): gather and interact with fellow enthusiasts.
* [News & Announcements](https://forum.ansible.com/c/news/5): track project-wide announcements including social events.
* The Ansible [Bullhorn newsletter](https://docs.ansible.com/ansible/devel/community/communication.html#the-bullhorn): used to announce releases and important changes.
* Matrix chat:
* [#mysql:ansible.com](https://matrix.to/#/#mysql:ansible.com) room: questions on how to contribute to this collection.
For more information about communication, see the [Ansible communication guide](https://docs.ansible.com/ansible/devel/community/communication.html).
## Contributing ## Contributing
The content of this collection is made by [people](https://github.com/ansible-collections/community.mysql/blob/main/CONTRIBUTORS) just like you, a community of individuals collaborating on making the world better through developing automation software. The content of this collection is made by [people](https://github.com/ansible-collections/community.mysql/blob/main/CONTRIBUTORS) just like you, a community of individuals collaborating on making the world better through developing automation software.
@ -54,13 +38,21 @@ It is necessary for maintainers of this collection to be subscribed to:
They also should be subscribed to Ansible's [The Bullhorn newsletter](https://docs.ansible.com/ansible/devel/community/communication.html#the-bullhorn). They also should be subscribed to Ansible's [The Bullhorn newsletter](https://docs.ansible.com/ansible/devel/community/communication.html#the-bullhorn).
## Governance ## Communication
We, [the MySQL team](https://forum.ansible.com/g/MySQLTeam), use [the forum](https://forum.ansible.com/tag/mysql) posts tagged with `mysql` for general announcements and discussions. We announce releases and important changes through Ansible's [The Bullhorn newsletter](https://eepurl.com/gZmiEP). Be sure you are subscribed.
Join us on Matrix in the `#mysql:ansible.com` [room](https://matrix.to/#/#mysql:ansible.com), the `#users:ansible.com` [room](https://matrix.to/#/#users:ansible.com) (general use questions and support), `#ansible-community:ansible.com` [room](https://matrix.to/#/#community:ansible.com) (community and collection development questions), and other Matrix rooms or corresponding bridged Libera.Chat channels. See the [Ansible Communication Guide](https://docs.ansible.com/ansible/devel/community/communication.html) for details.
We take part in the global quarterly [Ansible Contributor Summit](https://github.com/ansible/community/wiki/Contributor-Summit) virtually or in-person. Track [The Bullhorn newsletter](https://eepurl.com/gZmiEP) and join us.
For more information about communication, refer to the [Ansible Communication guide](https://docs.ansible.com/ansible/devel/community/communication.html).
## Governance
The process of decision making in this collection is based on discussing and finding consensus among participants. The process of decision making in this collection is based on discussing and finding consensus among participants.
Every voice is important and every idea is valuable. If you have something on your mind, create an issue or dedicated forum [discussion](https://forum.ansible.com/new-topic?title=topic%20title&body=topic%20body&category=project&tags=mysql) and let's discuss it! Every voice is important and every idea is valuable. If you have something on your mind, create an issue or dedicated discussion and let's discuss it!
## Included content ## Included content
@ -73,7 +65,6 @@ Every voice is important and every idea is valuable. If you have something on yo
- [mysql_user](https://docs.ansible.com/ansible/latest/collections/community/mysql/mysql_user_module.html) - [mysql_user](https://docs.ansible.com/ansible/latest/collections/community/mysql/mysql_user_module.html)
- [mysql_variables](https://docs.ansible.com/ansible/latest/collections/community/mysql/mysql_variables_module.html) - [mysql_variables](https://docs.ansible.com/ansible/latest/collections/community/mysql/mysql_variables_module.html)
## Releases Support Timeline ## Releases Support Timeline
We maintain each major release (1.x.y, 2.x.y, ...) for two years after the next major version is released. We maintain each major release (1.x.y, 2.x.y, ...) for two years after the next major version is released.
@ -82,54 +73,44 @@ Here is the table for the support timeline:
- 1.x.y: released 2020-08-17, EOL - 1.x.y: released 2020-08-17, EOL
- 2.x.y: released 2021-04-15, EOL - 2.x.y: released 2021-04-15, EOL
- 3.x.y: released 2021-12-01, current
- 4.x.y: To be released
## Tested with ## Tested with
### ansible-core ### ansible-core
- stable-2.16 - 2.12
- stable-2.17 - 2.13
- stable-2.18 - 2.14
- current development version
### Python
- 3.8 (Unit tests only)
- 3.9 (Unit tests only)
- 3.10 (Sanity, Units and integrations tests)
- 3.11 (Unit tests only, collection version >= 3.10.0)
### Databases ### Databases
For MariaDB, only Long Term releases are tested. When multiple LTS are available, we test the oldest and the newest only. Usually breaking changes introduced in the versions in between are also present in the latest version. For MariaDB, only Long Term releases are tested.
- mysql 5.7.40
- mysql 8.0.31
- mariadb:10.3.34 (only collection version <= 3.5.1)
- mariadb:10.4.24 (only collection version >= 3.5.2)
- mariadb:10.5.18 (only collection version >= 3.5.2)
- mariadb:10.6.11 (only collection version >= 3.5.2)
- mariadb:10.11.?? (waiting for release)
- mysql 5.7.40 (collection version < 3.10.0)
- mysql 8.0.31 (collection version < 3.10.0)
- mysql 8.4.1 (collection version >= 3.10.0) !!! FAILING, no support yet !!!
- 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.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 ### Database connectors
- pymysql 0.7.11 (collection version < 3.10 and MySQL 5.7) - pymysql 0.7.11 (Only tested with MySQL 5.7)
- pymysql 0.9.3 - pymysql 0.9.3
- pymysql 0.10.1 (for RHEL8 context) - pymysql 1.0.2 (only collection version >= ???) !!! Unsuported until future release !!!
- pymysql 1.0.2 (collection version >= 3.6.1) - mysqlclient 2.0.1
- pymysql 1.1.1 (collection version >= 3.10.0) - mysqlclient 2.0.3 (only collection version >= 3.5.2)
- mysqlclient 2.1.1 (only collection version >= 3.5.2)
## External requirements ## External requirements
The MySQL modules rely on a [PyMySQL](https://github.com/PyMySQL/PyMySQL) connector. The MySQL modules rely on a MySQL connector. The list of supported drivers is below:
The `mysqlclient` connector support has been [deprecated](https://github.com/ansible-collections/community.mysql/issues/654) - use `PyMySQL` connector instead! We will stop testing against it in collection version 4.0.0 and remove the related code in 5.0.0. - [PyMySQL](https://github.com/PyMySQL/PyMySQL)
- [MySQLdb](https://github.com/PyMySQL/mysqlclient-python)
- Support for other Python MySQL connectors may be added in a future release.
## Using this collection ## Using this collection

View file

@ -19,16 +19,19 @@ For now, the makefile only supports Podman.
### Requirements ### Requirements
- python >= 3.8 - python >= 3.8 and <= 3.10
- make - make
- podman - podman
- Minimum 15GB of free space on the device storing containers images and volumes. You can use this command to check: `podman system info --format='{{.Store.GraphRoot}}'|xargs findmnt --noheadings --nofsroot --output SOURCE --target|xargs df -h --output=size,used,avail,pcent,target` - Minimum 15GB of free space on the device storing containers images and volumes. You can use this command to check: `podman system info --format='{{.Store.GraphRoot}}'|xargs findmnt --noheadings --nofsroot --output SOURCE --target|xargs df -h --output=size,used,avail,pcent,target`
- Minimum 2GB of RAM - Minimum 2GB of RAM
### ansible-test environment ### Custom ansible-test containers
Integration tests use the default container from ansible-test. Then required packages for the tests are installed from the `setup_controller` target located in the `tests/integration/targets` folder. Our integrations tests use custom containers for ansible-test. Those images have their definition file stored in the directory [test-containers](test-containers/). We build and publish the images on ghcr.io under the ansible-collection namespace: E.G.:
`ghcr.io/ansible-collections/community.mysql/test-container-mariadb106-py310-mysqlclient211:latest`.
Availables images are listed [here](https://github.com/orgs/ansible-collections/packages).
### Makefile options ### Makefile options
@ -41,16 +44,14 @@ The Makefile accept the following options
- "3.8" - "3.8"
- "3.9" - "3.9"
- "3.10" - "3.10"
- "3.11" (for stable-2.15+) - Description: If `Python -V` shows an unsupported version, use this option and choose one of the version available on your system. Use `ls /usr/bin/python3*|grep -v config` to list them.
- Description: If `Python -V` shows an unsupported version, use this option to select a compatible Python version available on your system. Use `ls /usr/bin/python3*|grep -v config` to list the available versions (You may have to install one). Unsupported versions are those that are too recent for the Ansible version you are using. In such cases, you will see an error message similar to: 'This version of ansible-test cannot be executed with Python version 3.12.3. Supported Python versions are: 3.9, 3.10, 3.11'.
- `ansible` - `ansible`
- Mandatory: true - Mandatory: true
- Choices: - Choices:
- "stable-2.15" - "stable-2.12"
- "stable-2.16" - "stable-2.13"
- "stable-2.17" - "stable-2.14"
- "devel"
- Description: Version of ansible to install in a venv to run ansible-test - Description: Version of ansible to install in a venv to run ansible-test
- `db_engine_name` - `db_engine_name`
@ -63,28 +64,39 @@ The Makefile accept the following options
- `db_engine_version` - `db_engine_version`
- Mandatory: true - Mandatory: true
- Choices: - Choices:
- "8.0.38" <- mysql - "5.7.40" <- 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) - "8.0.31" <- mysql
- "10.11.8" <- mariadb - "10.4.24" <- mariadb
- "11.4.5" <- mariadb - "10.5.18" <- mariadb
- "10.6.11" <- 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. - 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` - `connector_name`
- Mandatory: true - Mandatory: true
- Choices: - Choices:
- "pymysql" - "pymysql
- "mysqlclient" - "mysqlclient"
- Description: The python package of the connector to use. In addition to selecting the test container, this value is also used for tests filtering: `when: connector_name == 'pymysql'`. - Description: The python package of the connector to use. In addition to selecting the test container, this value is also used for tests filtering: `when: connector_name == 'pymysql'`.
- `connector_version` - `connector_version`
- Mandatory: true - Mandatory: true
- Choices: - Choices:
- "0.7.11" <- pymysql (Only for MySQL 5.7)
- "0.9.3" <- pymysql - "0.9.3" <- pymysql
- "0.10.1" <- pymysql - "1.0.2" <- pymysql (Not working, need fix)
- "1.0.2" <- pymysql - "2.0.1" <- mysqlclient
- "1.1.1" <- pymysql - "2.0.3" <- mysqlclient
- "2.1.1" <- mysqlclient
- Description: The version of the python package of the connector to use. This value is used to filter tests meant for other connectors. - Description: The version of the python package of the connector to use. This value is used to filter tests meant for other connectors.
- `python`
- Mandatory: true
- Choices:
- "3.8"
- "3.9"
- "3.10"
- Description: The python version to use in the controller (ansible-test container).
- `target` - `target`
- Mandatory: false - Mandatory: false
- Choices: - Choices:
@ -104,30 +116,30 @@ tests will overwrite the 3 databases containers so no need to kill them in advan
- `continue_on_errors` - `continue_on_errors`
- Mandatory: false - Mandatory: false
- Description: Tells ansible-test to continue on errors. This is the way the GitHub Action's workflow runs the tests. This can be used to catch all errors in a single run, but you'll need to scroll up to find them. Add any value to activate this option: `continue_on_errors=1` - Description: Tells ansible-test to retry on errors and also continue on errors. This is the way the GitHub Action's workflow runs the tests. This can be used to catch all errors in a single run, but you'll need to scroll up to find them. Add any value to activate this option: `continue_on_errors=1`
#### Makefile usage examples: #### Makefile usage examples:
```sh ```sh
# Run all targets # Run all targets
make ansible="stable-2.16" db_engine_name="mysql" db_engine_version="8.0.31" connector_name="pymysql" connector_version="1.0.2" make ansible="stable-2.12" db_engine_name="mysql" db_engine_version="5.7.40" python="3.8" connector_name="pymysql" connector_version="0.7.11"
# A single target # A single target
make ansible="stable-2.16" db_engine_name="mysql" db_engine_version="8.0.31" connector_name="pymysql" connector_version="1.0.2" target="test_mysql_info" make ansible="stable-2.14" db_engine_name="mysql" db_engine_version="5.7.40" python="3.8" connector_name="pymysql" connector_version="0.7.11" target="test_mysql_info"
# Keep databases and ansible tests containers alives # Keep databases and ansible tests containers alives
# A single target and continue on errors # A single target and continue on errors
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 make ansible="stable-2.14" db_engine_name="mysql" db_engine_version="8.0.31" python="3.9" 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: # 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.8" ansible="stable-2.14" db_engine_name="mariadb" db_engine_version="10.6.11" python="3.9" connector_name="pymysql" connector_version="0.9.3"
``` ```
### Run all tests ### Run all tests
GitHub Action offer a test matrix that run every combination of MySQL, MariaDB and Connector against each other. To reproduce this, this repo provides a script called *run_all_tests.py*. GitHub Action offer a test matrix that run every combination of Python, MySQL, MariaDB and Connector against each other. To reproduce this, this repo provides a script called *run_all_tests.py*.
Examples: Examples:
@ -136,8 +148,18 @@ python run_all_tests.py
``` ```
### Add a new Connector or Database version ### Add a new Python, Connector or Database version
New components version should be added to this file: [.github/workflows/ansible-test-plugins.yml](https://github.com/ansible-collections/community.mysql/tree/main/.github/workflows) You can look into `[.github/workflows/ansible-test-plugins.yml](https://github.com/ansible-collections/community.mysql/tree/main/.github/workflows)` to see how those containers are built using [build-docker-image.yml](https://github.com/ansible-collections/community.mysql/blob/main/.github/workflows/build-docker-image.yml) and all [docker-image-xxx.yml](https://github.com/ansible-collections/community.mysql/blob/main/.github/workflows/docker-image-mariadb103-py38-mysqlclient201.yml) files.
Be careful to not add too much tests. The matrix creates an exponential number of virtual machines! 1. Add a workflow in [.github/workflows/](.github/workflows)
1. Add a new folder in [test-containers](test-containers) containing a new Dockerfile. Your container must contains 3 things:
- Python
- A connector: The python package to connect to the database (pymysql, mysqlclient, ...)
- A mysql client to prepare databases before our tests starts. This client must provide both `mysql` and `mysqldump` commands.
1. Add your version in the matrix of *.github/workflows/ansible-test-plugins.yml*. You can use [run_all_tests.py](run_all_tests.py) to help you see what the matrix will be. Simply comment out the line `os.system(make_cmd)` before runing the script. You can also add `print(len(matrix))` to display how many tests there will be on GitHub Action.
1. Ask the lead maintainer to mark your new image(s) as `public` under [https://github.com/orgs/ansible-collections/packages](https://github.com/orgs/ansible-collections/packages)
After pushing your commit to the remote, the container will be built and published on ghcr.io. Have a look in the "Action" tab to see if it worked. In case of error `failed to copy: io: read/write on closed pipe` re-run the workflow, this append unfortunately a lot.
To see the docker image produced, go to the package page in the ansible-collection namespace [https://github.com/orgs/ansible-collections/packages](https://github.com/orgs/ansible-collections/packages). This page indicate a "Published x days ago" that is updated infrequently. To see the last time the container has been updated you must click on its title and look in the right hands side bellow the title "Last published".

View file

@ -1,278 +1,431 @@
ancestor: 2.0.0 ancestor: null
releases: releases:
3.0.0: 1.0.0:
changes:
breaking_changes:
- mysql_replication - remove ``Is_Slave`` and ``Is_Master`` return values (were
replaced with ``Is_Primary`` and ``Is_Replica`` (https://github.com/ansible-collections/community.mysql/issues/145).
- mysql_replication - remove the mode options values containing ``master``/``slave``
and the master_use_gtid option ``slave_pos`` (were replaced with corresponding
``primary``/``replica`` values) (https://github.com/ansible-collections/community.mysql/issues/145).
- mysql_user - remove support for the `REQUIRESSL` special privilege as it has
ben superseded by the `tls_requires` option (https://github.com/ansible-collections/community.mysql/discussions/121).
- mysql_user - validate privileges using database engine directly (https://github.com/ansible-collections/community.mysql/issues/234
https://github.com/ansible-collections/community.mysql/pull/243). Do not validate
privileges in this module anymore.
release_summary: 'This is the major release of the ``community.mysql`` collection.
This changelog contains all breaking changes to the modules in this collection
that have been added after the release of ``community.mysql`` 2.3.2.'
fragments:
- 243-get-rid-of-privs-comparison.yml
- 244-remove-requiressl-privilege.yaml
- 3.0.0.yml
- 300-mysql_replication_remove_master_slave.yml
release_date: '2021-12-01'
3.1.0:
changes: changes:
bugfixes: bugfixes:
- Collection core functions - use vendored version of ``distutils.version`` - mysql - dont mask ``mysql_connect`` function errors from modules (https://github.com/ansible/ansible/issues/64560).
instead of the deprecated Python standard library ``distutils`` (https://github.com/ansible-collections/community.mysql/pull/269). - mysql_db - fix Broken pipe error appearance when state is import and the target
file is compressed (https://github.com/ansible/ansible/issues/20196).
- mysql_db - fix bug in the ``db_import`` function introduced by https://github.com/ansible/ansible/pull/56721
(https://github.com/ansible/ansible/issues/65351).
- mysql_info - add parameter for __collect to get only what are wanted (https://github.com/ansible-collections/community.general/pull/136).
- mysql_replication - allow to pass empty values to parameters (https://github.com/ansible/ansible/issues/23976).
- mysql_user - Fix idempotence when long grant lists are used (https://github.com/ansible/ansible/issues/68044)
- mysql_user - Remove false positive ``no_log`` warning for ``update_password``
option
- mysql_user - add ``INVOKE LAMBDA`` privilege support (https://github.com/ansible-collections/community.general/issues/283).
- mysql_user - add missed privileges to support (https://github.com/ansible-collections/community.general/issues/617).
- mysql_user - fix ``host_all`` arguments conversion string formatting error
(https://github.com/ansible/ansible/issues/29644).
- mysql_user - fix overriding password to the same (https://github.com/ansible-collections/community.general/issues/543).
- mysql_user - fix support privileges with underscore (https://github.com/ansible/ansible/issues/66974).
- mysql_user - fix the error No database selected (https://github.com/ansible/ansible/issues/68070).
- mysql_user - make sure current_pass_hash is a string before using it in comparison
(https://github.com/ansible/ansible/issues/60567).
- mysql_variable - fix the module doesn't support variables name with dot (https://github.com/ansible/ansible/issues/54239).
minor_changes: minor_changes:
- Added explicit description of the supported versions of databases and connectors. - mysql_db - add ``master_data`` parameter (https://github.com/ansible/ansible/pull/66048).
Changes to the collection are **NOT** tested against database versions older - mysql_db - add ``skip_lock_tables`` option (https://github.com/ansible/ansible/pull/66688).
than `mysql 5.7.31` and `mariadb 10.2.37` or connector versions older than - mysql_db - add the ``check_implicit_admin`` parameter (https://github.com/ansible/ansible/issues/24418).
`pymysql 0.7.10` and `mysqlclient 2.0.1`. (https://github.com/ansible-collections/community.mysql/discussions/141) - mysql_db - add the ``dump_extra_args`` parameter (https://github.com/ansible/ansible/pull/67747).
- mysql_user - added the ``force_context`` boolean option to set the default - mysql_db - add the ``executed_commands`` returned value (https://github.com/ansible/ansible/pull/65498).
database context for the queries to be the ``mysql`` database. This way replication/binlog - mysql_db - add the ``force`` parameter (https://github.com/ansible/ansible/pull/65547).
filters can catch the statements (https://github.com/ansible-collections/community.mysql/issues/265). - mysql_db - add the ``restrict_config_file`` parameter (https://github.com/ansible/ansible/issues/34488).
- mysql_db - add the ``unsafe_login_password`` parameter (https://github.com/ansible/ansible/issues/63955).
- mysql_db - add the ``use_shell`` parameter (https://github.com/ansible/ansible/issues/20196).
- mysql_info - add ``exclude_fields`` parameter (https://github.com/ansible/ansible/issues/63319).
- mysql_info - add ``global_status`` filter parameter option and return (https://github.com/ansible/ansible/pull/63189).
- mysql_info - add ``return_empty_dbs`` parameter to list empty databases (https://github.com/ansible/ansible/issues/65727).
- mysql_replication - add ``channel`` parameter (https://github.com/ansible/ansible/issues/29311).
- mysql_replication - add ``connection_name`` parameter (https://github.com/ansible/ansible/issues/46243).
- mysql_replication - add ``fail_on_error`` parameter (https://github.com/ansible/ansible/pull/66252).
- mysql_replication - add ``master_delay`` parameter (https://github.com/ansible/ansible/issues/51326).
- mysql_replication - add ``master_use_gtid`` parameter (https://github.com/ansible/ansible/pull/62648).
- mysql_replication - add ``queries`` return value (https://github.com/ansible/ansible/pull/63036).
- mysql_replication - add support of ``resetmaster`` choice to ``mode`` parameter
(https://github.com/ansible/ansible/issues/42870).
- mysql_user - ``priv`` parameter can be string or dictionary (https://github.com/ansible/ansible/issues/57533).
- mysql_user - add TLS REQUIRES parameters (https://github.com/ansible-collections/community.mysql/pull/9).
- mysql_user - add ``plugin_auth_string`` parameter (https://github.com/ansible/ansible/pull/44267).
- mysql_user - add ``plugin_hash_string`` parameter (https://github.com/ansible/ansible/pull/44267).
- mysql_user - add ``plugin`` parameter (https://github.com/ansible/ansible/pull/44267).
- mysql_user - add the resource_limits parameter (https://github.com/ansible-collections/community.general/issues/133).
- mysql_variables - add ``mode`` parameter (https://github.com/ansible/ansible/issues/60119).
release_summary: 'This is the first proper release of the ``community.mysql``
collection.
This changelog contains all changes to the modules in this collection that
were added after the release of Ansible 2.9.0.
'
fragments:
- 1.0.0.yml
- 142-mysql_user_add_resource_limit_parameter.yml
- 151-mysql_db_add_use_shell_parameter.yml
- 18-mysql_user-update_password-no_log.yml
- 225-mysql_user_fix_no_database_selected.yml
- 285-mysql_user_invoke_lambda_support.yml
- 369-mysql_user_add_tls_requires.yml
- 428-mysql_db_add_unsafe_login_password_param.yml
- 468-mysql_db_add_restrict_config_file_param.yml
- 486-mysql_db_add_check_implicit_admin_parameter.yml
- 490-mysql_user_fix_cursor_errors.yml
- 609-mysql_user_fix_overriding_password_to_the_same.yml
- 618-mysql_user_add_missed_privileges.yml
- 62648-mysql_replication_add_master_use_gtid_param.yml
- 63036-mysql_replication_add_return_value.yml
- 63130-mysql_replication_add_master_delay_parameter.yml
- 63189-mysql_info-global-status.yml
- 63229-mysql_replication_add_connection_name_parameter.yml
- 63271-mysql_replication_add_channel_parameter.yml
- 63321-mysql_replication_add_resetmaster_to_mode.yml
- 63371-mysql_info_add_exclude_fields_parameter.yml
- 63546-mysql_replication_allow_to_pass_empty_values.yml
- 63547-mysql_variables_add_mode_param.yml
- 64059-mysql_user_fix_password_comparison.yaml
- 64585-mysql_dont_mask_mysql_connect_errors_from_modules.yml
- 65498-mysql_db_add_executed_commands_return_val.yml
- 65547-mysql_db_add_force_param.yml
- 65755-mysql_info_doesnt_list_empty_dbs.yml
- 65789-mysql_user_add_plugin_authentication_parameters.yml
- 66048-mysql_add_master_data_parameter.yml
- 66252-mysql_replication_fail_on_error.yml
- 66688-mysql_db_add_skip_lock_tables_option.yml
- 66801-mysql_user_priv_can_be_dict.yml
- 66806-mysql_variables_not_support_variables_with_dot.yml
- 66974-mysql_user_doesnt_support_privs_with_underscore.yml
- 67337-fix-proxysql-mysql-cursor.yaml
- 67747-mysql_db_add_dump_extra_args_param.yml
- 67767-mysql_db_fix_bug_introduced_by_56721.yml
- mysql_info_add_parameter.yml
- mysql_user_idempotency.yml
release_date: '2020-08-17'
1.0.1:
changes:
bugfixes:
- mysql_db - fix false warning related to ``unsafe_login_password`` option (https://github.com/ansible-collections/community.mysql/issues/33).
- mysql_user - added tests to verify that TLS requirements are removed with
an empty ``tls_requires`` option (https://github.com/ansible-collections/community.mysql/issues/20).
- mysql_user - correct procedure to check existing TLS requirements (https://github.com/ansible-collections/community.mysql/pull/26).
- mysql_user - minor syntax fixes (https://github.com/ansible-collections/community.mysql/pull/26).
release_summary: 'This is the patch release of the ``community.mysql`` collection.
This changelog contains all changes to the modules in this collection that
have been added after the release of ``community.mysql`` 1.0.0.
'
fragments:
- 1.0.1.yml
- 26-remove_tls_requirements.yml
- 34-mysql_db_fix_false_warning.yml
release_date: '2020-09-29'
1.0.2:
changes:
bugfixes:
- mysql_user - fix module's crash when modifying a user with ``host_all`` (https://github.com/ansible-collections/community.mysql/issues/39).
release_summary: 'This is the patch release of the ``community.mysql`` collection.
This changelog contains all changes to the modules in this collection that
have been added after the release of ``community.mysql`` 1.0.1.
'
fragments:
- 1.0.2.yml
- 40-mysql_user_fix_error_when_host_all_used.yml
release_date: '2020-10-01'
1.1.0:
changes:
bugfixes:
- mysql modules - fix crash when ``!includedir`` option is in config file (https://github.com/ansible-collections/community.mysql/issues/46).
minor_changes:
- mysql modules - add the ``check_hostname`` option (https://github.com/ansible-collections/community.mysql/issues/28).
- mysql modules - patch the ``Connection`` class to add a destructor that ensures
connections to the server are explicitly closed (https://github.com/ansible-collections/community.mysql/pull/44).
release_summary: 'This is the minor release of the ``community.mysql`` collection.
This changelog contains all changes to the modules in this collection that
have been added after the release of ``community.mysql`` 1.0.2.
'
fragments:
- 1.1.0.yml
- 35-disable-hostname-check.yml
- 44-close-connection.yml
- 47-mysql_modules_fix_failings_when_include_dir_in_config_file.yml
release_date: '2020-10-13'
1.1.1:
changes:
bugfixes:
- mysql_query - fix failing when single-row query contains commas (https://github.com/ansible-collections/community.mysql/issues/51).
release_summary: 'This is the patch release of the ``community.mysql`` collection.
This changelog contains all changes to the modules in this collection that
have been added after the release of ``community.mysql`` 1.1.0.
'
fragments:
- 1.1.1.yml
- 53-mysql_query_fix_single_query_with_commas.yml
release_date: '2020-11-03'
1.1.2:
changes:
bugfixes:
- mysql_db - fix false warning related to ``unsafe_login_password`` option (https://github.com/ansible-collections/community.mysql/issues/33).
- mysql_replication - fix crashes of mariadb >= 10.5.1 and mysql >= 8.0.22 caused
by using deprecated terminology (https://github.com/ansible-collections/community.mysql/issues/70).
- mysql_user - fixed change detection when using append_privs (https://github.com/ansible-collections/community.mysql/pull/72).
minor_changes:
- mysql_query - simple refactoring of query type check (https://github.com/ansible-collections/community.mysql/pull/58).
- mysql_user - simple refactoring of priv type check (https://github.com/ansible-collections/community.mysql/pull/58).
release_summary: 'This is the patch release of the ``community.mysql`` collection.
This changelog contains all changes to the modules in this collection that
have been added after the release of ``community.mysql`` 1.1.1.'
fragments:
- 1.1.2.yml
- 58-mysql_query_refactoring.yml
- 71-mysql_replication_add_replica_keyword_support.yml
- 72-mysql_db_fix_false_warning.yml
- 72-mysql_user_change_detection.yml
release_date: '2020-12-18'
1.2.0:
changes:
bugfixes:
- mysql_user - add ``SHOW_ROUTINE`` privilege support (https://github.com/ansible-collections/community.mysql/issues/86).
- mysql_user - fixed creating user with encrypted password in MySQL 8.0 (https://github.com/ansible-collections/community.mysql/pull/79).
minor_changes:
- mysql_user - refactor to reduce cursor.execute() calls in preparation for
adding query logging (https://github.com/ansible-collections/community.mysql/pull/76).
release_summary: 'This is the minor release of the ``community.mysql`` collection. release_summary: 'This is the minor release of the ``community.mysql`` collection.
This changelog contains all changes to the modules in this collection This changelog contains all changes to the modules in this collection
that have been added after the release of ``community.mysql`` 3.0.0.' that have been added after the release of ``community.mysql`` 1.1.2.'
fragments: fragments:
- 141-supported-database-and-connector-versions.yaml - 1.2.0.yml
- 266-default-database-for-mysql-user.yml - 76-mysql-user-query-refact.yaml
- 79-mysql-user-tests-and-fixes.yml
- 87-mysql_user_show_routine_support.yml
release_date: '2021-01-18'
1.3.0:
changes:
bugfixes:
- mysql_user - fix handling of INSERT, UPDATE, REFERENCES on columns (https://github.com/ansible-collections/community.mysql/issues/106).
- mysql_user - the module is not idempotent when SELECT on columns granted (https://github.com/ansible-collections/community.mysql/issues/99).
major_changes:
- mysql_replication - the mode options values ``getslave``, ``startslave``,
``stopslave``, ``resetslave``, ``resetslaveall` and the master_use_gtid option
``slave_pos`` are deprecated (see the alternative values) and will be removed
in ``community.mysql`` 3.0.0 (https://github.com/ansible-collections/community.mysql/pull/97).
- mysql_replication - the word ``SLAVE`` in messages returned by the module
will be changed to ``REPLICA`` in ``community.mysql`` 2.0.0 (https://github.com/ansible-collections/community.mysql/issues/98).
minor_changes:
- mysql_replication - deprecate offending terminology, add alternative choices
to the ``mode`` option (https://github.com/ansible-collections/community.mysql/issues/78).
release_summary: 'This is the minor release of the ``community.mysql`` collection.
This changelog contains all changes to the modules in this collection
that have been added after the release of ``community.mysql`` 1.2.0.'
fragments:
- 1.3.0.yml
- 100-mysql_user_not_idemponent_when_select_on_columns_granted.yml
- 107-mysql_user_fix_grant_on_col_handling.yml
- 97-mysql_replication_deprecate_offending_terminology.yml
release_date: '2021-03-08'
2.0.0:
changes:
bugfixes:
- mysql_user - add support for ``REPLICA MONITOR`` privilege (https://github.com/ansible-collections/community.mysql/issues/105).
major_changes:
- mysql_replication - the return value ``Is_Slave`` and ``Is_Master`` will be
replaced with ``Is_Replica`` and ``Is_Primary`` in ``community.mysql`` 3.0.0
(https://github.com/ansible-collections/community.mysql/issues/145).
- mysql_replication - the word ``master`` in messages returned by the module
will be replaced with ``primary`` in ``community.mysql`` 3.0.0 (https://github.com/ansible-collections/community.mysql/issues/145).
- mysql_replication - the word ``slave`` in messages returned by the module
replaced with ``replica`` (https://github.com/ansible-collections/community.mysql/issues/98).
- mysql_user - the ``REQUIRESSL`` is an alias for the ``ssl`` key in the ``tls_requires``
option in ``community.mysql`` 2.0.0 and support will be dropped altogether
in ``community.mysql`` 3.0.0 (https://github.com/ansible-collections/community.mysql/issues/121).
minor_changes:
- mysql module utils - change deprecated connection parameters ``passwd`` and
``db`` to ``password`` and ``database`` (https://github.com/ansible-collections/community.mysql/pull/116).
- mysql_collection - introduce codebabse split to handle divergences between
MySQL and MariaDB (https://github.com/ansible-collections/community.mysql/pull/103).
- mysql_info - add `version.full` and `version.suffix` return values (https://github.com/ansible-collections/community.mysql/issues/114).
- mysql_user - deprecate the ``REQUIRESSL`` privilege (https://github.com/ansible-collections/community.mysql/issues/101).
release_summary: This is release 2.0.0 of the ``community.mysql`` collection,
released on 2021-04-15.
fragments:
- 101-drop-requiressl-support.yml
- 103-mysql_and_mariadb_divergence.yml
- 108-mysql_priv_add_grant.yml
- 115-add_mysql_full_version_suffix_return_var.yml
- 116-change_deprecated_connection_ parameters.yml
- 144-mysql_replication_remove_slave_from_messages.yml
- 2.0.0.yml
release_date: '2021-04-15'
2.1.0:
changes:
bugfixes:
- mysql - revert changes of connector arguments made in pull request 116 causing
the invalid keyword argument error (https://github.com/ansible-collections/community.mysql/pull/116).
major_changes:
- mysql_replication - add deprecation warning that the ``Is_Slave`` and ``Is_Master``
return values will be replaced with ``Is_Primary`` and ``Is_Replica`` in ``community.mysql``
3.0.0 (https://github.com/ansible-collections/community.mysql/pull/147).
- mysql_replication - the choices of the ``state`` option containing ``master``
will be finally replaced with the alternative ``primary`` choices in ``community.mysql``
3.0.0, add deprecation warnings (https://github.com/ansible-collections/community.mysql/pull/150).
minor_changes:
- mysql_replication - add alternative (``primary``) choices to the ``state``
option choices containing ``master`` (https://github.com/ansible-collections/community.mysql/pull/150).
- mysql_replication - add the ``Is_Primary`` and ``Is_Replica`` alternatives
to the ``Is_Slave`` and ``Is_Master`` return values as a preparation for replacement
in ``community.mysql`` 3.0.0 (https://github.com/ansible-collections/community.mysql/pull/147).
- mysql_replication - change ``master_`` options to ``primary_`` options, add
aliases to keep compatibility (https://github.com/ansible-collections/community.mysql/pull/150).
release_summary: 'This is the minor release of the ``community.mysql`` collection.
This changelog contains all changes to the modules in this collection
that have been added after the release of ``community.mysql`` 2.0.0.'
fragments:
- 147-mysql_replication_deprecate_ret_vals.yml
- 150-mysql_replication_master_related.yml
- 153-mysql_revert_connector_changes.yml
release_date: '2021-04-23'
2.1.1:
changes:
minor_changes:
- mysql_query - correctly reflect changed status in replace statements (https://github.com/ansible-collections/community.mysql/pull/193).
release_summary: 'This is the patch release of the ``community.mysql`` collection.
This changelog contains all changes to the modules in this collection that
have been added after the release of ``community.mysql`` 2.1.0.'
fragments:
- 193-reflect_changed_status_in_replace_statements.yml
- 2.1.1.yml
release_date: '2021-08-11'
2.2.0:
changes:
release_summary: 'This is the minor release of the ``community.mysql`` collection.
This changelog contains all changes to the modules in this collection that
have been added after the release of ``community.mysql`` 2.1.1'
fragments:
- 2.2.0.yml
release_date: '2021-09-23'
2.2.0-a1:
changes:
release_summary: 'This is the minor pre-release of the ``community.mysql`` collection.
This changelog contains all changes to the modules in this collection that
have been added after the release of ``community.mysql`` 2.1.1'
fragments:
- 2.2.0-a1.yml
modules:
- description: Adds, removes, or updates a MySQL role
name: mysql_role
namespace: ''
release_date: '2021-08-11'
2.3.0:
changes:
bugfixes:
- mysql_info - fix TypeError failure when there are databases that do not contain
tables (https://github.com/ansible-collections/community.mysql/issues/204).
minor_changes:
- mysql_user - replace VALID_PRIVS constant by get_valid_privs() function (https://github.com/ansible-collections/community.mysql/pull/217).
release_summary: 'This is the minor release of the ``community.mysql`` collection.
This changelog contains all changes to the modules in this collection that
have been added after the release of ``community.mysql`` 2.2.0.'
fragments:
- 2.3.0.yml
- 205-mysql_info_fix_failure_when_no_tables_in_db.yml
- 217-mysql-user-replace-get-valid-privs-from-show-privilegees.yml
release_date: '2021-09-23'
2.3.1:
changes:
bugfixes:
- mysql_user - Fix crash reporting ``Invalid privileges specified`` when passing
privileges that became aliases (https://github.com/ansible-collections/community.mysql/issues/232).
release_summary: 'This is the patch release of the ``community.mysql`` collection.
This changelog contains all changes to the modules in this collection that
have been added after the release of ``community.mysql`` 2.3.0.'
fragments:
- 2.3.1.yml
- 233-mysql_user_return_valid_privs.yml
release_date: '2021-10-19'
2.3.2:
changes:
bugfixes:
- mysql_db - Fix mismatch when database name contains a ``%`` character (https://github.com/ansible-collections/community.mysql/pull/227).
release_summary: 'This is the patch release of the ``community.mysql`` collection.
This changelog contains all changes to the modules in this collection that
have been added after the release of ``community.mysql`` 2.3.1.'
fragments:
- 2.3.2.yml
- 227-db-create-special-name.yaml
release_date: '2021-11-29'
2.3.3:
changes:
bugfixes:
- Collection core functions - use vendored version of ``distutils.version``
instead of the deprecated Python standard library ``distutils`` (https://github.com/ansible-collections/community.mysql/pull/269).
release_summary: This is the patch release of the ``community.mysql`` collection.
This changelog contains all changes to the modules in this collection that
have been added after the release of ``community.mysql`` 2.3.2.
fragments:
- 2.3.3.yml
- 267-prepare_for_distutils_be_removed.yml - 267-prepare_for_distutils_be_removed.yml
- 3.1.0.yml
release_date: '2022-01-18' release_date: '2022-01-18'
3.1.1: 2.3.4:
changes: changes:
bugfixes: bugfixes:
- mysql_role - make the ``set_default_role_all`` parameter actually working - mysql_role - make the ``set_default_role_all`` parameter actually working
(https://github.com/ansible-collections/community.mysql/pull/282). (https://github.com/ansible-collections/community.mysql/pull/282).
release_summary: 'This is the patch release of the ``community.mysql`` collection. release_summary: This is the patch release of the ``community.mysql`` collection.
This changelog contains all changes to the modules in this collection that
This changelog contains all changes to the modules in this collection have been added after the release of ``community.mysql`` 2.3.3.
that have been added after the release of ``community.mysql`` 3.1.0.'
fragments: fragments:
- 2.3.4.yml
- 282-mysql_role_fix_set_default_role_all_argument.yml - 282-mysql_role_fix_set_default_role_all_argument.yml
- 3.1.1.yml
release_date: '2022-02-16' release_date: '2022-02-16'
3.1.2: 2.3.5:
changes: changes:
bugfixes: bugfixes:
- Collection core functions - fixes related to the mysqlclient Python connector - Collection core functions - fixes related to the mysqlclient Python connector
(https://github.com/ansible-collections/community.mysql/issues/292). (https://github.com/ansible-collections/community.mysql/issues/292).
release_summary: 'This is the patch release of the ``community.mysql`` collection. release_summary: This is the patch release of the ``community.mysql`` collection.
This changelog contains all changes to the modules in this collection that
This changelog contains all changes to the modules in this collection have been added after the release of ``community.mysql`` 2.3.4.
that have been added after the release of ``community.mysql`` 3.1.1.'
fragments: fragments:
- 0-mysqlclient.yml - 0-mysqlclient.yml
- 3.1.2.yml - 2.3.5.yml
release_date: '2022-03-14' release_date: '2022-03-14'
3.1.3: 2.3.6:
changes: changes:
bugfixes: bugfixes:
- mysql_replication - fails when using the `primary_use_gtid` option with `slave_pos`
or `replica_pos` (https://github.com/ansible-collections/community.mysql/issues/335).
- mysql_role - remove redundant connection closing (https://github.com/ansible-collections/community.mysql/pull/330). - mysql_role - remove redundant connection closing (https://github.com/ansible-collections/community.mysql/pull/330).
- mysql_user - fix missing dynamic privileges after revoke and grant privileges
to user (https://github.com/ansible-collections/community.mysql/issues/120).
- mysql_user - fix parsing privs when a user has roles assigned (https://github.com/ansible-collections/community.mysql/issues/231).
- 'mysql_user - fix the possibility for a race condition that breaks certain - 'mysql_user - fix the possibility for a race condition that breaks certain
(circular) replication configurations when ``DROP USER`` is executed on multiple (circular) replication configurations when ``DROP USER`` is executed on multiple
nodes in the replica set. Adding ``IF EXISTS`` avoids the need to use ``sql_log_bin: nodes in the replica set. Adding ``IF EXISTS`` avoids the need to use ``sql_log_bin:
no`` making the statement always replication safe (https://github.com/ansible-collections/community.mysql/pull/287).' no`` making the statement always replication safe (https://github.com/ansible-collections/community.mysql/pull/287).'
release_summary: 'This is the patch release of the ``community.mysql`` collection.
This changelog contains all changes to the modules in this collection
that have been added after the release of ``community.mysql`` 3.1.2.'
fragments:
- 0-mysql_replication_replica_pos.yml
- 3.1.3.yml
- 307-mysql_user_add_if_exists_to_drop.yml
- 329-mysql_role-remove-redudant-connection-closing.yml
release_date: '2022-04-26'
3.10.0:
changes:
bugfixes:
- mysql_info - Add ``plugin_hash_string`` to ``users_info`` filter's output.
The existing ``plugin_auth_string`` contained the hashed password and thus
is missleading, it will be removed from community.mysql 4.0.0. (https://github.com/ansible-collections/community.mysql/pull/629).
- mysql_user - Added a warning to update_password's on_new_username option if
multiple accounts with the same username but different passwords exist (https://github.com/ansible-collections/community.mysql/pull/642).
- mysql_user - Fix ``tls_requires`` not removing ``SSL`` and ``X509`` when sets
as empty (https://github.com/ansible-collections/community.mysql/pull/628).
- mysql_user - Fix idempotence when using variables from the ``users_info``
filter of ``mysql_info`` as an input (https://github.com/ansible-collections/community.mysql/pull/628).
- mysql_user - Fixed an IndexError in the update_password functionality introduced
in PR https://github.com/ansible-collections/community.mysql/pull/580 and
released in community.mysql 3.8.0. If you used this functionality, please
avoid versions 3.8.0 to 3.9.0 (https://github.com/ansible-collections/community.mysql/pull/642).
- mysql_user - add correct ``ed25519`` auth plugin handling (https://github.com/ansible-collections/community.mysql/issues/6).
- mysql_variables - fix the module always changes on boolean values (https://github.com/ansible-collections/community.mysql/issues/652).
deprecated_features:
- collection - support of mysqlclient connector is deprecated - use PyMySQL
connector instead! We will stop testing against it in collection version 4.0.0
and remove the related code in 5.0.0 (https://github.com/ansible-collections/community.mysql/issues/654).
- mysql_info - The ``users_info`` filter returned variable ``plugin_auth_string``
contains the hashed password and it's misleading, it will be removed from
community.mysql 4.0.0. Use the `plugin_hash_string` return value instead (https://github.com/ansible-collections/community.mysql/pull/629).
minor_changes:
- mysql_info - Add ``tls_requires`` returned value for the ``users_info`` filter
(https://github.com/ansible-collections/community.mysql/pull/628).
- mysql_info - return a database server engine used (https://github.com/ansible-collections/community.mysql/issues/644).
- mysql_replication - Adds support for `CHANGE REPLICATION SOURCE TO` statement
(https://github.com/ansible-collections/community.mysql/issues/635).
- mysql_replication - Adds support for `SHOW BINARY LOG STATUS` and `SHOW BINLOG
STATUS` on getprimary mode.
- mysql_replication - Improve detection of IsReplica and IsPrimary by inspecting
the dictionary returned from the SQL query instead of relying on variable
types. This ensures compatibility with changes in the connector or the output
of SHOW REPLICA STATUS and SHOW MASTER STATUS, allowing for easier maintenance
if these change in the future.
- mysql_user - Add salt parameter to generate static hash for `caching_sha2_password`
and `sha256_password` plugins.
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_user.yml
- 1-mysql_info.yml
- 2-mysql_variables.yml
- 3-deprecate_mysqlclient.yml
- 3.10.0.yml
- add_salt_param_to_gen_sha256_hash.yml
- get_primary_show_binary_log_status.yml
- improve_get_replica_primary_status.yml
- lie_fix_mysql_user_on_new_username.yml
- lie_fix_plugin_hash_string_return.yml
- mysql_user_tls_requires.yml
- supports_mysql_change_replication_source_to.yml
release_date: '2024-08-22'
3.10.1:
changes:
bugfixes:
- mysql_user - module makes changes when is executed with ``plugin_auth_string``
parameter and check mode.
deprecated_features:
- mysql_user - the ``user`` alias of the ``name`` argument has been deprecated
and will be removed in collection version 5.0.0. Use the ``name`` argument
instead.
release_summary: 'This is a patch release of the ``community.mysql`` collection.
Besides a bugfix, it contains an important upcoming breaking-change information.'
fragments:
- 0-mysql_user.yml
- 3.10.1.yml
- 596-fix-check-changes.yaml
release_date: '2024-09-04'
3.10.2:
changes:
bugfixes:
- mysql_user - add correct ``ed25519`` auth plugin handling when creating a
user (https://github.com/ansible-collections/community.mysql/issues/672).
release_summary: 'This is a bugfix 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_user.yml
- 3.10.2.yml
release_date: '2024-09-06'
3.10.3:
changes:
bugfixes:
- mysql_user - add correct ``ed25519`` auth plugin handling when creating a
user (https://github.com/ansible-collections/community.mysql/pull/676).
release_summary: 'This is a bugfix 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_user.yml
- 3.10.3.yml
release_date: '2024-09-09'
3.11.0:
changes:
bugfixes:
- mysql_user,mysql_role - The sql_mode ANSI_QUOTES affects how the modules mysql_user
and mysql_role compare the existing privileges with the configured privileges,
as well as decide whether double quotes or backticks should be used in the
GRANT statements. Pointing out in issue 671, the modules mysql_user and mysql_role
allow users to enable/disable ANSI_QUOTES in session variable (within a DB
session, the session variable always overwrites the global one). But due to
the issue, the modules do not check for ANSI_MODE in the session variable,
instead, they only check in the GLOBAL one.That behavior is not only limiting
the users' flexibility, but also not allowing users to explicitly disable
ANSI_MODE to work around such bugs like https://bugs.mysql.com/bug.php?id=115953.
(https://github.com/ansible-collections/community.mysql/issues/671)
minor_changes:
- mysql_info - adds the count of tables for each database to the returned values.
It is possible to exclude this new field using the ``db_table_count`` exclusion
filter. (https://github.com/ansible-collections/community.mysql/pull/691)
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.11.0.yml
- 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:
- mysql_user - fix missing dynamic privileges after revoke and grant privileges
to user (https://github.com/ansible-collections/community.mysql/issues/120).
- mysql_user - fix parsing privs when a user has roles assigned (https://github.com/ansible-collections/community.mysql/issues/231).
major_changes: major_changes:
- The community.mysql collection no longer supports ``Ansible 2.9`` and ``ansible-base - The community.mysql collection no longer supports ``Ansible 2.9`` and ``ansible-base
2.10``. While we take no active measures to prevent usage and there are no 2.10``. While we take no active measures to prevent usage and there are no
@ -281,23 +434,20 @@ releases:
End of Life and if you are still using them, you should consider upgrading End of Life and if you are still using them, you should consider upgrading
to the ``latest Ansible / ansible-core 2.11 or later`` as soon as possible to the ``latest Ansible / ansible-core 2.11 or later`` as soon as possible
(https://github.com/ansible-collections/community.mysql/pull/343). (https://github.com/ansible-collections/community.mysql/pull/343).
minor_changes: release_summary: 'This is the patch release of the ``community.mysql`` collection.
- 'mysql_user and mysql_role: Add the argument ``subtract_privs`` (boolean,
default false, mutually exclusive with ``append_privs``). If set, the privileges
given in ``priv`` are revoked and existing privileges are kept (https://github.com/ansible-collections/community.mysql/pull/333).'
release_summary: 'This is the minor release of the ``community.mysql`` collection.
This changelog contains all changes to the modules in this collection This changelog contains all changes to the modules in this collection
that have been added after the release of ``community.mysql`` 3.1.3.' that have been added after the release of ``community.mysql`` 2.3.5.'
fragments: fragments:
- 001-mysql_user_fix_pars_users_with_roles_assigned.yml - 001-mysql_user_fix_pars_users_with_roles_assigned.yml
- 3.2.0.yml - 2.3.6.yml
- 333-mysql_user-mysql_role-add-subtract_privileges-argument.yml - 307-mysql_user_add_if_exists_to_drop.yml
- 329-mysql_role-remove-redudant-connection-closing.yml
- 338-mysql_user_fix_missing_dynamic_privileges.yml - 338-mysql_user_fix_missing_dynamic_privileges.yml
- drop_support_of_2.9-2.10.yml - drop_support_of_2.9-2.10.yml
release_date: '2022-05-13' release_date: '2022-05-13'
3.2.1: 2.3.7:
changes: changes:
bugfixes: bugfixes:
- Include ``PSF-license.txt`` file for ``plugins/module_utils/_version.py``. - Include ``PSF-license.txt`` file for ``plugins/module_utils/_version.py``.
@ -305,12 +455,12 @@ releases:
This changelog contains all changes to the modules in this collection This changelog contains all changes to the modules in this collection
that have been added after the release of ``community.mysql`` 3.2.0.' that have been added after the release of ``community.mysql`` 2.3.6.'
fragments: fragments:
- 3.2.1.yml - 2.3.7.yml
- psf-license.yml - psf-license.yml
release_date: '2022-05-17' release_date: '2022-05-17'
3.3.0: 2.3.8:
changes: changes:
bugfixes: bugfixes:
- mysql_query - fix false change reports when ``IF EXISTS/IF NOT EXISTS`` clause - mysql_query - fix false change reports when ``IF EXISTS/IF NOT EXISTS`` clause
@ -330,249 +480,63 @@ releases:
but not for authentiation methods which uses the ``plugin*`` arguments. This but not for authentiation methods which uses the ``plugin*`` arguments. This
PR changes this so ``on_create`` also exchange ``plugin``, ``plugin_hash_string``, PR changes this so ``on_create`` also exchange ``plugin``, ``plugin_hash_string``,
``plugin_auth_string`` to None in the list of arguments to change ``plugin_auth_string`` to None in the list of arguments to change
minor_changes: release_summary: 'This is the patch release of the ``community.mysql`` collection.
- 'mysql_role - add the argument ``members_must_exist`` (boolean, default true).
The assertion that the users supplied in the ``members`` argument exist is
only executed when the new argument ``members_must_exist`` is ``true``, to
allow opt-out (https://github.com/ansible-collections/community.mysql/pull/369).
'
- 'mysql_user - Add the option ``on_new_username`` to argument ``update_password``
to reuse the password (plugin and authentication_string) when creating a new
user if some user with the same name already exists. If the existing user
with the same name have varying passwords, the password from the arguments
is used like with ``update_password: always`` (https://github.com/ansible-collections/community.mysql/pull/365).
'
- 'mysql_user - Add the result field ``password_changed`` (boolean). It is true,
when the user got a new password. When the user was created with ``update_password:
on_new_username`` and an existing password was reused, ``password_changed``
is false (https://github.com/ansible-collections/community.mysql/pull/365).
'
release_summary: 'This is the minor release of the ``community.mysql`` collection.
This changelog contains all changes to the modules in this collection This changelog contains all changes to the modules in this collection
that have been added after the release of ``community.mysql`` 3.2.1.' that have been added after the release of ``community.mysql`` 2.3.7.'
fragments: fragments:
- 3.3.0.yml - 2.3.8.yml
- 322-mysql_query_fix_false_change_report.yml - 322-mysql_query_fix_false_change_report.yml
- 334-mysql_user_fix_logic_on_oncreate.yml - 334-mysql_user_fix_logic_on_oncreate.yml
- 365-mysql_user-add-on_new_username-and-password_changed.yml
- 367-mysql_role-fix-deatch-members.yml - 367-mysql_role-fix-deatch-members.yml
- 368-mysql_role-fix-member-detection.yml - 368-mysql_role-fix-member-detection.yml
- 369_mysql_role-add-members_must_exist.yml
release_date: '2022-06-02' release_date: '2022-06-02'
3.4.0: 2.3.9:
changes: changes:
bugfixes: bugfixes:
- Include ``simplified_bsd.txt`` license file for various module utils. - Include ``simplified_bsd.txt`` license file for various module utils.
- mysql_db - Using compression masks errors messages from mysql_dump. By default
the fix is inactive to ensure retro-compatibility with system without bash.
To activate the fix, use the module option ``pipefail=true`` (https://github.com/ansible-collections/community.mysql/issues/256).
- mysql_replication - when the ``primary_ssl`` argument is set to ``no``, the
module will turn off SSL (https://github.com/ansible-collections/community.mysql/issues/393).
major_changes:
- mysql_db - the ``pipefail`` argument's default value will be changed to ``true``
in community.mysql 4.0.0. If your target machines do not use ``bash`` as a
default interpreter, set ``pipefail`` to ``false`` explicitly. However, we
strongly recommend setting up ``bash`` as a default and ``pipefail=true``
as it will protect you from getting broken dumps you don't know about (https://github.com/ansible-collections/community.mysql/issues/407).
minor_changes:
- mysql_db - add the ``chdir`` argument to avoid failings when a dump file contains
relative paths (https://github.com/ansible-collections/community.mysql/issues/395).
- mysql_db - add the ``pipefail`` argument to avoid broken dumps when ``state``
is ``dump`` and compression is used (https://github.com/ansible-collections/community.mysql/issues/256).
release_summary: 'This is the minor release of the ``community.mysql`` collection.
This changelog contains all changes to the modules in this collection
that have been added after the release of ``community.mysql`` 3.3.0.'
fragments:
- 0-mysql_db_add_chdir_argument.yml
- 1-mysql_replication_can_disable_master_ssl.yml
- 2-mysql_db_announce.yml
- 3.4.0.yml
- fix-256-mysql_dump-errors.yml
- simplified-bsd-license.yml
release_date: '2022-08-02'
3.5.0:
changes:
bugfixes:
- mysql_user - grant option was revoked accidentally when modifying users. This
fix revokes grant option only when privs are setup to do that (https://github.com/ansible-collections/community.mysql/issues/77#issuecomment-1209693807).
minor_changes:
- 'mysql_replication - add a new option: ``primary_ssl_verify_server_cert``
(https://github.com//pull/435).'
release_summary: 'This is the minor release of the ``community.mysql`` collection.
This changelog contains all changes to the modules in this collection
that have been added after the release of ``community.mysql`` 3.4.0.'
fragments:
- 3.5.0.yml
- 434-do-not-revoke-grant-option-always.yaml
- 435-mysql_replication_verify_server_cert.yml
release_date: '2022-09-05'
3.5.1:
changes:
bugfixes:
- mysql_user, mysql_role - mysql/mariadb recent versions translate 'ALL PRIVILEGES'
to a list of specific privileges. That caused a change every time we modified
user privileges. This fix compares privs before and after user modification
to avoid this infinite change (https://github.com/ansible-collections/community.mysql/issues/77).
release_summary: 'This is the patch release of the ``community.mysql`` collection. release_summary: 'This is the patch release of the ``community.mysql`` collection.
This changelog contains all changes to the modules and plugins in this collection This changelog contains all changes to the modules in this collection
that have been made after the previous release.' that have been added after the release of ``community.mysql`` 2.3.8.'
fragments: fragments:
- 3.5.1.yml - 2.3.9.yml
- 438-fix-privilege-changing-everytime.yml - simplified-bsd-license.yml
release_date: '2022-09-09' release_date: '2022-08-02'
3.6.0: 2.4.0:
changes: changes:
bugfixes:
- mysql_user - when revoke privs consists only of ``GRANT``, a 2nd revoke query
is executed with empty privs to revoke that ended in an SQL exception (https://github.com/ansible-collections/community.mysql/pull/503).
- mysql_variables - add uppercase character pattern to regex to allow GLOBAL
variables containing uppercase characters. This recognizes variable names
used in Galera, for example, ``wsrep_OSU_method``, which breaks the normal
pattern of all lowercase characters (https://github.com/ansible-collections/community.mysql/pull/501).
minor_changes: minor_changes:
- mysql_info - add ``connector_name`` and ``connector_version`` to returned - mysql_info - add ``connector_name`` and ``connector_version`` to returned
values (https://github.com/ansible-collections/community.mysql/pull/497). values (https://github.com/ansible-collections/community.mysql/pull/497).
- mysql_role - enable auto_commit to avoid MySQL metadata table lock (https://github.com/ansible-collections/community.mysql/issues/479). release_summary: 'This is a bugfix release of the ``community.mysql`` collection.
- mysql_user - add plugin_auth_string as optional parameter to use a specific
pam service if pam/auth_pam plugin is used (https://github.com/ansible-collections/community.mysql/pull/445).
- mysql_user - add the ``session_vars`` argument to set session variables at
the beginning of module execution (https://github.com/ansible-collections/community.mysql/issues/478).
- mysql_user - display a more informative invalid privilege exception. Changes
the exception handling of the granting permission logic to show the query
executed , params and the exception message granting privileges fails` (https://github.com/ansible-collections/community.mysql/issues/465).
- mysql_user - enable auto_commit to avoid MySQL metadata table lock (https://github.com/ansible-collections/community.mysql/issues/479).
- setup_mysql - update MySQL tarball URL (https://github.com/ansible-collections/community.mysql/pull/491).
release_summary: 'This is the minor release of the ``community.mysql`` collection.
This changelog contains all changes to the modules and plugins in this collection This changelog contains all changes to the modules and plugins in this collection
that have been made after the previous release.' that have been made after the previous release.'
fragments: fragments:
- 0_mysql_user_session_vars.yml - 2.3.11.yml
- 3.6.0.yml
- 445_add_service_name_to_plugin_pam_auth_pam_usage.yml
- 465-display_more_informative_invalid_priv_exceptiion.yml
- 479_enable_auto_commit.yml
- 479_enable_auto_commit_part2.yml
- 491_fix_download_url.yaml
- 497_mysql_info_returns_connector_name_and_version.yml - 497_mysql_info_returns_connector_name_and_version.yml
- 503-fix-revoke-grant-only.yml
- mysql_variables_allow_uppercase_identifiers.yml
release_date: '2023-02-08' release_date: '2023-02-08'
3.7.0: 2.4.1:
changes: changes:
minor_changes: release_summary: Maintenance release of ``community.mysql`` major version 2.
- mysql module utils - change deprecated connection parameters ``passwd`` and Only contains changes to the CI. There is no functional difference between
``db`` to ``password`` and ``database`` (https://github.com/ansible-collections/community.mysql/pull/177). 2.4.0 and 2.4.1.
- mysql_user - add ``MAX_STATEMENT_TIME`` support for mariadb to the ``resource_limits``
argument (https://github.com/ansible-collections/community.mysql/issues/211).
release_summary: 'This is the 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: fragments:
- 177-change_deprecated_connection_parameters.yml - 2.4.1.yml
- 3.7.0.yml - 490_refactor_integration_tests.yml
- 523-add-max_statement_time_resource-limit.yml release_date: '2023-05-04'
release_date: '2023-05-05' 2.4.2:
3.7.1:
changes: changes:
bugfixes:
- mysql module utils - use the connection arguments ``db`` instead of ``database``
and ``passwd`` instead of ``password`` when running with older mysql drivers
(MySQLdb < 2.1.0 or PyMySQL < 1.0.0) (https://github.com/ansible-collections/community.mysql/pull/551).
release_summary: 'This is a patch 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.7.1.yml
- 551-fix_connection_arguments_driver_compatability.yaml
release_date: '2023-05-22'
3.7.2:
changes:
bugfixes:
- mysql module utils - use the connection arguments ``db`` instead of ``database``
and ``passwd`` instead of ``password`` when running with MySQLdb < 2.0.0 (https://github.com/ansible-collections/community.mysql/pull/553).
release_summary: 'This is a patch 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.7.2.yml
- 553_fix_connection_arguemnts_for_old_mysqldb_driver.yaml
release_date: '2023-05-25'
3.8.0:
changes:
major_changes:
- The community.mysql collection no longer supports ``ansible-core 2.12`` and
``ansible-core 2.13``. While we take no active measures to prevent usage and
there are no plans to introduce incompatible code to the modules, we will
stop testing those versions. Both are or will soon be End of Life and if you
are still using them, you should consider upgrading to the ``latest Ansible
/ ansible-core 2.15 or later`` as soon as possible (https://github.com/ansible-collections/community.mysql/pull/574).
- mysql_role - the ``column_case_sensitive`` argument's default value will be
changed to ``true`` in community.mysql 4.0.0. If your playbook expected the
column to be automatically uppercased for your roles privileges, you should
set this to false explicitly (https://github.com/ansible-collections/community.mysql/issues/578).
- mysql_user - the ``column_case_sensitive`` argument's default value will be
changed to ``true`` in community.mysql 4.0.0. If your playbook expected the
column to be automatically uppercased for your users privileges, you should
set this to false explicitly (https://github.com/ansible-collections/community.mysql/issues/577).
minor_changes:
- mysql_info - add filter ``users_info`` (https://github.com/ansible-collections/community.mysql/pull/580).
- mysql_role - add ``column_case_sensitive`` option to prevent field names from
being uppercased (https://github.com/ansible-collections/community.mysql/pull/569).
- mysql_user - add ``column_case_sensitive`` option to prevent field names from
being uppercased (https://github.com/ansible-collections/community.mysql/pull/569).
release_summary: 'This is the 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.8.0.yml
- 569_fix_column_uppercasing.yml
- drop_ansible_core_2_12_and_2_13.yml
- lie_mysql_info_users_info.yml
release_date: '2023-10-25'
3.9.0:
changes:
bugfixes:
- mysql_info - the ``slave_status`` filter was returning an empty list on MariaDB
with multiple replication channels. It now returns all channels by running
``SHOW ALL SLAVES STATUS`` for MariaDB servers (https://github.com/ansible-collections/community.mysql/issues/603).
major_changes: major_changes:
- Collection version 2.*.* is EOL, no more bugfixes will be backported. Please - Collection version 2.*.* is EOL, no more bugfixes will be backported. Please
consider upgrading to the latest version. consider upgrading to the latest version.
minor_changes: release_summary: Maintenance release of ``community.mysql`` to inform users
- mysql_user - add the ``password_expire`` and ``password_expire_interval`` that the collection version 2 is EOL. There were no functional changes made
arguments to implement the password expiration management for mysql user (https://github.com/ansible-collections/community.mysql/pull/598). in this release.
- mysql_user - add user attribute support via the ``attributes`` parameter and
return value (https://github.com/ansible-collections/community.mysql/pull/604).
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: fragments:
- 0-stable-2-eol.yml - 0-stable-2-eol.yml
- 3.9.0.yml - 2.4.2.yml
- 598-password_expire-support-for-mysql_user.yml release_date: '2024-01-22'
- 602-show-all-slaves-status.yaml
- 604-user-attributes.yaml
release_date: '2024-02-22'

View file

@ -25,5 +25,5 @@ sections:
- Bugfixes - Bugfixes
- - known_issues - - known_issues
- Known Issues - Known Issues
title: Community MySQL and MariaDB Collection title: Community MySQL Collection
trivial_section_name: trivial trivial_section_name: trivial

View file

@ -1,11 +1,10 @@
---
namespace: community namespace: community
name: mysql name: mysql
version: 3.13.0 version: 2.4.2
readme: README.md readme: README.md
authors: authors:
- Ansible community - Ansible community
description: MySQL and MariaDB collection for Ansible description: MySQL collection for Ansible
license_file: COPYING license_file: COPYING
tags: tags:
- database - database

View file

@ -71,21 +71,24 @@ options:
- Whether to validate the server host name when an SSL connection is required. Corresponds to MySQL CLIs C(--ssl) switch. - Whether to validate the server host name when an SSL connection is required. Corresponds to MySQL CLIs C(--ssl) switch.
- Setting this to C(false) disables hostname verification. Use with caution. - Setting this to C(false) disables hostname verification. Use with caution.
- Requires pymysql >= 0.7.11. - Requires pymysql >= 0.7.11.
- This option has no effect on MySQLdb.
type: bool type: bool
version_added: '1.1.0' version_added: '1.1.0'
requirements: requirements:
- PyMySQL (Python 2.7 and Python 3.x) - mysqlclient (Python 3.5+) or
- PyMySQL (Python 2.7 and Python 3.x) or
- MySQLdb (Python 2.x)
notes: notes:
- Requires the PyMySQL (Python 2.7 and Python 3.X) package installed on the remote host. - Requires the PyMySQL (Python 2.7 and Python 3.X) or MySQL-python (Python 2.X) package installed on the remote host.
The Python package may be installed with apt-get install python-pymysql (Ubuntu; see M(ansible.builtin.apt)) or The Python package may be installed with apt-get install python-pymysql (Ubuntu; see M(ansible.builtin.apt)) or
yum install python2-PyMySQL (RHEL/CentOS/Fedora; see M(ansible.builtin.yum)). You can also use dnf install python2-PyMySQL yum install python2-PyMySQL (RHEL/CentOS/Fedora; see M(ansible.builtin.yum)). You can also use dnf install python2-PyMySQL
for newer versions of Fedora; see M(ansible.builtin.dnf). for newer versions of Fedora; see M(ansible.builtin.dnf).
- Be sure you have PyMySQL library installed on the target machine - Be sure you have mysqlclient, PyMySQL, or MySQLdb library installed on the target machine
for the Python interpreter Ansible discovers. For example if ansible discovers and uses Python 3, you need to install for the Python interpreter Ansible discovers. For example if ansible discovers and uses Python 3, you need to install
the Python 3 version of PyMySQL. If ansible discovers and uses Python 2, you need to install the Python 2 the Python 3 version of PyMySQL or mysqlclient. If ansible discovers and uses Python 2, you need to install the Python 2
version of PyMySQL. version of either PyMySQL or MySQL-python.
- If you have trouble, it may help to force Ansible to use the Python interpreter you need by specifying - If you have trouble, it may help to force Ansible to use the Python interpreter you need by specifying
C(ansible_python_interpreter). For more information, see C(ansible_python_interpreter). For more information, see
U(https://docs.ansible.com/ansible/latest/reference_appendices/interpreter_discovery.html). U(https://docs.ansible.com/ansible/latest/reference_appendices/interpreter_discovery.html).
- Both C(login_password) and C(login_user) are required when you are - Both C(login_password) and C(login_user) are required when you are
passing credentials. If none are present, the module will attempt to read passing credentials. If none are present, the module will attempt to read
@ -96,6 +99,9 @@ notes:
and later uses the unix_socket authentication plugin by default that and later uses the unix_socket authentication plugin by default that
without using I(login_unix_socket=/var/run/mysqld/mysqld.sock) (the default path) without using I(login_unix_socket=/var/run/mysqld/mysqld.sock) (the default path)
causes the error ``Host '127.0.0.1' is not allowed to connect to this MariaDB server``. causes the error ``Host '127.0.0.1' is not allowed to connect to this MariaDB server``.
- Alternatively, you can use the mysqlclient library instead of MySQL-python (MySQLdb)
which supports both Python 2.X and Python >=3.5.
See U(https://pypi.org/project/mysqlclient/) how to install it.
- "If credentials from the config file (for example, C(/root/.my.cnf)) are not needed to connect to a database server, but - "If credentials from the config file (for example, C(/root/.my.cnf)) are not needed to connect to a database server, but
the file exists and does not contain a C([client]) section, before any other valid directives, it will be read and this the file exists and does not contain a C([client]) section, before any other valid directives, it will be read and this
will cause the connection to fail, to prevent this set it to an empty string, (for example C(config_file: ''))." will cause the connection to fail, to prevent this set it to an empty string, (for example C(config_file: ''))."

View file

@ -1,180 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import (absolute_import, division, print_function)
from ._version import LooseVersion
__metaclass__ = type
class CommandResolver():
def __init__(self, server_implementation, server_version):
self.server_implementation = server_implementation
self.server_version = LooseVersion(server_version)
def resolve_command(self, command):
"""
Resolves the appropriate SQL command based on the server implementation and version.
Parameters:
command (str): The base SQL command to be resolved (e.g., "SHOW SLAVE HOSTS").
Returns:
str: The resolved SQL command suitable for the given server implementation and version.
Raises:
ValueError: If the command is not supported or recognized.
Example:
Given a server implementation `mysql` and server version `8.0.23`, and a command `SHOW SLAVE HOSTS`,
the method will resolve the command based on the following table of versions:
Table:
[
("mysql", "default", "SHOW SLAVES HOSTS default"),
("mysql", "5.7.0", "SHOW SLAVES HOSTS"),
("mysql", "8.0.22", "SHOW REPLICAS"),
("mysql", "8.4.0", "SHOW REPLICAS 8.4"),
("mariadb", "10.5.1", "SHOW REPLICAS HOSTS"),
]
Example usage:
>>> resolver = CommandResolver("mysql", "8.0.23")
>>> resolver.resolve_command("SHOW SLAVE HOSTS")
'SHOW REPLICAS'
In this example, the resolver will:
- Filter and sort applicable versions: [
("8.4.0", "SHOW REPLICAS 8.4"),
("8.0.22", "HOW REPLICAS"),
("5.7.0", "SHOW SLAVES HOSTS")
]
- Iterate through the sorted list and find the first version less than or equal to 8.0.23,
which is 8.0.22, and return the corresponding command.
"""
# Convert the command to uppercase to ensure case-insensitive lookup
command = command.upper()
commands = {
"SHOW MASTER STATUS": {
("mysql", "default"): "SHOW MASTER STATUS",
("mariadb", "default"): "SHOW MASTER STATUS",
("mysql", "8.2.0"): "SHOW BINARY LOG STATUS",
("mariadb", "10.5.2"): "SHOW BINLOG STATUS",
},
"SHOW SLAVE STATUS": {
("mysql", "default"): "SHOW SLAVE STATUS",
("mariadb", "default"): "SHOW SLAVE STATUS",
("mysql", "8.0.22"): "SHOW REPLICA STATUS",
("mariadb", "10.5.1"): "SHOW REPLICA STATUS",
},
"SHOW SLAVE HOSTS": {
("mysql", "default"): "SHOW SLAVE HOSTS",
("mariadb", "default"): "SHOW SLAVE HOSTS",
("mysql", "8.0.22"): "SHOW REPLICAS",
("mariadb", "10.5.1"): "SHOW REPLICA HOSTS",
},
"CHANGE MASTER": {
("mysql", "default"): "CHANGE MASTER",
("mariadb", "default"): "CHANGE MASTER",
("mysql", "8.0.23"): "CHANGE REPLICATION SOURCE",
},
"MASTER_HOST": {
("mysql", "default"): "MASTER_HOST",
("mariadb", "default"): "MASTER_HOST",
("mysql", "8.0.23"): "SOURCE_HOST",
},
"MASTER_USER": {
("mysql", "default"): "MASTER_USER",
("mariadb", "default"): "MASTER_USER",
("mysql", "8.0.23"): "SOURCE_USER",
},
"MASTER_PASSWORD": {
("mysql", "default"): "MASTER_PASSWORD",
("mariadb", "default"): "MASTER_PASSWORD",
("mysql", "8.0.23"): "SOURCE_PASSWORD",
},
"MASTER_PORT": {
("mysql", "default"): "MASTER_PORT",
("mariadb", "default"): "MASTER_PORT",
("mysql", "8.0.23"): "SOURCE_PORT",
},
"MASTER_CONNECT_RETRY": {
("mysql", "default"): "MASTER_CONNECT_RETRY",
("mariadb", "default"): "MASTER_CONNECT_RETRY",
("mysql", "8.0.23"): "SOURCE_CONNECT_RETRY",
},
"MASTER_LOG_FILE": {
("mysql", "default"): "MASTER_LOG_FILE",
("mariadb", "default"): "MASTER_LOG_FILE",
("mysql", "8.0.23"): "SOURCE_LOG_FILE",
},
"MASTER_LOG_POS": {
("mysql", "default"): "MASTER_LOG_POS",
("mariadb", "default"): "MASTER_LOG_POS",
("mysql", "8.0.23"): "SOURCE_LOG_POS",
},
"MASTER_DELAY": {
("mysql", "default"): "MASTER_DELAY",
("mariadb", "default"): "MASTER_DELAY",
("mysql", "8.0.23"): "SOURCE_DELAY",
},
"MASTER_SSL": {
("mysql", "default"): "MASTER_SSL",
("mariadb", "default"): "MASTER_SSL",
("mysql", "8.0.23"): "SOURCE_SSL",
},
"MASTER_SSL_CA": {
("mysql", "default"): "MASTER_SSL_CA",
("mariadb", "default"): "MASTER_SSL_CA",
("mysql", "8.0.23"): "SOURCE_SSL_CA",
},
"MASTER_SSL_CAPATH": {
("mysql", "default"): "MASTER_SSL_CAPATH",
("mariadb", "default"): "MASTER_SSL_CAPATH",
("mysql", "8.0.23"): "SOURCE_SSL_CAPATH",
},
"MASTER_SSL_CERT": {
("mysql", "default"): "MASTER_SSL_CERT",
("mariadb", "default"): "MASTER_SSL_CERT",
("mysql", "8.0.23"): "SOURCE_SSL_CERT",
},
"MASTER_SSL_KEY": {
("mysql", "default"): "MASTER_SSL_KEY",
("mariadb", "default"): "MASTER_SSL_KEY",
("mysql", "8.0.23"): "SOURCE_SSL_KEY",
},
"MASTER_SSL_CIPHER": {
("mysql", "default"): "MASTER_SSL_CIPHER",
("mariadb", "default"): "MASTER_SSL_CIPHER",
("mysql", "8.0.23"): "SOURCE_SSL_CIPHER",
},
"MASTER_SSL_VERIFY_SERVER_CERT": {
("mysql", "default"): "MASTER_SSL_VERIFY_SERVER_CERT",
("mariadb", "default"): "MASTER_SSL_VERIFY_SERVER_CERT",
("mysql", "8.0.23"): "SOURCE_SSL_VERIFY_SERVER_CERT",
},
"MASTER_AUTO_POSITION": {
("mysql", "default"): "MASTER_AUTO_POSITION",
("mariadb", "default"): "MASTER_AUTO_POSITION",
("mysql", "8.0.23"): "SOURCE_AUTO_POSITION",
},
"RESET MASTER": {
("mysql", "default"): "RESET MASTER",
("mariadb", "default"): "RESET MASTER",
("mysql", "8.4.0"): "RESET BINARY LOGS AND GTIDS",
},
# Add more command mappings here
}
if command in commands:
cmd_syntaxes = commands[command]
applicable_versions = [(v, cmd) for (impl, v), cmd in cmd_syntaxes.items() if impl == self.server_implementation and v != 'default']
applicable_versions.sort(reverse=True, key=lambda x: LooseVersion(x[0]))
for version, cmd in applicable_versions:
if self.server_version >= LooseVersion(version):
return cmd
return cmd_syntaxes[(self.server_implementation, "default")]
raise ValueError("Unsupported command: %s" % command)

View file

@ -17,60 +17,3 @@ def use_old_user_mgmt(cursor):
def supports_identified_by_password(cursor): def supports_identified_by_password(cursor):
return True return True
def server_supports_alter_user(cursor):
version = get_server_version(cursor)
return LooseVersion(version) >= LooseVersion("10.2")
def server_supports_password_expire(cursor):
version = get_server_version(cursor)
return LooseVersion(version) >= LooseVersion("10.4.3")
def get_tls_requires(cursor, user, host):
"""Get user TLS requirements.
Reads directly from mysql.user table allowing for a more
readable code.
Args:
cursor (cursor): DB driver cursor object.
user (str): User name.
host (str): User host name.
Returns: Dictionary containing current TLS required
"""
tls_requires = dict()
query = ('SELECT ssl_type, ssl_cipher, x509_issuer, x509_subject '
'FROM mysql.user WHERE User = %s AND Host = %s')
cursor.execute(query, (user, host))
res = cursor.fetchone()
# Mysql_info use a DictCursor so we must convert back to a list
# otherwise we get KeyError 0
if isinstance(res, dict):
res = list(res.values())
# When user don't require SSL, res value is: ('', '', '', '')
if not any(res):
return None
if res[0] == 'ANY':
tls_requires['SSL'] = None
if res[0] == 'X509':
tls_requires['X509'] = None
if res[1]:
tls_requires['CIPHER'] = res[1]
if res[2]:
tls_requires['ISSUER'] = res[2]
if res[3]:
tls_requires['SUBJECT'] = res[3]
return tls_requires

View file

@ -1,125 +0,0 @@
"""
Generate MySQL sha256 compatible plugins hash for a given password and salt
based on
* https://www.akkadia.org/drepper/SHA-crypt.txt
* https://crypto.stackexchange.com/questions/77427/whats-the-algorithm-behind-mysqls-sha256-password-hashing-scheme/111174#111174
* https://github.com/hashcat/hashcat/blob/master/tools/test_modules/m07400.pm
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
import hashlib
def _to64(v, n):
"""Convert a 32-bit integer to a base-64 string"""
i64 = (
[".", "/"]
+ [chr(x) for x in range(48, 58)]
+ [chr(x) for x in range(65, 91)]
+ [chr(x) for x in range(97, 123)]
)
result = ""
while n > 0:
n -= 1
result += i64[v & 0x3F]
v >>= 6
return result
def _hashlib_sha256(data):
"""Return SHA-256 digest from hashlib ."""
return hashlib.sha256(data).digest()
def _sha256_digest(key, salt, loops):
"""Return a SHA-256 digest of the concatenation of the key, the salt, and the key, repeated as necessary."""
# https://www.akkadia.org/drepper/SHA-crypt.txt
num_bytes = 32
bytes_key = key.encode()
bytes_salt = salt.encode()
digest_b = _hashlib_sha256(bytes_key + bytes_salt + bytes_key)
tmp = bytes_key + bytes_salt
for i in range(len(bytes_key), 0, -num_bytes):
tmp += digest_b if i > num_bytes else digest_b[:i]
i = len(bytes_key)
while i > 0:
tmp += digest_b if (i & 1) != 0 else bytes_key
i >>= 1
digest_a = _hashlib_sha256(tmp)
tmp = b""
for i in range(len(bytes_key)):
tmp += bytes_key
digest_dp = _hashlib_sha256(tmp)
byte_sequence_p = b""
for i in range(len(bytes_key), 0, -num_bytes):
byte_sequence_p += digest_dp if i > num_bytes else digest_dp[:i]
tmp = b""
til = 16 + digest_a[0]
for i in range(til):
tmp += bytes_salt
digest_ds = _hashlib_sha256(tmp)
byte_sequence_s = b""
for i in range(len(bytes_salt), 0, -num_bytes):
byte_sequence_s += digest_ds if i > num_bytes else digest_ds[:i]
digest_c = digest_a
for i in range(loops):
tmp = byte_sequence_p if (i & 1) else digest_c
if i % 3:
tmp += byte_sequence_s
if i % 7:
tmp += byte_sequence_p
tmp += digest_c if (i & 1) else byte_sequence_p
digest_c = _hashlib_sha256(tmp)
inc1, inc2, mod, end = (10, 21, 30, 0)
i = 0
tmp = ""
while True:
tmp += _to64(
(digest_c[i] << 16)
| (digest_c[(i + inc1) % mod] << 8)
| digest_c[(i + inc1 * 2) % mod],
4,
)
i = (i + inc2) % mod
if i == end:
break
tmp += _to64((digest_c[31] << 8) | digest_c[30], 3)
return tmp
def mysql_sha256_password_hash(password, salt):
"""Return a MySQL compatible caching_sha2_password hash in raw format."""
if len(salt) != 20:
raise ValueError("Salt must be 20 characters long.")
count = 5
iteration = 1000 * count
digest = _sha256_digest(password, salt, iteration)
return "$A${0:>03}${1}{2}".format(count, salt, digest)
def mysql_sha256_password_hash_hex(password, salt):
"""Return a MySQL compatible caching_sha2_password hash in hex format."""
return mysql_sha256_password_hash(password, salt).encode().hex().upper()

View file

@ -8,9 +8,6 @@ __metaclass__ = type
from ansible_collections.community.mysql.plugins.module_utils.version import LooseVersion from ansible_collections.community.mysql.plugins.module_utils.version import LooseVersion
from ansible_collections.community.mysql.plugins.module_utils.mysql import get_server_version from ansible_collections.community.mysql.plugins.module_utils.mysql import get_server_version
import re
import shlex
def use_old_user_mgmt(cursor): def use_old_user_mgmt(cursor):
version = get_server_version(cursor) version = get_server_version(cursor)
@ -21,58 +18,3 @@ def use_old_user_mgmt(cursor):
def supports_identified_by_password(cursor): def supports_identified_by_password(cursor):
version = get_server_version(cursor) version = get_server_version(cursor)
return LooseVersion(version) < LooseVersion("8") return LooseVersion(version) < LooseVersion("8")
def server_supports_alter_user(cursor):
version = get_server_version(cursor)
return LooseVersion(version) >= LooseVersion("5.6")
def server_supports_password_expire(cursor):
version = get_server_version(cursor)
return LooseVersion(version) >= LooseVersion("5.7")
def get_tls_requires(cursor, user, host):
"""Get user TLS requirements.
We must use SHOW GRANTS because some tls fileds are encoded.
Args:
cursor (cursor): DB driver cursor object.
user (str): User name.
host (str): User host name.
Returns: Dictionary containing current TLS required
"""
if not use_old_user_mgmt(cursor):
query = "SHOW CREATE USER '%s'@'%s'" % (user, host)
else:
query = "SHOW GRANTS for '%s'@'%s'" % (user, host)
cursor.execute(query)
grants = cursor.fetchone()
# Mysql_info use a DictCursor so we must convert back to a list
# otherwise we get KeyError 0
if isinstance(grants, dict):
grants = list(grants.values())
grants_str = ''.join(grants)
pattern = r"(?<=\bREQUIRE\b)(.*?)(?=(?:\bPASSWORD\b|$))"
requires_match = re.search(pattern, grants_str)
requires = requires_match.group().strip() if requires_match else ""
if requires.startswith('NONE'):
return None
if requires.startswith('SSL'):
return {'SSL': None}
if requires.startswith('X509'):
return {'X509': None}
items = iter(shlex.split(requires))
requires = dict(zip(items, items))
return requires or None

View file

@ -35,8 +35,6 @@ mysql_driver_fail_msg = ('A MySQL module is required: for Python 2.7 either PyMy
'Consider setting ansible_python_interpreter to use ' 'Consider setting ansible_python_interpreter to use '
'the intended Python version.') 'the intended Python version.')
from ansible_collections.community.mysql.plugins.module_utils.database import mysql_quote_identifier
def get_connector_name(connector): def get_connector_name(connector):
""" (class) -> str """ (class) -> str
@ -122,7 +120,7 @@ def mysql_connect(module, login_user=None, login_password=None, config_file='',
if login_user is not None: if login_user is not None:
config['user'] = login_user config['user'] = login_user
if login_password is not None: if login_password is not None:
config['password'] = login_password config['passwd'] = login_password
if ssl_cert is not None: if ssl_cert is not None:
config['ssl']['cert'] = ssl_cert config['ssl']['cert'] = ssl_cert
if ssl_key is not None: if ssl_key is not None:
@ -130,45 +128,22 @@ def mysql_connect(module, login_user=None, login_password=None, config_file='',
if ssl_ca is not None: if ssl_ca is not None:
config['ssl']['ca'] = ssl_ca config['ssl']['ca'] = ssl_ca
if db is not None: if db is not None:
config['database'] = 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 check_hostname is not None:
if get_connector_name(mysql_driver) == 'pymysql': if mysql_driver.__name__ == "pymysql":
version_tuple = (n for n in mysql_driver.__version__.split('.') if n != 'None') 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: if reduce(lambda x, y: int(x) * 100 + int(y), version_tuple) >= 711:
config['ssl']['check_hostname'] = check_hostname config['ssl']['check_hostname'] = check_hostname
else: else:
module.fail_json(msg='To use check_hostname, pymysql >= 0.7.11 is required on the target host') 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': if _mysql_cursor_param == 'cursor':
# In case of PyMySQL driver: # 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) db_connection = mysql_driver.connect(autocommit=autocommit, **config)
else: else:
# In case of MySQLdb driver # In case of MySQLdb driver
# Will be deprecated and dropped
# https://github.com/ansible-collections/community.mysql/issues/654
module.warn('Support of mysqlcline/MySQLdb connector is deprecated. '
'We\'ll stop testing against it in collection version 4.0.0 '
'and remove the related code in 5.0.0. Use PyMySQL connector instead.')
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) db_connection = mysql_driver.connect(**config)
if autocommit: if autocommit:
db_connection.autocommit(True) db_connection.autocommit(True)
@ -212,20 +187,3 @@ def get_server_version(cursor):
version_str = result[0] version_str = result[0]
return version_str return version_str
def get_server_implementation(cursor):
if 'mariadb' in get_server_version(cursor).lower():
return "mariadb"
else:
return "mysql"
def set_session_vars(module, cursor, session_vars):
"""Set session vars."""
for var, value in session_vars.items():
query = "SET SESSION %s = " % mysql_quote_identifier(var, 'vars')
try:
cursor.execute(query + "%s", (value,))
except Exception as e:
module.fail_json(msg='Failed to execute %s%s: %s' % (query, value, e))

View file

@ -1,6 +1,4 @@
from __future__ import (absolute_import, division, print_function) from __future__ import (absolute_import, division, print_function)
__metaclass__ = type __metaclass__ = type
# This code is part of Ansible, but is an independent component. # This code is part of Ansible, but is an independent component.
@ -12,27 +10,64 @@ __metaclass__ = type
# Simplified BSD License (see simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause) # Simplified BSD License (see simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
import string import string
import json
import re import re
from ansible.module_utils.six import iteritems from ansible.module_utils.six import iteritems
from ansible_collections.community.mysql.plugins.module_utils.mysql import ( from ansible_collections.community.mysql.plugins.module_utils.mysql import (
mysql_driver, mysql_driver,
get_server_implementation,
)
from ansible_collections.community.mysql.plugins.module_utils.implementations.mysql.hash import (
mysql_sha256_password_hash,
mysql_sha256_password_hash_hex,
) )
EXTRA_PRIVS = ['ALL', 'ALL PRIVILEGES', 'GRANT', 'REQUIRESSL']
# This list is kept for backwards compatibility after release 2.3.0,
# see https://github.com/ansible-collections/community.mysql/issues/232 for details
VALID_PRIVS = [
'CREATE', 'DROP', 'GRANT', 'GRANT OPTION',
'LOCK TABLES', 'REFERENCES', 'EVENT', 'ALTER',
'DELETE', 'INDEX', 'INSERT', 'SELECT', 'UPDATE',
'CREATE TEMPORARY TABLES', 'TRIGGER', 'CREATE VIEW',
'SHOW VIEW', 'ALTER ROUTINE', 'CREATE ROUTINE',
'EXECUTE', 'FILE', 'CREATE TABLESPACE', 'CREATE USER',
'PROCESS', 'PROXY', 'RELOAD', 'REPLICATION CLIENT',
'REPLICATION SLAVE', 'SHOW DATABASES', 'SHUTDOWN',
'SUPER', 'ALL', 'ALL PRIVILEGES', 'USAGE',
'REQUIRESSL', # Deprecated, to be removed in version 3.0.0
'CREATE ROLE', 'DROP ROLE', 'APPLICATION_PASSWORD_ADMIN',
'AUDIT_ADMIN', 'BACKUP_ADMIN', 'BINLOG_ADMIN',
'BINLOG_ENCRYPTION_ADMIN', 'CLONE_ADMIN', 'CONNECTION_ADMIN',
'ENCRYPTION_KEY_ADMIN', 'FIREWALL_ADMIN', 'FIREWALL_USER',
'GROUP_REPLICATION_ADMIN', 'INNODB_REDO_LOG_ARCHIVE',
'NDB_STORED_USER', 'PERSIST_RO_VARIABLES_ADMIN',
'REPLICATION_APPLIER', 'REPLICATION_SLAVE_ADMIN',
'RESOURCE_GROUP_ADMIN', 'RESOURCE_GROUP_USER',
'ROLE_ADMIN', 'SESSION_VARIABLES_ADMIN', 'SET_USER_ID',
'SYSTEM_USER', 'SYSTEM_VARIABLES_ADMIN', 'SYSTEM_USER',
'TABLE_ENCRYPTION_ADMIN', 'VERSION_TOKEN_ADMIN',
'XA_RECOVER_ADMIN', 'LOAD FROM S3', 'SELECT INTO S3',
'INVOKE LAMBDA',
'ALTER ROUTINE',
'BINLOG ADMIN',
'BINLOG MONITOR',
'BINLOG REPLAY',
'CONNECTION ADMIN',
'READ_ONLY ADMIN',
'REPLICATION MASTER ADMIN',
'REPLICATION SLAVE ADMIN',
'SET USER',
'SHOW_ROUTINE',
'SLAVE MONITOR',
'REPLICA MONITOR',
]
class InvalidPrivsError(Exception): class InvalidPrivsError(Exception):
pass pass
def get_mode(cursor): def get_mode(cursor):
cursor.execute('SELECT @@sql_mode') cursor.execute('SELECT @@GLOBAL.sql_mode')
result = cursor.fetchone() result = cursor.fetchone()
mode_str = result[0] mode_str = result[0]
if 'ANSI' in mode_str: if 'ANSI' in mode_str:
@ -52,25 +87,6 @@ def user_exists(cursor, user, host, host_all):
return count[0] > 0 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): def sanitize_requires(tls_requires):
sanitized_requires = {} sanitized_requires = {}
if tls_requires: if tls_requires:
@ -106,6 +122,39 @@ def do_not_mogrify_requires(query, params, tls_requires):
return query, params return query, params
def get_tls_requires(cursor, user, host):
if user:
if not impl.use_old_user_mgmt(cursor):
query = "SHOW CREATE USER '%s'@'%s'" % (user, host)
else:
query = "SHOW GRANTS for '%s'@'%s'" % (user, host)
cursor.execute(query)
require_list = [tuple[0] for tuple in filter(lambda x: "REQUIRE" in x[0], cursor.fetchall())]
require_line = require_list[0] if require_list else ""
pattern = r"(?<=\bREQUIRE\b)(.*?)(?=(?:\bPASSWORD\b|$))"
requires_match = re.search(pattern, require_line)
requires = requires_match.group().strip() if requires_match else ""
if any((requires.startswith(req) for req in ('SSL', 'X509', 'NONE'))):
requires = requires.split()[0]
if requires == 'NONE':
requires = None
else:
import shlex
items = iter(shlex.split(requires))
requires = dict(zip(items, items))
return requires or None
def get_valid_privs(cursor):
cursor.execute("SHOW PRIVILEGES")
show_privs = [priv[0].upper() for priv in cursor.fetchall()]
# See the comment above VALID_PRIVS declaration
all_privs = show_privs + EXTRA_PRIVS + VALID_PRIVS
return frozenset(all_privs)
def get_grants(cursor, user, host): def get_grants(cursor, user, host):
cursor.execute("SHOW GRANTS FOR %s@%s", (user, host)) cursor.execute("SHOW GRANTS FOR %s@%s", (user, host))
grants_line = list(filter(lambda x: "ON *.*" in x[0], cursor.fetchall()))[0] grants_line = list(filter(lambda x: "ON *.*" in x[0], cursor.fetchall()))[0]
@ -114,107 +163,21 @@ def get_grants(cursor, user, host):
return grants.split(", ") return grants.split(", ")
def get_existing_authentication(cursor, user, host=None):
""" Return a list of dict containing the plugin and auth_string for the
specified username.
If hostname is provided, return only the information about this particular
account.
"""
cursor.execute("SELECT VERSION()")
srv_type = cursor.fetchone()
# Mysql_info use a DictCursor so we must convert back to a list
# otherwise we get KeyError 0
if isinstance(srv_type, dict):
srv_type = list(srv_type.values())
if 'mariadb' in srv_type[0].lower():
# before MariaDB 10.2.19 and 10.3.11, "password" and "authentication_string" can differ
# when using mysql_native_password
if host:
cursor.execute("""select plugin, auth from (
select plugin, password as auth from mysql.user where user=%(user)s
and host=%(host)s
union select plugin, authentication_string as auth from mysql.user where user=%(user)s
and host=%(host)s) x group by plugin, auth
""", {'user': user, 'host': host})
else:
cursor.execute("""select plugin, auth from (
select plugin, password as auth from mysql.user where user=%(user)s
union select plugin, authentication_string as auth from mysql.user where user=%(user)s
) x group by plugin, auth
""", {'user': user})
else:
if host:
cursor.execute("""select plugin, authentication_string as auth
from mysql.user where user=%(user)s and host=%(host)s
group by plugin, authentication_string""", {'user': user, 'host': host})
else:
cursor.execute("""select plugin, authentication_string as auth
from mysql.user where user=%(user)s
group by plugin, authentication_string""", {'user': user})
rows = cursor.fetchall()
if len(rows) == 0:
return []
# Mysql_info use a DictCursor so we must convert list(dict)
# to list(tuple) otherwise we get KeyError 0
if isinstance(rows[0], dict):
rows = [tuple(row.values()) for row in rows]
existing_auth_list = []
# 'plugin_auth_string' contains the hash string. Must be removed in c.mysql 4.0
# See https://github.com/ansible-collections/community.mysql/pull/629
for r in rows:
existing_auth_list.append({
'plugin': r[0],
'plugin_auth_string': r[1],
'plugin_hash_string': r[1]})
return existing_auth_list
def user_add(cursor, user, host, host_all, password, encrypted, def user_add(cursor, user, host, host_all, password, encrypted,
plugin, plugin_hash_string, plugin_auth_string, salt, new_priv, plugin, plugin_hash_string, plugin_auth_string, new_priv,
attributes, tls_requires, reuse_existing_password, module, tls_requires, check_mode):
password_expire, password_expire_interval, locked=False):
# 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")
# we cannot create users without a proper hostname # we cannot create users without a proper hostname
if host_all: if host_all:
return {'changed': False, 'password_changed': False, 'attributes': attributes} return False
if module.check_mode: if check_mode:
return {'changed': True, 'password_changed': None, 'attributes': attributes} return True
# Determine what user management method server uses # Determine what user management method server uses
impl = get_user_implementation(cursor)
old_user_mgmt = impl.use_old_user_mgmt(cursor) old_user_mgmt = impl.use_old_user_mgmt(cursor)
mogrify = do_not_mogrify_requires if old_user_mgmt else mogrify_requires mogrify = do_not_mogrify_requires if old_user_mgmt else mogrify_requires
# This is for update_password: on_new_username
used_existing_password = False
if reuse_existing_password:
existing_auth = get_existing_authentication(cursor, user)
if existing_auth:
if len(existing_auth) != 1:
module.warn("An account with the username %s has a different "
"password than the others existing accounts. Thus "
"on_new_username can't decide which password to "
"reuse so it will use your provided password "
"instead. If no password is provided, the account "
"will have an empty password!" % user)
used_existing_password = False
else:
plugin_hash_string = existing_auth[0]['plugin_hash_string']
password = None
used_existing_password = True
plugin = existing_auth[0]['plugin'] # What if plugin differ?
if password and encrypted: if password and encrypted:
if impl.supports_identified_by_password(cursor): if impl.supports_identified_by_password(cursor):
query_with_args = "CREATE USER %s@%s IDENTIFIED BY PASSWORD %s", (user, host, password) query_with_args = "CREATE USER %s@%s IDENTIFIED BY PASSWORD %s", (user, host, password)
@ -230,19 +193,7 @@ def user_add(cursor, user, host, host_all, password, encrypted,
elif plugin and plugin_hash_string: elif plugin and plugin_hash_string:
query_with_args = "CREATE USER %s@%s IDENTIFIED WITH %s AS %s", (user, host, plugin, plugin_hash_string) query_with_args = "CREATE USER %s@%s IDENTIFIED WITH %s AS %s", (user, host, plugin, plugin_hash_string)
elif plugin and plugin_auth_string: elif plugin and plugin_auth_string:
# Mysql and MariaDB differ in naming pam plugin and Syntax to set it query_with_args = "CREATE USER %s@%s IDENTIFIED WITH %s BY %s", (user, host, plugin, plugin_auth_string)
if plugin == 'pam': # Used by MariaDB which requires the USING keyword, not BY
query_with_args = "CREATE USER %s@%s IDENTIFIED WITH %s USING %s", (user, host, plugin, plugin_auth_string)
elif plugin == 'ed25519': # Used by MariaDB which requires the USING keyword, not BY
query_with_args = "CREATE USER %s@%s IDENTIFIED WITH %s USING PASSWORD(%s)", (user, host, plugin, plugin_auth_string)
elif salt:
if plugin in ['caching_sha2_password', 'sha256_password']:
generated_hash_string = mysql_sha256_password_hash_hex(password=plugin_auth_string, salt=salt)
else:
module.fail_json(msg="salt not handled for %s authentication plugin" % plugin)
query_with_args = ("CREATE USER %s@%s IDENTIFIED WITH %s AS 0x" + generated_hash_string), (user, host, plugin)
else:
query_with_args = "CREATE USER %s@%s IDENTIFIED WITH %s BY %s", (user, host, plugin, plugin_auth_string)
elif plugin: elif plugin:
query_with_args = "CREATE USER %s@%s IDENTIFIED WITH %s", (user, host, plugin) query_with_args = "CREATE USER %s@%s IDENTIFIED WITH %s", (user, host, plugin)
else: else:
@ -251,28 +202,12 @@ def user_add(cursor, user, host, host_all, password, encrypted,
query_with_args_and_tls_requires = query_with_args + (tls_requires,) query_with_args_and_tls_requires = query_with_args + (tls_requires,)
cursor.execute(*mogrify(*query_with_args_and_tls_requires)) cursor.execute(*mogrify(*query_with_args_and_tls_requires))
if password_expire:
if not impl.server_supports_password_expire(cursor):
module.fail_json(msg="The server version does not match the requirements "
"for password_expire parameter. See module's documentation.")
set_password_expire(cursor, user, host, password_expire, password_expire_interval)
if new_priv is not None: if new_priv is not None:
for db_table, priv in iteritems(new_priv): for db_table, priv in iteritems(new_priv):
privileges_grant(cursor, user, host, db_table, priv, tls_requires) privileges_grant(cursor, user, host, db_table, priv, tls_requires)
if tls_requires is not None: if tls_requires is not None:
privileges_grant(cursor, user, host, "*.*", get_grants(cursor, user, host), tls_requires) privileges_grant(cursor, user, host, "*.*", get_grants(cursor, user, host), tls_requires)
return True
final_attributes = None
if attributes:
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}
def is_hash(password): def is_hash(password):
@ -284,15 +219,13 @@ def is_hash(password):
def user_mod(cursor, user, host, host_all, password, encrypted, def user_mod(cursor, user, host, host_all, password, encrypted,
plugin, plugin_hash_string, plugin_auth_string, salt, new_priv, plugin, plugin_hash_string, plugin_auth_string, new_priv,
append_privs, subtract_privs, attributes, tls_requires, module, append_privs, tls_requires, module, role=False, maria_role=False):
password_expire, password_expire_interval, locked=None, role=False, maria_role=False):
changed = False changed = False
msg = "User unchanged" msg = "User unchanged"
grant_option = False grant_option = False
# Determine what user management method server uses # Determine what user management method server uses
impl = get_user_implementation(cursor)
old_user_mgmt = impl.use_old_user_mgmt(cursor) old_user_mgmt = impl.use_old_user_mgmt(cursor)
if host_all and not role: if host_all and not role:
@ -300,7 +233,6 @@ def user_mod(cursor, user, host, host_all, password, encrypted,
else: else:
hostnames = [host] hostnames = [host]
password_changed = False
for host in hostnames: for host in hostnames:
# Handle clear text and hashed passwords. # Handle clear text and hashed passwords.
if not role: if not role:
@ -345,50 +277,28 @@ def user_mod(cursor, user, host, host_all, password, encrypted,
encrypted_password = cursor.fetchone()[0] encrypted_password = cursor.fetchone()[0]
if current_pass_hash != encrypted_password: if current_pass_hash != encrypted_password:
password_changed = True
msg = "Password updated" msg = "Password updated"
if not module.check_mode: if module.check_mode:
if old_user_mgmt: return (True, msg)
cursor.execute("SET PASSWORD FOR %s@%s = %s", (user, host, encrypted_password)) if old_user_mgmt:
msg = "Password updated (old style)" cursor.execute("SET PASSWORD FOR %s@%s = %s", (user, host, encrypted_password))
else: msg = "Password updated (old style)"
try: else:
cursor.execute("ALTER USER %s@%s IDENTIFIED WITH mysql_native_password AS %s", (user, host, encrypted_password)) try:
msg = "Password updated (new style)" cursor.execute("ALTER USER %s@%s IDENTIFIED WITH mysql_native_password AS %s", (user, host, encrypted_password))
except (mysql_driver.Error) as e: msg = "Password updated (new style)"
# https://stackoverflow.com/questions/51600000/authentication-string-of-root-user-on-mysql except (mysql_driver.Error) as e:
# Replacing empty root password with new authentication mechanisms fails with error 1396 # https://stackoverflow.com/questions/51600000/authentication-string-of-root-user-on-mysql
if e.args[0] == 1396: # Replacing empty root password with new authentication mechanisms fails with error 1396
cursor.execute( if e.args[0] == 1396:
"UPDATE mysql.user SET plugin = %s, authentication_string = %s, Password = '' WHERE User = %s AND Host = %s", cursor.execute(
('mysql_native_password', encrypted_password, user, host) "UPDATE mysql.user SET plugin = %s, authentication_string = %s, Password = '' WHERE User = %s AND Host = %s",
) ('mysql_native_password', encrypted_password, user, host)
cursor.execute("FLUSH PRIVILEGES") )
msg = "Password forced update" cursor.execute("FLUSH PRIVILEGES")
else: msg = "Password forced update"
raise e else:
changed = True raise e
# Handle password expiration
if bool(password_expire):
if not impl.server_supports_password_expire(cursor):
module.fail_json(msg="The server version does not match the requirements "
"for password_expire parameter. See module's documentation.")
update = False
mariadb_role = True if "mariadb" in str(impl.__name__) else False
current_password_policy = get_password_expiration_policy(cursor, user, host, maria_role=mariadb_role)
password_expired = is_password_expired(cursor, user, host)
# Check if changes needed to be applied.
if not ((current_password_policy == -1 and password_expire == "default") or
(current_password_policy == 0 and password_expire == "never") or
(current_password_policy == password_expire_interval and password_expire == "interval") or
(password_expire == 'now' and password_expired)):
update = True
if not module.check_mode:
set_password_expire(cursor, user, host, password_expire, password_expire_interval)
password_changed = True
changed = True changed = True
# Handle plugin authentication # Handle plugin authentication
@ -405,11 +315,7 @@ def user_mod(cursor, user, host, host_all, password, encrypted,
if plugin_hash_string and current_plugin[1] != plugin_hash_string: if plugin_hash_string and current_plugin[1] != plugin_hash_string:
update = True update = True
if salt: if plugin_auth_string and current_plugin[1] != plugin_auth_string:
if plugin in ['caching_sha2_password', 'sha256_password']:
if current_plugin[1] != mysql_sha256_password_hash(password=plugin_auth_string, salt=salt):
update = True
elif plugin_auth_string and current_plugin[1] != plugin_auth_string:
# this case can cause more updates than expected, # this case can cause more updates than expected,
# as plugin can hash auth_string in any way it wants # as plugin can hash auth_string in any way it wants
# and there's no way to figure it out for # and there's no way to figure it out for
@ -417,29 +323,14 @@ def user_mod(cursor, user, host, host_all, password, encrypted,
update = True update = True
if update: if update:
query_with_args = None
if plugin_hash_string: if plugin_hash_string:
query_with_args = "ALTER USER %s@%s IDENTIFIED WITH %s AS %s", (user, host, plugin, plugin_hash_string) query_with_args = "ALTER USER %s@%s IDENTIFIED WITH %s AS %s", (user, host, plugin, plugin_hash_string)
elif plugin_auth_string: elif plugin_auth_string:
# Mysql and MariaDB differ in naming pam plugin and syntax to set it query_with_args = "ALTER USER %s@%s IDENTIFIED WITH %s BY %s", (user, host, plugin, plugin_auth_string)
if plugin == 'pam':
query_with_args = "ALTER USER %s@%s IDENTIFIED WITH %s USING %s", (user, host, plugin, plugin_auth_string)
elif plugin == 'ed25519':
query_with_args = "ALTER USER %s@%s IDENTIFIED WITH %s USING PASSWORD(%s)", (user, host, plugin, plugin_auth_string)
elif salt:
if plugin in ['caching_sha2_password', 'sha256_password']:
generated_hash_string = mysql_sha256_password_hash_hex(password=plugin_auth_string, salt=salt)
else:
module.fail_json(msg="salt not handled for %s authentication plugin" % plugin)
query_with_args = ("ALTER USER %s@%s IDENTIFIED WITH %s AS 0x" + generated_hash_string), (user, host, plugin)
else:
query_with_args = "ALTER USER %s@%s IDENTIFIED WITH %s BY %s", (user, host, plugin, plugin_auth_string)
else: else:
query_with_args = "ALTER USER %s@%s IDENTIFIED WITH %s", (user, host, plugin) query_with_args = "ALTER USER %s@%s IDENTIFIED WITH %s", (user, host, plugin)
if not module.check_mode: cursor.execute(*query_with_args)
cursor.execute(*query_with_args)
password_changed = True
changed = True changed = True
# Handle privileges # Handle privileges
@ -448,156 +339,74 @@ def user_mod(cursor, user, host, host_all, password, encrypted,
# If the user has privileges on a db.table that doesn't appear at all in # If the user has privileges on a db.table that doesn't appear at all in
# the new specification, then revoke all privileges on it. # the new specification, then revoke all privileges on it.
if not append_privs and not subtract_privs: for db_table, priv in iteritems(curr_priv):
for db_table, priv in iteritems(curr_priv): # If the user has the GRANT OPTION on a db.table, revoke it first.
# If the user has the GRANT OPTION on a db.table, revoke it first. if "GRANT" in priv:
if "GRANT" in priv: grant_option = True
grant_option = True if db_table not in new_priv:
if db_table not in new_priv: if user != "root" and "PROXY" not in priv and not append_privs:
if user != "root" and "PROXY" not in priv: msg = "Privileges updated"
msg = "Privileges updated" if module.check_mode:
if not module.check_mode: return (True, msg)
privileges_revoke(cursor, user, host, db_table, priv, grant_option, maria_role) privileges_revoke(cursor, user, host, db_table, priv, grant_option, maria_role)
changed = True changed = True
# If the user doesn't currently have any privileges on a db.table, then # If the user doesn't currently have any privileges on a db.table, then
# we can perform a straight grant operation. # we can perform a straight grant operation.
if not subtract_privs: for db_table, priv in iteritems(new_priv):
for db_table, priv in iteritems(new_priv): if db_table not in curr_priv:
if db_table not in curr_priv: msg = "New privileges granted"
msg = "New privileges granted" if module.check_mode:
if not module.check_mode: return (True, msg)
privileges_grant(cursor, user, host, db_table, priv, tls_requires, maria_role) privileges_grant(cursor, user, host, db_table, priv, tls_requires, maria_role)
changed = True changed = True
# If the db.table specification exists in both the user's current privileges # If the db.table specification exists in both the user's current privileges
# and in the new privileges, then we need to see if there's a difference. # and in the new privileges, then we need to see if there's a difference.
db_table_intersect = set(new_priv.keys()) & set(curr_priv.keys()) db_table_intersect = set(new_priv.keys()) & set(curr_priv.keys())
for db_table in db_table_intersect: for db_table in db_table_intersect:
grant_privs = [] # If appending privileges, only the set difference between new privileges and current privileges matter.
revoke_privs = [] # The symmetric difference isn't relevant for append because existing privileges will not be revoked.
if append_privs: if append_privs:
# When appending privileges, only missing privileges need to be granted. Nothing is revoked. priv_diff = set(new_priv[db_table]) - set(curr_priv[db_table])
grant_privs = list(set(new_priv[db_table]) - set(curr_priv[db_table]))
elif subtract_privs:
# When subtracting privileges, revoke only the intersection of requested and current privileges.
# No privileges are granted.
revoke_privs = list(set(new_priv[db_table]) & set(curr_priv[db_table]))
else: else:
# When replacing (neither append_privs nor subtract_privs), grant all missing privileges priv_diff = set(new_priv[db_table]) ^ set(curr_priv[db_table])
# and revoke existing privileges that were not requested...
grant_privs = list(set(new_priv[db_table]) - set(curr_priv[db_table]))
revoke_privs = list(set(curr_priv[db_table]) - set(new_priv[db_table]))
# ... avoiding pointless revocations when ALL are granted if len(priv_diff) > 0:
if 'ALL' in grant_privs or 'ALL PRIVILEGES' in grant_privs: msg = "Privileges updated"
revoke_privs = list(set(['GRANT', 'PROXY']).intersection(set(revoke_privs))) if module.check_mode:
return (True, msg)
# Only revoke grant option if it exists and absence is requested if not append_privs:
# privileges_revoke(cursor, user, host, db_table, curr_priv[db_table], grant_option, maria_role)
# For more details privileges_grant(cursor, user, host, db_table, new_priv[db_table], tls_requires, maria_role)
# https://github.com/ansible-collections/community.mysql/issues/77#issuecomment-1209693807
grant_option = 'GRANT' in revoke_privs and 'GRANT' not in grant_privs
if grant_privs == ['GRANT']:
# USAGE grants no privileges, it is only needed because 'WITH GRANT OPTION' cannot stand alone
grant_privs.append('USAGE')
if len(grant_privs) + len(revoke_privs) > 0:
msg = "Privileges updated: granted %s, revoked %s" % (grant_privs, revoke_privs)
if not module.check_mode:
if len(revoke_privs) > 0:
privileges_revoke(cursor, user, host, db_table, revoke_privs, grant_option, maria_role)
if len(grant_privs) > 0:
privileges_grant(cursor, user, host, db_table, grant_privs, tls_requires, maria_role)
else:
changed = True
# after privilege manipulation, compare privileges from before and now
after_priv = privileges_get(cursor, user, host, maria_role)
changed = changed or (curr_priv != after_priv)
# Handle attributes
attribute_support = get_attribute_support(cursor)
final_attributes = {}
if attributes:
if not attribute_support:
module.fail_json(msg="user attributes were specified but the server does not support user attributes")
else:
current_attributes = attributes_get(cursor, user, host)
if current_attributes is None:
current_attributes = {}
attributes_to_change = {}
for key, value in attributes.items():
if key not in current_attributes or current_attributes[key] != value:
attributes_to_change[key] = value
if attributes_to_change:
msg = "Attributes updated: %s" % (", ".join(["%s: %s" % (key, value) for key, value in attributes_to_change.items()]))
# Calculate final attributes by re-running attributes_get when not in check mode, and merge dictionaries when in check mode
if not module.check_mode:
cursor.execute("ALTER USER %s@%s ATTRIBUTE %s", (user, host, json.dumps(attributes_to_change)))
final_attributes = attributes_get(cursor, user, host)
else:
# Final if statements excludes items whose values are None in attributes_to_change, i.e. attributes that will be deleted
final_attributes = {k: v for d in (current_attributes, attributes_to_change) for k, v in d.items() if k not in attributes_to_change or
attributes_to_change[k] is not None}
# Convert empty dict to None per return value requirements
final_attributes = final_attributes if final_attributes else None
changed = True changed = True
else:
final_attributes = current_attributes
else:
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: if role:
continue continue
# Handle TLS requirements # Handle TLS requirements
current_requires = sanitize_requires(impl.get_tls_requires(cursor, user, host)) current_requires = get_tls_requires(cursor, user, host)
if current_requires != tls_requires: if current_requires != tls_requires:
msg = "TLS requires updated" msg = "TLS requires updated"
if not module.check_mode: if module.check_mode:
if not old_user_mgmt: return (True, msg)
pre_query = "ALTER USER" if not old_user_mgmt:
else: pre_query = "ALTER USER"
pre_query = "GRANT %s ON *.* TO" % ",".join(get_grants(cursor, user, host)) else:
pre_query = "GRANT %s ON *.* TO" % ",".join(get_grants(cursor, user, host))
if tls_requires is not None: if tls_requires is not None:
query = " ".join((pre_query, "%s@%s")) query = " ".join((pre_query, "%s@%s"))
query_with_args = mogrify_requires(query, (user, host), tls_requires) query_with_args = mogrify_requires(query, (user, host), tls_requires)
else: else:
query = " ".join((pre_query, "%s@%s REQUIRE NONE")) query = " ".join((pre_query, "%s@%s REQUIRE NONE"))
query_with_args = query, (user, host) query_with_args = query, (user, host)
cursor.execute(*query_with_args) cursor.execute(*query_with_args)
changed = True changed = True
return {'changed': changed, 'msg': msg, 'password_changed': password_changed, 'attributes': final_attributes} return (changed, msg)
def user_delete(cursor, user, host, host_all, check_mode): def user_delete(cursor, user, host, host_all, check_mode):
@ -653,12 +462,6 @@ def privileges_get(cursor, user, host, maria_role=False):
return x return x
for grant in grants: for grant in grants:
# Mysql_info use a DictCursor so we must convert back to a list
# otherwise we get KeyError 0
if isinstance(grant, dict):
grant = list(grant.values())
if not maria_role: if not maria_role:
res = re.match("""GRANT (.+) ON (.+) TO (['`"]).*\\3@(['`"]).*\\4( IDENTIFIED BY PASSWORD (['`"]).+\\6)? ?(.*)""", grant[0]) res = re.match("""GRANT (.+) ON (.+) TO (['`"]).*\\3@(['`"]).*\\4( IDENTIFIED BY PASSWORD (['`"]).+\\6)? ?(.*)""", grant[0])
else: else:
@ -808,7 +611,7 @@ def sort_column_order(statement):
return '%s(%s)' % (priv_name, ', '.join(columns)) return '%s(%s)' % (priv_name, ', '.join(columns))
def privileges_unpack(priv, mode, column_case_sensitive, ensure_usage=True): def privileges_unpack(priv, mode, valid_privs):
""" Take a privileges string, typically passed as a parameter, and unserialize """ Take a privileges string, typically passed as a parameter, and unserialize
it into a dictionary, the same format as privileges_get() above. We have this it into a dictionary, the same format as privileges_get() above. We have this
custom format to avoid using YAML/JSON strings inside YAML playbooks. Example custom format to avoid using YAML/JSON strings inside YAML playbooks. Example
@ -844,14 +647,9 @@ def privileges_unpack(priv, mode, column_case_sensitive, ensure_usage=True):
pieces[0] = object_type + '.'.join(dbpriv) pieces[0] = object_type + '.'.join(dbpriv)
if '(' in pieces[1]: if '(' in pieces[1]:
if column_case_sensitive is True: output[pieces[0]] = re.split(r',\s*(?=[^)]*(?:\(|$))', pieces[1].upper())
output[pieces[0]] = re.split(r',\s*(?=[^)]*(?:\(|$))', pieces[1]) for i in output[pieces[0]]:
for i in output[pieces[0]]: privs.append(re.sub(r'\s*\(.*\)', '', i))
privs.append(re.sub(r'\s*\(.*\)', '', i))
else:
output[pieces[0]] = re.split(r',\s*(?=[^)]*(?:\(|$))', pieces[1].upper())
for i in output[pieces[0]]:
privs.append(re.sub(r'\s*\(.*\)', '', i))
else: else:
output[pieces[0]] = pieces[1].upper().split(',') output[pieces[0]] = pieces[1].upper().split(',')
privs = output[pieces[0]] privs = output[pieces[0]]
@ -859,7 +657,11 @@ def privileges_unpack(priv, mode, column_case_sensitive, ensure_usage=True):
# Handle cases when there's privs like GRANT SELECT (colA, ...) in privs. # Handle cases when there's privs like GRANT SELECT (colA, ...) in privs.
output[pieces[0]] = normalize_col_grants(output[pieces[0]]) output[pieces[0]] = normalize_col_grants(output[pieces[0]])
if ensure_usage and '*.*' not in output: new_privs = frozenset(privs)
if not new_privs.issubset(valid_privs):
raise InvalidPrivsError('Invalid privileges specified: %s' % new_privs.difference(valid_privs))
if '*.*' not in output:
output['*.*'] = ['USAGE'] output['*.*'] = ['USAGE']
return output return output
@ -878,19 +680,17 @@ def privileges_revoke(cursor, user, host, db_table, priv, grant_option, maria_ro
query = ' '.join(query) query = ' '.join(query)
cursor.execute(query, (user, host)) cursor.execute(query, (user, host))
priv_string = ",".join([p for p in priv if p not in ('GRANT', )]) priv_string = ",".join([p for p in priv if p not in ('GRANT', )])
query = ["REVOKE %s ON %s" % (priv_string, db_table)]
if priv_string != "": if not maria_role:
query = ["REVOKE %s ON %s" % (priv_string, db_table)] query.append("FROM %s@%s")
params = (user, host)
else:
query.append("FROM %s")
params = (user,)
if not maria_role: query = ' '.join(query)
query.append("FROM %s@%s") cursor.execute(query, params)
params = (user, host)
else:
query.append("FROM %s")
params = (user,)
query = ' '.join(query)
cursor.execute(query, params)
cursor.execute("FLUSH PRIVILEGES") cursor.execute("FLUSH PRIVILEGES")
@ -916,7 +716,6 @@ def privileges_grant(cursor, user, host, db_table, priv, tls_requires, maria_rol
query.append("TO %s") query.append("TO %s")
params = (user) params = (user)
impl = get_user_implementation(cursor)
if tls_requires and impl.use_old_user_mgmt(cursor): if tls_requires and impl.use_old_user_mgmt(cursor):
query, params = mogrify_requires(" ".join(query), params, tls_requires) query, params = mogrify_requires(" ".join(query), params, tls_requires)
query = [query] query = [query]
@ -927,11 +726,7 @@ def privileges_grant(cursor, user, host, db_table, priv, tls_requires, maria_rol
if isinstance(params, str): if isinstance(params, str):
params = (params,) params = (params,)
try: cursor.execute(query, params)
cursor.execute(query, params)
except (mysql_driver.ProgrammingError, mysql_driver.OperationalError, mysql_driver.InternalError) as e:
raise InvalidPrivsError("Error granting privileges, invalid priv string: %s , params: %s, query: %s ,"
" exception: %s." % (priv_string, str(params), query, str(e)))
def convert_priv_dict_to_str(priv): def convert_priv_dict_to_str(priv):
@ -948,6 +743,54 @@ def convert_priv_dict_to_str(priv):
return '/'.join(priv_list) return '/'.join(priv_list)
def handle_requiressl_in_priv_string(module, priv, tls_requires):
module.deprecate('The "REQUIRESSL" privilege is deprecated, use the "tls_requires" option instead.',
version='3.0.0', collection_name='community.mysql')
priv_groups = re.search(r"(.*?)(\*\.\*:)([^/]*)(.*)", priv)
if priv_groups.group(3) == "REQUIRESSL":
priv = priv_groups.group(1) + priv_groups.group(4) or None
else:
inner_priv_groups = re.search(r"(.*?),?REQUIRESSL,?(.*)", priv_groups.group(3))
priv = '{0}{1}{2}{3}'.format(
priv_groups.group(1),
priv_groups.group(2),
','.join(filter(None, (inner_priv_groups.group(1), inner_priv_groups.group(2)))),
priv_groups.group(4)
)
if not tls_requires:
tls_requires = "SSL"
else:
module.warn('Ignoring "REQUIRESSL" privilege as "tls_requires" is defined and it takes precedence.')
return priv, tls_requires
# Alter user is supported since MySQL 5.6 and MariaDB 10.2.0
def server_supports_alter_user(cursor):
"""Check if the server supports ALTER USER statement or doesn't.
Args:
cursor (cursor): DB driver cursor object.
Returns: True if supports, False otherwise.
"""
cursor.execute("SELECT VERSION()")
version_str = cursor.fetchone()[0]
version = version_str.split('.')
if 'mariadb' in version_str.lower():
# MariaDB 10.2 and later
if int(version[0]) * 1000 + int(version[1]) >= 10002:
return True
else:
return False
else:
# MySQL 5.6 and later
if int(version[0]) * 1000 + int(version[1]) >= 5006:
return True
else:
return False
def get_resource_limits(cursor, user, host): def get_resource_limits(cursor, user, host):
"""Get user resource limits. """Get user resource limits.
@ -967,11 +810,6 @@ def get_resource_limits(cursor, user, host):
cursor.execute(query, (user, host)) cursor.execute(query, (user, host))
res = cursor.fetchone() res = cursor.fetchone()
# Mysql_info use a DictCursor so we must convert back to a list
# otherwise we get KeyError 0
if isinstance(res, dict):
res = list(res.values())
if not res: if not res:
return None return None
@ -981,26 +819,6 @@ def get_resource_limits(cursor, user, host):
'MAX_CONNECTIONS_PER_HOUR': res[2], 'MAX_CONNECTIONS_PER_HOUR': res[2],
'MAX_USER_CONNECTIONS': res[3], 'MAX_USER_CONNECTIONS': res[3],
} }
cursor.execute("SELECT VERSION()")
srv_type = cursor.fetchone()
# Mysql_info use a DictCursor so we must convert back to a list
# otherwise we get KeyError 0
if isinstance(srv_type, dict):
srv_type = list(srv_type.values())
if 'mariadb' in srv_type[0].lower():
query = ('SELECT max_statement_time AS MAX_STATEMENT_TIME '
'FROM mysql.user WHERE User = %s AND Host = %s')
cursor.execute(query, (user, host))
res_max_statement_time = cursor.fetchone()
# Mysql_info use a DictCursor so we must convert back to a list
# otherwise we get KeyError 0
if isinstance(res_max_statement_time, dict):
res_max_statement_time = list(res_max_statement_time.values())
current_limits['MAX_STATEMENT_TIME'] = res_max_statement_time[0]
return current_limits return current_limits
@ -1053,16 +871,10 @@ def limit_resources(module, cursor, user, host, resource_limits, check_mode):
Returns: True, if changed, False otherwise. Returns: True, if changed, False otherwise.
""" """
impl = get_user_implementation(cursor) if not server_supports_alter_user(cursor):
if not impl.server_supports_alter_user(cursor):
module.fail_json(msg="The server version does not match the requirements " module.fail_json(msg="The server version does not match the requirements "
"for resource_limits parameter. See module's documentation.") "for resource_limits parameter. See module's documentation.")
cursor.execute("SELECT VERSION()")
if 'mariadb' not in cursor.fetchone()[0].lower():
if 'MAX_STATEMENT_TIME' in resource_limits:
module.fail_json(msg="MAX_STATEMENT_TIME resource limit is only supported by MariaDB.")
current_limits = get_resource_limits(cursor, user, host) current_limits = get_resource_limits(cursor, user, host)
needs_to_change = match_resource_limits(module, current_limits, resource_limits) needs_to_change = match_resource_limits(module, current_limits, resource_limits)
@ -1084,116 +896,12 @@ def limit_resources(module, cursor, user, host, resource_limits, check_mode):
return True return True
def set_password_expire(cursor, user, host, password_expire, password_expire_interval): def get_impl(cursor):
"""Fuction to set passowrd expiration for user. global impl
cursor.execute("SELECT VERSION()")
Args: if 'mariadb' in cursor.fetchone()[0].lower():
cursor (cursor): DB driver cursor object.
user (str): User name.
host (str): User hostname.
password_expire (str): Password expiration mode.
password_expire_days (int): Invterval of days password expires.
"""
if password_expire.lower() == "never":
statement = "PASSWORD EXPIRE NEVER"
elif password_expire.lower() == "default":
statement = "PASSWORD EXPIRE DEFAULT"
elif password_expire.lower() == "interval":
statement = "PASSWORD EXPIRE INTERVAL %d DAY" % (password_expire_interval)
elif password_expire.lower() == "now":
statement = "PASSWORD EXPIRE"
cursor.execute("ALTER USER %s@%s " + statement, (user, host))
def get_password_expiration_policy(cursor, user, host, maria_role=False):
"""Function to get password policy for user.
Args:
cursor (cursor): DB driver cursor object.
user (str): User name.
host (str): User hostname.
maria_role (bool, optional): mariadb or mysql. Defaults to False.
Returns:
policy (int): Current users password policy.
"""
if not maria_role:
statement = "SELECT IFNULL(password_lifetime, -1) FROM mysql.user \
WHERE User = %s AND Host = %s", (user, host)
else:
statement = "SELECT JSON_EXTRACT(Priv, '$.password_lifetime') AS password_lifetime \
FROM mysql.global_priv \
WHERE User = %s AND Host = %s", (user, host)
cursor.execute(*statement)
policy = cursor.fetchone()[0]
return int(policy)
def is_password_expired(cursor, user, host):
"""Function to check if password is expired
Args:
cursor (cursor): DB driver cursor object.
user (str): User name.
host (str): User hostname.
Returns:
expired (bool): True if expired, else False.
"""
statement = "SELECT password_expired FROM mysql.user \
WHERE User = %s AND Host = %s", (user, host)
cursor.execute(*statement)
expired = cursor.fetchone()[0]
if str(expired) == "Y":
return True
return False
def get_attribute_support(cursor):
"""Checks if the MySQL server supports user attributes.
Args:
cursor (cursor): DB driver cursor object.
Returns:
True if attributes are supported, False if they are not.
"""
try:
# information_schema.tables does not hold the tables within information_schema itself
cursor.execute("SELECT attribute FROM INFORMATION_SCHEMA.USER_ATTRIBUTES LIMIT 0")
cursor.fetchone()
except mysql_driver.Error:
return False
return True
def attributes_get(cursor, user, host):
"""Get attributes for a given user.
Args:
cursor (cursor): DB driver cursor object.
user (str): User name.
host (str): User host name.
Returns:
None if the user does not exist or the user has no attributes set, otherwise a dict of attributes set on the user
"""
cursor.execute("SELECT attribute FROM INFORMATION_SCHEMA.USER_ATTRIBUTES WHERE user = %s AND host = %s", (user, host))
r = cursor.fetchone()
# convert JSON string stored in row into a dict - mysql enforces that user_attributes entires are in JSON format
j = json.loads(r[0]) if r and r[0] else None
# if the attributes dict is empty, return None instead
return j if j else None
def get_user_implementation(cursor):
db_engine = get_server_implementation(cursor)
if db_engine == 'mariadb':
from ansible_collections.community.mysql.plugins.module_utils.implementations.mariadb import user as mariauser from ansible_collections.community.mysql.plugins.module_utils.implementations.mariadb import user as mariauser
return mariauser impl = mariauser
else: else:
from ansible_collections.community.mysql.plugins.module_utils.implementations.mysql import user as mysqluser from ansible_collections.community.mysql.plugins.module_utils.implementations.mysql import user as mysqluser
return mysqluser impl = mysqluser

View file

@ -11,9 +11,9 @@ __metaclass__ = type
DOCUMENTATION = r''' DOCUMENTATION = r'''
--- ---
module: mysql_db module: mysql_db
short_description: Add or remove MySQL or MariaDB databases from a remote host short_description: Add or remove MySQL databases from a remote host
description: description:
- Add or remove MySQL or MariaDB databases from a remote host. - Add or remove MySQL databases from a remote host.
options: options:
name: name:
description: description:
@ -46,8 +46,8 @@ options:
target: target:
description: description:
- Location, on the remote host, of the dump file to read from or write to. - 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)), - Uncompressed SQL files (C(.sql)) as well as bzip2 (C(.bz2)), gzip (C(.gz)) and
xz (Added in 2.0) and zstd (C(.zst)) (Added in 3.12.0) compressed files are supported. xz (Added in 2.0) compressed files are supported.
type: path type: path
single_transaction: single_transaction:
description: description:
@ -150,21 +150,6 @@ options:
type: bool type: bool
default: false default: false
version_added: '0.1.0' version_added: '0.1.0'
chdir:
description:
- Changes the current working directory.
- Can be useful, for example, when I(state=import) and a dump file contains relative paths.
type: path
version_added: '3.4.0'
pipefail:
description:
- Use C(bash) instead of C(sh) and add C(-o pipefail) to catch errors from the
mysql_dump command when I(state=dump) and compression is used.
- The default is C(no) to prevent issues on systems without bash as a default interpreter.
- The default will change to C(yes) in community.mysql 4.0.0.
type: bool
default: false
version_added: '3.4.0'
seealso: seealso:
- module: community.mysql.mysql_info - module: community.mysql.mysql_info
@ -188,7 +173,6 @@ requirements:
- mysql (command line binary) - mysql (command line binary)
- mysqldump (command line binary) - mysqldump (command line binary)
notes: notes:
- Compatible with MariaDB or MySQL.
- Requires the mysql and mysqldump binaries on the remote host. - Requires the mysql and mysqldump binaries on the remote host.
- This module is B(not idempotent) when I(state) is C(import), - This module is B(not idempotent) when I(state) is C(import),
and will import the dump file each time if run more than once. and will import the dump file each time if run more than once.
@ -309,13 +293,6 @@ EXAMPLES = r'''
login_password: 123456 login_password: 123456
name: bobdata name: bobdata
state: present state: present
- name: Dump a database with compression and catch errors from mysqldump with bash pipefail
community.mysql.mysql_db:
state: dump
name: foo
target: /tmp/dump.sql.gz
pipefail: true
''' '''
RETURN = r''' RETURN = r'''
@ -343,15 +320,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 ( from ansible_collections.community.mysql.plugins.module_utils.mysql import mysql_connect, mysql_driver, mysql_driver_fail_msg, mysql_common_argument_spec
mysql_connect,
mysql_driver,
mysql_driver_fail_msg,
mysql_common_argument_spec,
get_server_implementation,
get_server_version,
)
from ansible_collections.community.mysql.plugins.module_utils.version import LooseVersion
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
@ -380,81 +349,68 @@ def db_delete(cursor, db):
def db_dump(module, host, user, password, db_name, target, all_databases, port, def db_dump(module, host, user, password, db_name, target, all_databases, port,
config_file, server_implementation, server_version, socket=None, config_file, socket=None, ssl_cert=None, ssl_key=None, ssl_ca=None,
ssl_cert=None, ssl_key=None, ssl_ca=None,
single_transaction=None, quick=None, ignore_tables=None, hex_blob=None, single_transaction=None, quick=None, ignore_tables=None, hex_blob=None,
encoding=None, force=False, master_data=0, skip_lock_tables=False, encoding=None, force=False, master_data=0, skip_lock_tables=False,
dump_extra_args=None, unsafe_password=False, restrict_config_file=False, dump_extra_args=None, unsafe_password=False, restrict_config_file=False,
check_implicit_admin=False, pipefail=False): check_implicit_admin=False):
cmd = module.get_bin_path('mysqldump', True)
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)
# If defined, mysqldump demands --defaults-extra-file be the first option # If defined, mysqldump demands --defaults-extra-file be the first option
if config_file: if config_file:
if restrict_config_file: if restrict_config_file:
cmd.append("--defaults-file=%s" % shlex_quote(config_file)) cmd += " --defaults-file=%s" % shlex_quote(config_file)
else: else:
cmd.append("--defaults-extra-file=%s" % shlex_quote(config_file)) cmd += " --defaults-extra-file=%s" % shlex_quote(config_file)
if check_implicit_admin: if check_implicit_admin:
cmd.append("--user=root --password=''") cmd += " --user=root --password=''"
else: else:
if user is not None: if user is not None:
cmd.append("--user=%s" % shlex_quote(user)) cmd += " --user=%s" % shlex_quote(user)
if password is not None: if password is not None:
if not unsafe_password: if not unsafe_password:
cmd.append("--password=%s" % shlex_quote(password)) cmd += " --password=%s" % shlex_quote(password)
else: else:
cmd.append("--password=%s" % password) cmd += " --password=%s" % password
if ssl_cert is not None: 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: 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: 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: if force:
cmd.append("--force") cmd += " --force"
if socket is not None: if socket is not None:
cmd.append("--socket=%s" % shlex_quote(socket)) cmd += " --socket=%s" % shlex_quote(socket)
else: else:
cmd.append("--host=%s --port=%i" % (shlex_quote(host), port)) cmd += " --host=%s --port=%i" % (shlex_quote(host), port)
if all_databases: if all_databases:
cmd.append("--all-databases") cmd += " --all-databases"
elif len(db_name) > 1: elif len(db_name) > 1:
cmd.append("--databases {0}".format(' '.join(db_name))) cmd += " --databases {0}".format(' '.join(db_name))
else: else:
cmd.append("%s" % shlex_quote(' '.join(db_name))) cmd += " %s" % shlex_quote(' '.join(db_name))
if skip_lock_tables: if skip_lock_tables:
cmd.append("--skip-lock-tables") cmd += " --skip-lock-tables"
if (encoding is not None) and (encoding != ""): 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: if single_transaction:
cmd.append("--single-transaction=true") cmd += " --single-transaction=true"
if quick: if quick:
cmd.append("--quick") cmd += " --quick"
if ignore_tables: if ignore_tables:
for an_ignored_table in 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: if hex_blob:
cmd.append("--hex-blob") cmd += " --hex-blob"
if master_data: if master_data:
if (server_implementation == 'mysql' and cmd += " --master-data=%s" % master_data
LooseVersion(server_version) >= LooseVersion("8.2.0")):
cmd.append("--source-data=%s" % master_data)
else:
cmd.append("--master-data=%s" % master_data)
if dump_extra_args is not None: if dump_extra_args is not None:
cmd.append(dump_extra_args) cmd += " " + dump_extra_args
path = None path = None
if os.path.splitext(target)[-1] == '.gz': if os.path.splitext(target)[-1] == '.gz':
@ -463,44 +419,25 @@ def db_dump(module, host, user, password, db_name, target, all_databases, port,
path = module.get_bin_path('bzip2', True) path = module.get_bin_path('bzip2', True)
elif os.path.splitext(target)[-1] == '.xz': elif os.path.splitext(target)[-1] == '.xz':
path = module.get_bin_path('xz', True) 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: if path:
cmd = '%s | %s > %s' % (cmd, path, shlex_quote(target)) cmd = '%s | %s > %s' % (cmd, path, shlex_quote(target))
if pipefail:
cmd = 'set -o pipefail && ' + cmd
else: else:
cmd += " > %s" % shlex_quote(target) cmd += " > %s" % shlex_quote(target)
executed_commands.append(cmd) executed_commands.append(cmd)
rc, stdout, stderr = module.run_command(cmd, use_unsafe_shell=True)
if pipefail:
rc, stdout, stderr = module.run_command(cmd, use_unsafe_shell=True, executable='bash')
else:
rc, stdout, stderr = module.run_command(cmd, use_unsafe_shell=True)
return rc, stdout, stderr return rc, stdout, stderr
def db_import(module, host, user, password, db_name, target, all_databases, port, config_file, 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, socket=None, ssl_cert=None, ssl_key=None, ssl_ca=None, encoding=None, force=False,
encoding=None, force=False,
use_shell=False, unsafe_password=False, restrict_config_file=False, use_shell=False, unsafe_password=False, restrict_config_file=False,
check_implicit_admin=False): check_implicit_admin=False):
if not os.path.exists(target): if not os.path.exists(target):
return module.fail_json(msg="target %s does not exist on the host" % target) return module.fail_json(msg="target %s does not exist on the host" % target)
cmd_str = 'mysql' cmd = [module.get_bin_path('mysql', True)]
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)
# --defaults-file must go first, or errors out # --defaults-file must go first, or errors out
if config_file: if config_file:
if restrict_config_file: if restrict_config_file:
@ -546,8 +483,6 @@ def db_import(module, host, user, password, db_name, target, all_databases, port
comp_prog_path = module.get_bin_path('bzip2', required=True) comp_prog_path = module.get_bin_path('bzip2', required=True)
elif os.path.splitext(target)[-1] == '.xz': elif os.path.splitext(target)[-1] == '.xz':
comp_prog_path = module.get_bin_path('xz', required=True) 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: if comp_prog_path:
# The line below is for returned data only: # The line below is for returned data only:
executed_commands.append('%s -dc %s | %s' % (comp_prog_path, target, cmd)) executed_commands.append('%s -dc %s | %s' % (comp_prog_path, target, cmd))
@ -613,14 +548,14 @@ def db_create(cursor, db, encoding, collation):
def main(): def main():
argument_spec = mysql_common_argument_spec() argument_spec = mysql_common_argument_spec()
argument_spec.update( argument_spec.update(
name=dict(type='list', elements='str', 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']),
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', elements='str', default=[]), ignore_tables=dict(type='list', default=[]),
hex_blob=dict(default=False, type='bool'), hex_blob=dict(default=False, type='bool'),
force=dict(type='bool', default=False), force=dict(type='bool', default=False),
master_data=dict(type='int', default=0, choices=[0, 1, 2]), master_data=dict(type='int', default=0, choices=[0, 1, 2]),
@ -631,8 +566,6 @@ def main():
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),
chdir=dict(type='path'),
pipefail=dict(type='bool', default=False),
) )
module = AnsibleModule( module = AnsibleModule(
@ -681,14 +614,6 @@ def main():
restrict_config_file = module.params["restrict_config_file"] restrict_config_file = module.params["restrict_config_file"]
check_implicit_admin = module.params['check_implicit_admin'] check_implicit_admin = module.params['check_implicit_admin']
config_overrides_defaults = module.params['config_overrides_defaults'] config_overrides_defaults = module.params['config_overrides_defaults']
chdir = module.params['chdir']
pipefail = module.params['pipefail']
if chdir:
try:
os.chdir(chdir)
except Exception as e:
module.fail_json("Cannot change the current directory to %s: %s" % (chdir, e))
if len(db) > 1 and state == 'import': if len(db) > 1 and state == 'import':
module.fail_json(msg="Multiple databases are not supported with state=import") module.fail_json(msg="Multiple databases are not supported with state=import")
@ -725,9 +650,6 @@ def main():
else: else:
module.fail_json(msg="unable to find %s. Exception message: %s" % (config_file, to_native(e))) module.fail_json(msg="unable to find %s. Exception message: %s" % (config_file, to_native(e)))
server_implementation = get_server_implementation(cursor)
server_version = get_server_version(cursor)
changed = False changed = False
if not os.path.exists(config_file): if not os.path.exists(config_file):
config_file = None config_file = None
@ -768,12 +690,11 @@ def main():
module.exit_json(changed=True, db=db_name, db_list=db) module.exit_json(changed=True, db=db_name, db_list=db)
rc, stdout, stderr = db_dump(module, login_host, login_user, rc, stdout, stderr = db_dump(module, login_host, login_user,
login_password, db, target, all_databases, login_password, db, target, all_databases,
login_port, config_file, server_implementation, server_version, login_port, config_file, socket, ssl_cert, ssl_key,
socket, ssl_cert, ssl_key,
ssl_ca, single_transaction, quick, ignore_tables, ssl_ca, single_transaction, quick, ignore_tables,
hex_blob, encoding, force, master_data, skip_lock_tables, hex_blob, encoding, force, master_data, skip_lock_tables,
dump_extra_args, unsafe_login_password, restrict_config_file, dump_extra_args, unsafe_login_password, restrict_config_file,
check_implicit_admin, pipefail) check_implicit_admin)
if rc != 0: if rc != 0:
module.fail_json(msg="%s" % stderr) module.fail_json(msg="%s" % stderr)
module.exit_json(changed=True, db=db_name, db_list=db, msg=stdout, module.exit_json(changed=True, db=db_name, db_list=db, msg=stdout,
@ -790,8 +711,8 @@ def main():
rc, stdout, stderr = db_import(module, login_host, login_user, rc, stdout, stderr = db_import(module, login_host, login_user,
login_password, db, target, login_password, db, target,
all_databases, all_databases,
login_port, config_file, server_implementation, login_port, config_file,
server_version, socket, ssl_cert, ssl_key, ssl_ca, socket, ssl_cert, ssl_key, ssl_ca,
encoding, force, use_shell, unsafe_login_password, encoding, force, use_shell, unsafe_login_password,
restrict_config_file, check_implicit_admin) restrict_config_file, check_implicit_admin)
if rc != 0: if rc != 0:

View file

@ -1,27 +1,25 @@
#!/usr/bin/python #!/usr/bin/python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) <andrew.a.klychkov@gmail.com> # 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) # 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 from __future__ import absolute_import, division, print_function
__metaclass__ = type __metaclass__ = type
DOCUMENTATION = r''' DOCUMENTATION = r'''
--- ---
module: mysql_info module: mysql_info
short_description: Gather information about MySQL or MariaDB servers short_description: Gather information about MySQL servers
description: description:
- Gathers information about MySQL or MariaDB servers. - Gathers information about MySQL servers.
options: options:
filter: filter:
description: description:
- Limit the collected information by comma separated string or YAML list. - Limit the collected information by comma separated string or YAML list.
- Allowable values are C(version), C(databases), C(settings), C(global_status), - Allowable values are C(version), C(databases), C(settings), C(global_status),
C(users), C(users_info), C(engines), C(master_status), C(slave_status), C(slave_hosts). C(users), C(engines), C(master_status), C(slave_status), C(slave_hosts).
- By default, collects all subsets. - By default, collects all subsets.
- You can use '!' before value (for example, C(!settings)) to exclude it from the information. - You can use '!' before value (for example, C(!settings)) to exclude it from the information.
- If you pass including and excluding values to the filter, for example, I(filter=!settings,version), - If you pass including and excluding values to the filter, for example, I(filter=!settings,version),
@ -36,7 +34,7 @@ options:
exclude_fields: exclude_fields:
description: description:
- List of fields which are not needed to collect. - List of fields which are not needed to collect.
- "Supports elements: C(db_size), C(db_table_count). Unsupported elements will be ignored." - "Supports elements: C(db_size). Unsupported elements will be ignored."
type: list type: list
elements: str elements: str
version_added: '0.1.0' version_added: '0.1.0'
@ -47,7 +45,6 @@ options:
default: false default: false
notes: notes:
- Compatible with MariaDB or MySQL.
- Calculating the size of a database might be slow, depending on the number and size of tables in it. - Calculating the size of a database might be slow, depending on the number and size of tables in it.
To avoid this, use I(exclude_fields=db_size). To avoid this, use I(exclude_fields=db_size).
@ -77,9 +74,6 @@ EXAMPLES = r'''
# Display only databases and users info: # Display only databases and users info:
# ansible mysql-hosts -m mysql_info -a 'filter=databases,users' # ansible mysql-hosts -m mysql_info -a 'filter=databases,users'
# Display all users privileges:
# ansible mysql-hosts -m mysql_info -a 'filter=users_info'
# Display only slave status: # Display only slave status:
# ansible standby -m mysql_info -a 'filter=slave_status' # ansible standby -m mysql_info -a 'filter=slave_status'
@ -128,47 +122,9 @@ EXAMPLES = r'''
- databases - databases
exclude_fields: db_size exclude_fields: db_size
return_empty_dbs: true return_empty_dbs: true
- name: Clone users from one server to another
block:
# Step 1
- name: Fetch information from a source server
delegate_to: server_source
community.mysql.mysql_info:
filter:
- users_info
register: result
# Step 2
# Don't work with sha256_password and cache_sha2_password
- name: Clone users fetched in a previous task to a target server
community.mysql.mysql_user:
name: "{{ item.name }}"
host: "{{ item.host }}"
plugin: "{{ item.plugin | default(omit) }}"
plugin_auth_string: "{{ item.plugin_auth_string | default(omit) }}"
plugin_hash_string: "{{ item.plugin_hash_string | default(omit) }}"
tls_requires: "{{ item.tls_requires | default(omit) }}"
priv: "{{ item.priv | default(omit) }}"
resource_limits: "{{ item.resource_limits | default(omit) }}"
column_case_sensitive: true
state: present
loop: "{{ result.users_info }}"
loop_control:
label: "{{ item.name }}@{{ item.host }}"
when:
- item.name != 'root' # In case you don't want to import admin accounts
- item.name != 'mariadb.sys'
- item.name != 'mysql'
''' '''
RETURN = r''' RETURN = r'''
server_engine:
description: Database server engine.
returned: if not excluded by filter
type: str
sample: 'MariaDB'
version_added: '3.10.0'
version: version:
description: Database server version. description: Database server version.
returned: if not excluded by filter returned: if not excluded by filter
@ -205,19 +161,13 @@ databases:
returned: if not excluded by filter returned: if not excluded by filter
type: dict type: dict
sample: sample:
- { "mysql": { "size": 656594, "tables": 31 }, "information_schema": { "size": 73728, "tables": 79 } } - { "mysql": { "size": 656594 }, "information_schema": { "size": 73728 } }
contains: contains:
size: size:
description: Database size in bytes. description: Database size in bytes.
returned: if not excluded by filter returned: if not excluded by filter
type: dict type: dict
sample: { 'size': 656594 } sample: { 'size': 656594 }
tables:
description: Count of tables and views in that database.
returned: if not excluded by filter
type: dict
sample: { 'tables': 12 }
version_added: '3.11.0'
settings: settings:
description: Global settings (variables) information. description: Global settings (variables) information.
returned: if not excluded by filter returned: if not excluded by filter
@ -231,32 +181,11 @@ global_status:
sample: sample:
- { "Innodb_buffer_pool_read_requests": 123, "Innodb_buffer_pool_reads": 32 } - { "Innodb_buffer_pool_read_requests": 123, "Innodb_buffer_pool_reads": 32 }
users: users:
description: Return a dictionnary of users grouped by host and with global privileges only. description: Users information.
returned: if not excluded by filter returned: if not excluded by filter
type: dict type: dict
sample: sample:
- { "localhost": { "root": { "Alter_priv": "Y", "Alter_routine_priv": "Y" } } } - { "localhost": { "root": { "Alter_priv": "Y", "Alter_routine_priv": "Y" } } }
users_info:
description:
- Information about users accounts.
- The output can be used as an input of the M(community.mysql.mysql_user) plugin.
- Useful when migrating accounts to another server or to create an inventory.
- Does not support proxy privileges. If an account has proxy privileges, they won't appear in the output.
- Causes issues with authentications plugins C(sha256_password) and C(caching_sha2_password).
If the output is fed to M(community.mysql.mysql_user), the
``plugin_auth_string`` will most likely be unreadable due to non-binary
characters.
returned: if not excluded by filter
type: dict
sample:
- { "plugin_auth_string": '*1234567',
"name": "user1",
"host": "host.com",
"plugin": "mysql_native_password",
"priv": "db1.*:SELECT/db2.*:SELECT",
"resource_limits": { "MAX_USER_CONNECTIONS": 100 },
"tls_requires": { "SSL": null } }
version_added: '3.8.0'
engines: engines:
description: Information about the server's storage engines. description: Information about the server's storage engines.
returned: if not excluded by filter returned: if not excluded by filter
@ -287,22 +216,20 @@ connector_name:
type: str type: str
sample: sample:
- "pymysql" - "pymysql"
version_added: '3.6.0' - "MySQLdb"
version_added: '2.4.0'
connector_version: connector_version:
description: Version of the python connector used by the module. When the connector is not identified, returns C(Unknown). description: Version of the python connector used by the module. When the connector is not identified, returns C(Unknown).
returned: always returned: always
type: str type: str
sample: sample:
- "1.0.2" - "1.0.2"
version_added: '3.6.0' version_added: '2.4.0'
''' '''
from decimal import Decimal from decimal import Decimal
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.mysql.plugins.module_utils.command_resolver import (
CommandResolver
)
from ansible_collections.community.mysql.plugins.module_utils.mysql import ( from ansible_collections.community.mysql.plugins.module_utils.mysql import (
mysql_connect, mysql_connect,
mysql_common_argument_spec, mysql_common_argument_spec,
@ -310,16 +237,6 @@ from ansible_collections.community.mysql.plugins.module_utils.mysql import (
mysql_driver_fail_msg, mysql_driver_fail_msg,
get_connector_name, get_connector_name,
get_connector_version, get_connector_version,
get_server_implementation,
get_server_version,
)
from ansible_collections.community.mysql.plugins.module_utils.user import (
privileges_get,
get_resource_limits,
get_existing_authentication,
get_user_implementation,
user_is_locked,
) )
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
@ -347,13 +264,9 @@ class MySQL_Info(object):
5. add info about the new subset with an example to RETURN block 5. add info about the new subset with an example to RETURN block
""" """
def __init__(self, module, cursor, server_implementation, server_version, user_implementation): def __init__(self, module, cursor):
self.module = module self.module = module
self.cursor = cursor self.cursor = cursor
self.server_implementation = server_implementation
self.server_version = server_version
self.user_implementation = user_implementation
self.command_resolver = CommandResolver(self.server_implementation, self.server_version)
self.info = { self.info = {
'version': {}, 'version': {},
'databases': {}, 'databases': {},
@ -361,7 +274,6 @@ class MySQL_Info(object):
'global_status': {}, 'global_status': {},
'engines': {}, 'engines': {},
'users': {}, 'users': {},
'users_info': {},
'master_status': {}, 'master_status': {},
'slave_hosts': {}, 'slave_hosts': {},
'slave_status': {}, 'slave_status': {},
@ -430,9 +342,6 @@ class MySQL_Info(object):
if 'users' in wanted: if 'users' in wanted:
self.__get_users() self.__get_users()
if 'users_info' in wanted:
self.__get_users_info()
if 'master_status' in wanted: if 'master_status' in wanted:
self.__get_master_status() self.__get_master_status()
@ -515,8 +424,7 @@ class MySQL_Info(object):
def __get_master_status(self): def __get_master_status(self):
"""Get master status if the instance is a master.""" """Get master status if the instance is a master."""
query = self.command_resolver.resolve_command("SHOW MASTER STATUS") res = self.__exec_sql('SHOW MASTER STATUS')
res = self.__exec_sql(query)
if res: if res:
for line in res: for line in res:
for vname, val in iteritems(line): for vname, val in iteritems(line):
@ -524,8 +432,7 @@ class MySQL_Info(object):
def __get_slave_status(self): def __get_slave_status(self):
"""Get slave status if the instance is a slave.""" """Get slave status if the instance is a slave."""
query = self.command_resolver.resolve_command("SHOW SLAVE STATUS") res = self.__exec_sql('SHOW SLAVE STATUS')
res = self.__exec_sql(query)
if res: if res:
for line in res: for line in res:
host = line['Master_Host'] host = line['Master_Host']
@ -546,8 +453,7 @@ class MySQL_Info(object):
def __get_slaves(self): def __get_slaves(self):
"""Get slave hosts info if the instance is a master.""" """Get slave hosts info if the instance is a master."""
query = self.command_resolver.resolve_command("SHOW SLAVE HOSTS") res = self.__exec_sql('SHOW SLAVE HOSTS')
res = self.__exec_sql(query)
if res: if res:
for line in res: for line in res:
srv_id = line['Server_id'] srv_id = line['Server_id']
@ -574,131 +480,42 @@ class MySQL_Info(object):
if vname not in ('Host', 'User'): if vname not in ('Host', 'User'):
self.info['users'][host][user][vname] = self.__convert(val) self.info['users'][host][user][vname] = self.__convert(val)
def __get_users_info(self):
"""Get user privileges, passwords, resources_limits, ...
Query the server to get all the users and return a string
of privileges that can be used by the mysql_user plugin.
For instance:
"users_info": [
{
"host": "users_info.com",
"priv": "*.*: ALL,GRANT",
"name": "users_info_adm"
},
{
"host": "users_info.com",
"priv": "`mysql`.*: SELECT/`users_info_db`.*: SELECT",
"name": "users_info_multi"
}
]
"""
res = self.__exec_sql('SELECT * FROM mysql.user')
if not res:
return None
output = list()
for line in res:
user = line['User']
host = line['Host']
user_priv = privileges_get(self.cursor, user, host)
if not user_priv:
self.module.warn("No privileges found for %s on host %s" % (user, host))
continue
priv_string = list()
for db_table, priv in user_priv.items():
# Proxy privileges are hard to work with because of different quotes or
# backticks like ''@'', ''@'%' or even ``@``. In addition, MySQL will
# forbid you to grant a proxy privileges through TCP.
if set(priv) == {'PROXY', 'GRANT'} or set(priv) == {'PROXY'}:
continue
unquote_db_table = db_table.replace('`', '').replace("'", '')
priv_string.append('%s:%s' % (unquote_db_table, ','.join(priv)))
# Only keep *.* USAGE if it's the only user privilege given
if len(priv_string) > 1 and '*.*:USAGE' in priv_string:
priv_string.remove('*.*:USAGE')
resource_limits = get_resource_limits(self.cursor, user, host)
copy_ressource_limits = dict.copy(resource_limits)
tls_requires = self.user_implementation.get_tls_requires(
self.cursor, user, host)
output_dict = {
'name': user,
'host': host,
'priv': '/'.join(priv_string),
'resource_limits': copy_ressource_limits,
'tls_requires': tls_requires,
}
# Prevent returning a resource limit if empty
if resource_limits:
for key, value in resource_limits.items():
if value == 0:
del output_dict['resource_limits'][key]
if len(output_dict['resource_limits']) == 0:
del output_dict['resource_limits']
# Prevent returning tls_require if empty
if not tls_requires:
del output_dict['tls_requires']
authentications = get_existing_authentication(self.cursor, user, host)
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
# but both are not supported by mysql_user atm. So no point yet.
output.append(output_dict)
self.info['users_info'] = output
def __get_databases(self, exclude_fields, return_empty_dbs): def __get_databases(self, exclude_fields, return_empty_dbs):
"""Get info about databases.""" """Get info about databases."""
if not exclude_fields:
query = ('SELECT table_schema AS "name", '
'SUM(data_length + index_length) AS "size" '
'FROM information_schema.TABLES GROUP BY table_schema')
else:
if 'db_size' in exclude_fields:
query = ('SELECT table_schema AS "name" '
'FROM information_schema.TABLES GROUP BY table_schema')
def is_field_included(field_name): res = self.__exec_sql(query)
return not exclude_fields or 'db_{}'.format(field_name) not in exclude_fields
def create_db_info(db_data): if res:
info = {} for db in res:
if is_field_included('size'): self.info['databases'][db['name']] = {}
info['size'] = int(db_data.get('size', 0) or 0)
if is_field_included('table_count'):
info['tables'] = int(db_data.get('tables', 0) or 0)
return info
# Build the main query if not exclude_fields or 'db_size' not in exclude_fields:
query_parts = ['SELECT table_schema AS "name"'] if db['size'] is None:
if is_field_included('size'): db['size'] = 0
query_parts.append('SUM(data_length + index_length) AS "size"')
if is_field_included('table_count'):
query_parts.append('COUNT(table_name) as "tables"')
query = "{} FROM information_schema.TABLES GROUP BY table_schema".format(", ".join(query_parts)) self.info['databases'][db['name']]['size'] = int(db['size'])
# Get and process databases with tables # If empty dbs are not needed in the returned dict, exit from the method
databases = self.__exec_sql(query) or [] if not return_empty_dbs:
for db in databases: return None
self.info['databases'][db['name']] = create_db_info(db)
# Handle empty databases if requested # Add info about empty databases (issue #65727):
if return_empty_dbs: res = self.__exec_sql('SHOW DATABASES')
empty_databases = self.__exec_sql('SHOW DATABASES') or [] if res:
for db in empty_databases: for db in res:
db_name = db['Database'] if db['Database'] not in self.info['databases']:
if db_name not in self.info['databases']: self.info['databases'][db['Database']] = {}
self.info['databases'][db_name] = create_db_info({})
if not exclude_fields or 'db_size' not in exclude_fields:
self.info['databases'][db['Database']]['size'] = 0
def __exec_sql(self, query, ddl=False): def __exec_sql(self, query, ddl=False):
"""Execute SQL. """Execute SQL.
@ -730,8 +547,8 @@ def main():
argument_spec = mysql_common_argument_spec() argument_spec = mysql_common_argument_spec()
argument_spec.update( argument_spec.update(
login_db=dict(type='str'), login_db=dict(type='str'),
filter=dict(type='list', elements='str'), filter=dict(type='list'),
exclude_fields=dict(type='list', elements='str'), exclude_fields=dict(type='list'),
return_empty_dbs=dict(type='bool', default=False), return_empty_dbs=dict(type='bool', default=False),
) )
@ -776,17 +593,12 @@ def main():
'Exception message: %s' % (connector_name, connector_version, config_file, to_native(e))) 'Exception message: %s' % (connector_name, connector_version, config_file, to_native(e)))
module.fail_json(msg) module.fail_json(msg)
server_implementation = get_server_implementation(cursor)
server_version = get_server_version(cursor)
user_implementation = get_user_implementation(cursor)
############################### ###############################
# Create object and do main job # Create object and do main job
mysql = MySQL_Info(module, cursor, server_implementation, server_version, user_implementation) mysql = MySQL_Info(module, cursor)
module.exit_json(changed=False, module.exit_json(changed=False,
server_engine='MariaDB' if server_implementation == 'mariadb' else 'MySQL',
connector_name=connector_name, connector_name=connector_name,
connector_version=connector_version, connector_version=connector_version,
**mysql.get_info(filter_, exclude_fields, return_empty_dbs)) **mysql.get_info(filter_, exclude_fields, return_empty_dbs))

View file

@ -1,7 +1,7 @@
#!/usr/bin/python #!/usr/bin/python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright: (c) 2020, Andrew Klychkov (@Andersson007) <andrew.a.klychkov@gmail.com> # Copyright: (c) 2020, Andrew Klychkov (@Andersson007) <aaklychkov@mail.ru>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # 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) from __future__ import (absolute_import, division, print_function)
@ -10,9 +10,9 @@ __metaclass__ = type
DOCUMENTATION = r''' DOCUMENTATION = r'''
--- ---
module: mysql_query module: mysql_query
short_description: Run MySQL or MariaDB queries short_description: Run MySQL queries
description: description:
- Runs arbitrary MySQL or MariaDB queries. - Runs arbitrary MySQL queries.
- Pay attention, the module does not support check mode! - Pay attention, the module does not support check mode!
All queries will be executed in autocommit mode. All queries will be executed in autocommit mode.
- To run SQL queries from a file, use M(community.mysql.mysql_db) module. - To run SQL queries from a file, use M(community.mysql.mysql_db) module.
@ -26,9 +26,9 @@ options:
as a formatting character. All literal C(%) characters in the query should be as a formatting character. All literal C(%) characters in the query should be
escaped as C(%%). escaped as C(%%).
- Note that if you use the C(IF EXISTS/IF NOT EXISTS) clauses in your query - Note that if you use the C(IF EXISTS/IF NOT EXISTS) clauses in your query
and C(mysqlclient) or C(PyMySQL 0.10.0+) connectors, the module will report and C(mysqlclient) connector, the module will report that
that the state has been changed even if it has not. If it is important in your the state has been changed even if it has not. If it is important in your
workflow, use the C(PyMySQL 0.9.3) connector instead. workflow, use the C(PyMySQL) connector instead.
type: raw type: raw
required: true required: true
positional_args: positional_args:
@ -36,7 +36,6 @@ options:
- List of values to be passed as positional arguments to the query. - List of values to be passed as positional arguments to the query.
- Mutually exclusive with I(named_args). - Mutually exclusive with I(named_args).
type: list type: list
elements: raw
named_args: named_args:
description: description:
- Dictionary of key-value arguments to pass to the query. - Dictionary of key-value arguments to pass to the query.
@ -56,12 +55,11 @@ attributes:
support: none support: none
seealso: seealso:
- module: community.mysql.mysql_db - module: community.mysql.mysql_db
notes:
- Compatible with MariaDB or MySQL.
author: author:
- Andrew Klychkov (@Andersson007) - Andrew Klychkov (@Andersson007)
extends_documentation_fragment: extends_documentation_fragment:
- community.mysql.mysql - community.mysql.mysql
''' '''
EXAMPLES = r''' EXAMPLES = r'''
@ -116,18 +114,8 @@ rowcount:
returned: changed returned: changed
type: list type: list
sample: [5, 1] 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 import warnings
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
@ -148,24 +136,12 @@ DDL_QUERY_KEYWORDS = ('CREATE', 'DROP', 'ALTER', 'RENAME', 'TRUNCATE')
# Module execution. # 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(): def main():
argument_spec = mysql_common_argument_spec() argument_spec = mysql_common_argument_spec()
argument_spec.update( argument_spec.update(
query=dict(type='raw', required=True), query=dict(type='raw', required=True),
login_db=dict(type='str'), login_db=dict(type='str'),
positional_args=dict(type='list', elements='raw'), positional_args=dict(type='list'),
named_args=dict(type='dict'), named_args=dict(type='dict'),
single_transaction=dict(type='bool', default=False), single_transaction=dict(type='bool', default=False),
) )
@ -234,7 +210,6 @@ def main():
query_result = [] query_result = []
executed_queries = [] executed_queries = []
rowcount = [] rowcount = []
execution_time_ms = []
already_exists = False already_exists = False
for q in query: for q in query:
@ -245,14 +220,12 @@ def main():
category=mysql_driver.Warning) category=mysql_driver.Warning)
try: try:
cursor, exec_time_ms = execute_and_return_time(cursor, q, arguments) cursor.execute(q, arguments)
execution_time_ms.append(exec_time_ms)
except mysql_driver.Warning: except mysql_driver.Warning:
# When something is run with IF NOT EXISTS # When something is run with IF NOT EXISTS
# and there's "already exists" MySQL warning, # and there's "already exists" MySQL warning,
# set the flag as True. # set the flag as True.
# PyMySQL < 0.10.0 throws the warning, mysqlclient # PyMySQL throws the warning, mysqlclinet does NOT.
# and PyMySQL 0.10.0+ does NOT.
already_exists = True already_exists = True
except Exception as e: except Exception as e:
@ -303,7 +276,6 @@ def main():
'executed_queries': executed_queries, 'executed_queries': executed_queries,
'query_result': query_result, 'query_result': query_result,
'rowcount': rowcount, 'rowcount': rowcount,
'execution_time_ms': execution_time_ms,
} }
# Exit: # Exit:

View file

@ -2,7 +2,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright: (c) 2013, Balazs Pocze <banyek@gawker.com> # Copyright: (c) 2013, Balazs Pocze <banyek@gawker.com>
# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) <andrew.a.klychkov@gmail.com> # Copyright: (c) 2019, Andrew Klychkov (@Andersson007) <aaklychkov@mail.ru>
# Certain parts are taken from Mark Theunissen's mysqldb module # Certain parts are taken from Mark Theunissen's mysqldb module
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
@ -13,38 +13,42 @@ __metaclass__ = type
DOCUMENTATION = r''' DOCUMENTATION = r'''
--- ---
module: mysql_replication module: mysql_replication
short_description: Manage MySQL or MariaDB replication short_description: Manage MySQL replication
description: description:
- Manages MySQL or MariaDB server replication, replica, primary status, get and change primary host. - Manages MySQL server replication, replica, primary status, get and change primary host.
author: author:
- Balazs Pocze (@banyek) - Balazs Pocze (@banyek)
- Andrew Klychkov (@Andersson007) - Andrew Klychkov (@Andersson007)
- Dennis Urtubia (@dennisurtubia)
- Laurent Indermühle (@laurent-indermuehle)
options: options:
mode: mode:
description: description:
- Module operating mode. Could be - Module operating mode. Could be
C(changeprimary) (CHANGE MASTER TO) - also works for MySQL 8.0.23 and later since community.mysql 3.10.0, C(changeprimary | changemaster) (CHANGE PRIMARY | MASTER TO),
C(changereplication) (CHANGE REPLICATION SOURCE TO) - only supported in MySQL 8.0.23 and later, C(getprimary | getmaster) (SHOW PRIMARY | MASTER STATUS),
C(getprimary) (SHOW MASTER STATUS), C(getreplica | getslave) (SHOW REPLICA | SLAVE STATUS),
C(getreplica) (SHOW REPLICA STATUS), C(startreplica | startslave) (START REPLICA | SLAVE),
C(startreplica) (START REPLICA), C(stopreplica | stopslave) (STOP REPLICA | SLAVE),
C(stopreplica) (STOP REPLICA), C(resetprimary | resetmaster) (RESET PRIMARY | MASTER) - supported since community.mysql 0.1.0,
C(resetprimary) (RESET MASTER) - supported since community.mysql 0.1.0, C(resetreplica, resetslave) (RESET REPLICA | SLAVE),
C(resetreplica) (RESET REPLICA), C(resetreplicaall, resetslave) (RESET REPLICA | SLAVE ALL).
C(resetreplicaall) (RESET REPLICA ALL).
type: str type: str
choices: choices:
- changeprimary - changeprimary
- changereplication - changemaster
- getprimary - getprimary
- getmaster
- getreplica - getreplica
- getslave
- startreplica - startreplica
- startslave
- stopreplica - stopreplica
- stopslave
- resetprimary - resetprimary
- resetmaster
- resetreplica - resetreplica
- resetslave
- resetreplicaall - resetreplicaall
- resetslaveall
default: getreplica default: getreplica
primary_host: primary_host:
description: description:
@ -96,8 +100,8 @@ options:
if an encrypted connection can be established. if an encrypted connection can be established.
- For details, refer to - For details, refer to
L(MySQL encrypted replication documentation,https://dev.mysql.com/doc/refman/8.0/en/replication-solutions-encrypted-connections.html). L(MySQL encrypted replication documentation,https://dev.mysql.com/doc/refman/8.0/en/replication-solutions-encrypted-connections.html).
- The default is C(false).
type: bool type: bool
default: false
aliases: [master_ssl] aliases: [master_ssl]
primary_ssl_ca: primary_ssl_ca:
description: description:
@ -135,12 +139,6 @@ options:
L(MySQL encrypted replication documentation,https://dev.mysql.com/doc/refman/8.0/en/replication-solutions-encrypted-connections.html). L(MySQL encrypted replication documentation,https://dev.mysql.com/doc/refman/8.0/en/replication-solutions-encrypted-connections.html).
type: str type: str
aliases: [master_ssl_cipher] aliases: [master_ssl_cipher]
primary_ssl_verify_server_cert:
description:
- Same as mysql variable.
type: bool
default: false
version_added: '3.5.0'
primary_auto_position: primary_auto_position:
description: description:
- Whether the host uses GTID based replication or not. - Whether the host uses GTID based replication or not.
@ -155,7 +153,9 @@ options:
- To find information about available values see - To find information about available values see
U(https://mariadb.com/kb/en/library/change-master-to/#master_use_gtid). U(https://mariadb.com/kb/en/library/change-master-to/#master_use_gtid).
- Available since MariaDB 10.0.2. - Available since MariaDB 10.0.2.
choices: [current_pos, replica_pos, disabled] - C(replica_pos) has been introduced in MariaDB 10.5.1 and
it is an alias for C(slave_pos).
choices: [current_pos, replica_pos, slave_pos, disabled]
type: str type: str
version_added: '0.1.0' version_added: '0.1.0'
aliases: [master_use_gtid] aliases: [master_use_gtid]
@ -192,8 +192,7 @@ options:
version_added: '0.1.0' version_added: '0.1.0'
notes: notes:
- Compatible with MariaDB or MySQL. - If an empty value for the parameter of string type is needed, use an empty string.
- If an empty value for the parameter of string type is needed, use an empty string.
attributes: attributes:
check_mode: check_mode:
@ -234,13 +233,6 @@ EXAMPLES = r'''
primary_log_file: mysql-bin.000009 primary_log_file: mysql-bin.000009
primary_log_pos: 4578 primary_log_pos: 4578
- name: Change replication source to replica server 192.0.2.1 and use binary log 'mysql-bin.000009' with position 4578
community.mysql.mysql_replication:
mode: changereplication
primary_host: 192.0.2.1
primary_log_file: mysql-bin.000009
primary_log_pos: 4578
- name: Check replica status using port 3308 - name: Check replica status using port 3308
community.mysql.mysql_replication: community.mysql.mysql_replication:
mode: getreplica mode: getreplica
@ -284,6 +276,7 @@ EXAMPLES = r'''
community.mysql.mysql_replication: community.mysql.mysql_replication:
mode: changeprimary mode: changeprimary
fail_on_error: true fail_on_error: true
''' '''
RETURN = r''' RETURN = r'''
@ -299,12 +292,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.command_resolver import (
CommandResolver
)
from ansible_collections.community.mysql.plugins.module_utils.mysql import ( from ansible_collections.community.mysql.plugins.module_utils.mysql import (
get_server_version,
get_server_implementation,
mysql_connect, mysql_connect,
mysql_driver, mysql_driver,
mysql_driver_fail_msg, mysql_driver_fail_msg,
@ -315,10 +303,11 @@ from ansible.module_utils._text import to_native
executed_queries = [] executed_queries = []
def get_primary_status(cursor, command_resolver): def get_primary_status(cursor):
query = command_resolver.resolve_command("SHOW MASTER STATUS") # TODO: when it's available to change on MySQL's side,
cursor.execute(query) # change MASTER to PRIMARY using the approach from
# get_replica_status() function. Same for other functions.
cursor.execute("SHOW MASTER STATUS")
primarystatus = cursor.fetchone() primarystatus = cursor.fetchone()
return primarystatus return primarystatus
@ -403,8 +392,8 @@ def reset_replica_all(module, cursor, connection_name='', channel='', fail_on_er
return reset return reset
def reset_primary(module, cursor, command_resolver, fail_on_error=False): def reset_primary(module, cursor, fail_on_error=False):
query = command_resolver.resolve_command('RESET MASTER') query = 'RESET MASTER'
try: try:
executed_queries.append(query) executed_queries.append(query)
cursor.execute(query) cursor.execute(query)
@ -413,7 +402,7 @@ def reset_primary(module, cursor, command_resolver, fail_on_error=False):
reset = False reset = False
except Exception as e: except Exception as e:
if fail_on_error: if fail_on_error:
module.fail_json(msg="%s failed: %s" % (command_resolver.resolve_command('RESET MASTER'), to_native(e))) module.fail_json(msg="RESET MASTER failed: %s" % to_native(e))
reset = False reset = False
return reset return reset
@ -440,22 +429,11 @@ def start_replica(module, cursor, connection_name='', channel='', fail_on_error=
return started return started
def changeprimary(cursor, command_resolver, chm, connection_name='', channel=''): def changeprimary(cursor, chm, connection_name='', channel=''):
query_head = command_resolver.resolve_command("CHANGE MASTER")
if connection_name: if connection_name:
query = "%s '%s' TO %s" % (query_head, connection_name, ','.join(chm)) query = "CHANGE MASTER '%s' TO %s" % (connection_name, ','.join(chm))
else: else:
query = '%s TO %s' % (query_head, ','.join(chm)) query = 'CHANGE MASTER TO %s' % ','.join(chm)
if channel:
query += " FOR CHANNEL '%s'" % channel
executed_queries.append(query)
cursor.execute(query)
def changereplication(cursor, chm, channel=''):
query = 'CHANGE REPLICATION SOURCE TO %s' % ','.join(chm)
if channel: if channel:
query += " FOR CHANNEL '%s'" % channel query += " FOR CHANNEL '%s'" % channel
@ -468,15 +446,14 @@ def main():
argument_spec = mysql_common_argument_spec() argument_spec = mysql_common_argument_spec()
argument_spec.update( argument_spec.update(
mode=dict(type='str', default='getreplica', choices=[ mode=dict(type='str', default='getreplica', choices=[
'getprimary', 'getprimary', 'getmaster',
'getreplica', 'getreplica', 'getslave',
'changeprimary', 'changeprimary', 'changemaster',
'stopreplica', 'stopreplica', 'stopslave',
'startreplica', 'startreplica', 'startslave',
'resetprimary', 'resetprimary', 'resetmaster',
'resetreplica', 'resetreplica', 'resetslave',
'resetreplicaall', 'resetreplicaall', 'resetslaveall']),
'changereplication']),
primary_auto_position=dict(type='bool', default=False, aliases=['master_auto_position']), primary_auto_position=dict(type='bool', default=False, aliases=['master_auto_position']),
primary_host=dict(type='str', aliases=['master_host']), primary_host=dict(type='str', aliases=['master_host']),
primary_user=dict(type='str', aliases=['master_user']), primary_user=dict(type='str', aliases=['master_user']),
@ -487,15 +464,14 @@ def main():
primary_log_pos=dict(type='int', aliases=['master_log_pos']), primary_log_pos=dict(type='int', aliases=['master_log_pos']),
relay_log_file=dict(type='str'), relay_log_file=dict(type='str'),
relay_log_pos=dict(type='int'), relay_log_pos=dict(type='int'),
primary_ssl=dict(type='bool', aliases=['master_ssl']), primary_ssl=dict(type='bool', default=False, aliases=['master_ssl']),
primary_ssl_ca=dict(type='str', aliases=['master_ssl_ca']), primary_ssl_ca=dict(type='str', aliases=['master_ssl_ca']),
primary_ssl_capath=dict(type='str', aliases=['master_ssl_capath']), primary_ssl_capath=dict(type='str', aliases=['master_ssl_capath']),
primary_ssl_cert=dict(type='str', aliases=['master_ssl_cert']), primary_ssl_cert=dict(type='str', aliases=['master_ssl_cert']),
primary_ssl_key=dict(type='str', no_log=False, aliases=['master_ssl_key']), primary_ssl_key=dict(type='str', no_log=False, aliases=['master_ssl_key']),
primary_ssl_cipher=dict(type='str', aliases=['master_ssl_cipher']), primary_ssl_cipher=dict(type='str', aliases=['master_ssl_cipher']),
primary_ssl_verify_server_cert=dict(type='bool', default=False),
primary_use_gtid=dict(type='str', choices=[ primary_use_gtid=dict(type='str', choices=[
'current_pos', 'replica_pos', 'disabled'], aliases=['master_use_gtid']), 'current_pos', 'replica_pos', 'slave_pos', 'disabled'], aliases=['master_use_gtid']),
primary_delay=dict(type='int', aliases=['master_delay']), primary_delay=dict(type='int', aliases=['master_delay']),
connection_name=dict(type='str'), connection_name=dict(type='str'),
channel=dict(type='str'), channel=dict(type='str'),
@ -523,7 +499,6 @@ def main():
primary_ssl_cert = module.params["primary_ssl_cert"] primary_ssl_cert = module.params["primary_ssl_cert"]
primary_ssl_key = module.params["primary_ssl_key"] primary_ssl_key = module.params["primary_ssl_key"]
primary_ssl_cipher = module.params["primary_ssl_cipher"] primary_ssl_cipher = module.params["primary_ssl_cipher"]
primary_ssl_verify_server_cert = module.params["primary_ssl_verify_server_cert"]
primary_auto_position = module.params["primary_auto_position"] primary_auto_position = module.params["primary_auto_position"]
ssl_cert = module.params["client_cert"] ssl_cert = module.params["client_cert"]
ssl_key = module.params["client_key"] ssl_key = module.params["client_key"]
@ -560,11 +535,8 @@ def main():
else: else:
module.fail_json(msg="unable to find %s. Exception message: %s" % (config_file, to_native(e))) module.fail_json(msg="unable to find %s. Exception message: %s" % (config_file, to_native(e)))
server_version = get_server_version(cursor)
server_implementation = get_server_implementation(cursor)
command_resolver = CommandResolver(server_implementation, server_version)
cursor.execute("SELECT VERSION()") cursor.execute("SELECT VERSION()")
if server_implementation == 'mariadb': if 'mariadb' in cursor.fetchone()["VERSION()"].lower():
from ansible_collections.community.mysql.plugins.module_utils.implementations.mariadb import replication as impl from ansible_collections.community.mysql.plugins.module_utils.implementations.mariadb import replication as impl
else: else:
from ansible_collections.community.mysql.plugins.module_utils.implementations.mysql import replication as impl from ansible_collections.community.mysql.plugins.module_utils.implementations.mysql import replication as impl
@ -573,168 +545,158 @@ def main():
# "REPLICA" must be used instead of "SLAVE" # "REPLICA" must be used instead of "SLAVE"
if impl.uses_replica_terminology(cursor): if impl.uses_replica_terminology(cursor):
replica_term = 'REPLICA' replica_term = 'REPLICA'
if primary_use_gtid == 'slave_pos':
module.deprecate('primary_use_gtid | master_use_gtid "slave_pos" value is '
'deprecated, use "replica_pos" instead.',
version='3.0.0', collection_name='community.mysql')
primary_use_gtid = 'replica_pos'
else: else:
replica_term = 'SLAVE' replica_term = 'SLAVE'
if primary_use_gtid == 'replica_pos': if primary_use_gtid == 'replica_pos':
primary_use_gtid = 'slave_pos' primary_use_gtid = 'slave_pos'
if mode == 'getprimary': if mode in ('getprimary', 'getmaster'):
status = get_primary_status(cursor, command_resolver) if mode == 'getmaster':
if status and "File" in status and "Position" in status: module.deprecate('"getmaster" option is deprecated, use "getprimary" instead.',
version='3.0.0', collection_name='community.mysql')
status = get_primary_status(cursor)
if not isinstance(status, dict):
# TODO: change the word master to primary in 3.0.0
status = dict(Is_Master=False, Is_Primary=False,
msg="Server is not configured as mysql master")
else:
status['Is_Master'] = True
status['Is_Primary'] = True status['Is_Primary'] = True
else:
status = dict( module.deprecate('"Is_Master" and "Is_Slave" return values are deprecated '
Is_Primary=False, 'and will be replaced with "Is_Primary" and "Is_Replica" '
msg="Server is not configured as mysql primary. " 'in the next major release. Use "Is_Primary" and "Is_Replica" instead.',
"Meaning: Binary logs are disabled") version='3.0.0', collection_name='community.mysql')
module.exit_json(queries=executed_queries, **status) module.exit_json(queries=executed_queries, **status)
elif mode == "getreplica": elif mode in ("getreplica", "getslave"):
if mode == "getslave":
module.deprecate('"getslave" option is deprecated, use "getreplica" instead.',
version='3.0.0', collection_name='community.mysql')
status = get_replica_status(cursor, connection_name, channel, replica_term) status = get_replica_status(cursor, connection_name, channel, replica_term)
# MySQL 8.0 uses Replica_... if not isinstance(status, dict):
# MariaDB 10.6 uses Slave_... status = dict(Is_Slave=False, Is_Replica=False, msg="Server is not configured as mysql replica")
if status and (
"Slave_IO_Running" in status or
"Replica_IO_Running" in status):
status['Is_Replica'] = True
else: else:
status = dict(Is_Replica=False, msg="Server is not configured as mysql replica") status['Is_Slave'] = True
status['Is_Replica'] = True
module.deprecate('"Is_Master" and "Is_Slave" return values are deprecated '
'and will be replaced with "Is_Primary" and "Is_Replica" '
'in the next major release. Use "Is_Primary" and "Is_Replica" instead.',
version='3.0.0', collection_name='community.mysql')
module.exit_json(queries=executed_queries, **status) module.exit_json(queries=executed_queries, **status)
elif mode == 'changeprimary': elif mode in ('changeprimary', 'changemaster'):
if mode == 'changemaster':
module.deprecate('"changemaster" option is deprecated, use "changeprimary" instead.',
version='3.0.0', collection_name='community.mysql')
chm = [] chm = []
result = {} result = {}
if primary_host is not None: if primary_host is not None:
chm.append("%s='%s'" % (command_resolver.resolve_command('MASTER_HOST'), primary_host)) chm.append("MASTER_HOST='%s'" % primary_host)
if primary_user is not None: if primary_user is not None:
chm.append("%s='%s'" % (command_resolver.resolve_command('MASTER_USER'), primary_user)) chm.append("MASTER_USER='%s'" % primary_user)
if primary_password is not None: if primary_password is not None:
chm.append("%s='%s'" % (command_resolver.resolve_command('MASTER_PASSWORD'), primary_password)) chm.append("MASTER_PASSWORD='%s'" % primary_password)
if primary_port is not None: if primary_port is not None:
chm.append("%s=%s" % (command_resolver.resolve_command('MASTER_PORT'), primary_port)) chm.append("MASTER_PORT=%s" % primary_port)
if primary_connect_retry is not None: if primary_connect_retry is not None:
chm.append("%s=%s" % (command_resolver.resolve_command('MASTER_CONNECT_RETRY'), primary_connect_retry)) chm.append("MASTER_CONNECT_RETRY=%s" % primary_connect_retry)
if primary_log_file is not None: if primary_log_file is not None:
chm.append("%s='%s'" % (command_resolver.resolve_command('MASTER_LOG_FILE'), primary_log_file)) chm.append("MASTER_LOG_FILE='%s'" % primary_log_file)
if primary_log_pos is not None: if primary_log_pos is not None:
chm.append("%s=%s" % (command_resolver.resolve_command('MASTER_LOG_POS'), primary_log_pos)) chm.append("MASTER_LOG_POS=%s" % primary_log_pos)
if primary_delay is not None: if primary_delay is not None:
chm.append("%s=%s" % (command_resolver.resolve_command('MASTER_DELAY'), primary_delay)) chm.append("MASTER_DELAY=%s" % primary_delay)
if relay_log_file is not None: if relay_log_file is not None:
chm.append("RELAY_LOG_FILE='%s'" % relay_log_file) chm.append("RELAY_LOG_FILE='%s'" % relay_log_file)
if relay_log_pos is not None: if relay_log_pos is not None:
chm.append("RELAY_LOG_POS=%s" % relay_log_pos) chm.append("RELAY_LOG_POS=%s" % relay_log_pos)
if primary_ssl is not None: if primary_ssl:
if primary_ssl: chm.append("MASTER_SSL=1")
chm.append("%s=1" % command_resolver.resolve_command('MASTER_SSL'))
else:
chm.append("%s=0" % command_resolver.resolve_command('MASTER_SSL'))
if primary_ssl_ca is not None: if primary_ssl_ca is not None:
chm.append("%s='%s'" % (command_resolver.resolve_command('MASTER_SSL_CA'), primary_ssl_ca)) chm.append("MASTER_SSL_CA='%s'" % primary_ssl_ca)
if primary_ssl_capath is not None: if primary_ssl_capath is not None:
chm.append("%s='%s'" % (command_resolver.resolve_command('MASTER_SSL_CAPATH'), primary_ssl_capath)) chm.append("MASTER_SSL_CAPATH='%s'" % primary_ssl_capath)
if primary_ssl_cert is not None: if primary_ssl_cert is not None:
chm.append("%s='%s'" % (command_resolver.resolve_command('MASTER_SSL_CERT'), primary_ssl_cert)) chm.append("MASTER_SSL_CERT='%s'" % primary_ssl_cert)
if primary_ssl_key is not None: if primary_ssl_key is not None:
chm.append("%s='%s'" % (command_resolver.resolve_command('MASTER_SSL_KEY'), primary_ssl_key)) chm.append("MASTER_SSL_KEY='%s'" % primary_ssl_key)
if primary_ssl_cipher is not None: if primary_ssl_cipher is not None:
chm.append("%s='%s'" % (command_resolver.resolve_command('MASTER_SSL_CIPHER'), primary_ssl_cipher)) chm.append("MASTER_SSL_CIPHER='%s'" % primary_ssl_cipher)
if primary_ssl_verify_server_cert:
chm.append("%s=1" % command_resolver.resolve_command('MASTER_SSL_VERIFY_SERVER_CERT'))
if primary_auto_position: if primary_auto_position:
chm.append("%s=1" % command_resolver.resolve_command('MASTER_AUTO_POSITION')) chm.append("MASTER_AUTO_POSITION=1")
if primary_use_gtid is not None: if primary_use_gtid is not None:
chm.append("MASTER_USE_GTID=%s" % primary_use_gtid) # MariaDB only chm.append("MASTER_USE_GTID=%s" % primary_use_gtid)
try: try:
changeprimary(cursor, command_resolver, chm, connection_name, channel) changeprimary(cursor, chm, connection_name, channel)
except mysql_driver.Warning as e: except mysql_driver.Warning as e:
result['warning'] = to_native(e) result['warning'] = to_native(e)
except Exception as e: except Exception as e:
module.fail_json(msg='%s. Query == %s TO %s' % (to_native(e), command_resolver.resolve_command('CHANGE MASTER'), chm)) module.fail_json(msg='%s. Query == CHANGE MASTER TO %s' % (to_native(e), chm))
result['changed'] = True result['changed'] = True
module.exit_json(queries=executed_queries, **result) module.exit_json(queries=executed_queries, **result)
elif mode == "startreplica": elif mode in ("startreplica", "startslave"):
if mode == "startslave":
module.deprecate('"startslave" option is deprecated, use "startreplica" instead.',
version='3.0.0', collection_name='community.mysql')
started = start_replica(module, cursor, connection_name, channel, fail_on_error, replica_term) started = start_replica(module, cursor, connection_name, channel, fail_on_error, replica_term)
if started is True: if started is True:
module.exit_json(msg="Replica started ", changed=True, queries=executed_queries) module.exit_json(msg="Replica started ", changed=True, queries=executed_queries)
else: else:
module.exit_json(msg="Replica already started (Or cannot be started)", changed=False, queries=executed_queries) module.exit_json(msg="Replica already started (Or cannot be started)", changed=False, queries=executed_queries)
elif mode == "stopreplica": elif mode in ("stopreplica", "stopslave"):
if mode == "stopslave":
module.deprecate('"stopslave" option is deprecated, use "stopreplica" instead.',
version='3.0.0', collection_name='community.mysql')
stopped = stop_replica(module, cursor, connection_name, channel, fail_on_error, replica_term) stopped = stop_replica(module, cursor, connection_name, channel, fail_on_error, replica_term)
if stopped is True: if stopped is True:
module.exit_json(msg="Replica stopped", changed=True, queries=executed_queries) module.exit_json(msg="Replica stopped", changed=True, queries=executed_queries)
else: else:
module.exit_json(msg="Replica already stopped", changed=False, queries=executed_queries) module.exit_json(msg="Replica already stopped", changed=False, queries=executed_queries)
elif mode == 'resetprimary': elif mode in ('resetprimary', 'resetmaster'):
reset = reset_primary(module, cursor, command_resolver, fail_on_error) if mode == 'resetmaster':
module.deprecate('"resetmaster" option is deprecated, use "resetprimary" instead.',
version='3.0.0', collection_name='community.mysql')
reset = reset_primary(module, cursor, fail_on_error)
if reset is True: if reset is True:
module.exit_json(msg="Primary reset", changed=True, queries=executed_queries) # TODO: Change "Master" to "Primary" in release 3.0.0
module.exit_json(msg="Master reset", changed=True, queries=executed_queries)
else: else:
module.exit_json(msg="Primary already reset", changed=False, queries=executed_queries) # TODO: Change "Master" to "Primary" in release 3.0.0
elif mode == "resetreplica": module.exit_json(msg="Master already reset", changed=False, queries=executed_queries)
elif mode in ("resetreplica", "resetslave"):
if mode == "resetslave":
module.deprecate('"resetslave" option is deprecated, use "resetreplica" instead.',
version='3.0.0', collection_name='community.mysql')
reset = reset_replica(module, cursor, connection_name, channel, fail_on_error, replica_term) reset = reset_replica(module, cursor, connection_name, channel, fail_on_error, replica_term)
if reset is True: if reset is True:
module.exit_json(msg="Replica reset", changed=True, queries=executed_queries) module.exit_json(msg="Replica reset", changed=True, queries=executed_queries)
else: else:
module.exit_json(msg="Replica already reset", changed=False, queries=executed_queries) module.exit_json(msg="Replica already reset", changed=False, queries=executed_queries)
elif mode == "resetreplicaall": elif mode in ("resetreplicaall", "resetslaveall"):
if mode == "resetslaveall":
module.deprecate('"resetslaveall" option is deprecated, use "resetreplicaall" instead.',
version='3.0.0', collection_name='community.mysql')
reset = reset_replica_all(module, cursor, connection_name, channel, fail_on_error, replica_term) reset = reset_replica_all(module, cursor, connection_name, channel, fail_on_error, replica_term)
if reset is True: if reset is True:
module.exit_json(msg="Replica reset", changed=True, queries=executed_queries) module.exit_json(msg="Replica reset", changed=True, queries=executed_queries)
else: else:
module.exit_json(msg="Replica already reset", changed=False, queries=executed_queries) module.exit_json(msg="Replica already reset", changed=False, queries=executed_queries)
elif mode == 'changereplication':
chm = []
result = {}
if primary_host is not None:
chm.append("SOURCE_HOST='%s'" % primary_host)
if primary_user is not None:
chm.append("SOURCE_USER='%s'" % primary_user)
if primary_password is not None:
chm.append("SOURCE_PASSWORD='%s'" % primary_password)
if primary_port is not None:
chm.append("SOURCE_PORT=%s" % primary_port)
if primary_connect_retry is not None:
chm.append("SOURCE_CONNECT_RETRY=%s" % primary_connect_retry)
if primary_log_file is not None:
chm.append("SOURCE_LOG_FILE='%s'" % primary_log_file)
if primary_log_pos is not None:
chm.append("SOURCE_LOG_POS=%s" % primary_log_pos)
if primary_delay is not None:
chm.append("SOURCE_DELAY=%s" % primary_delay)
if relay_log_file is not None:
chm.append("RELAY_LOG_FILE='%s'" % relay_log_file)
if relay_log_pos is not None:
chm.append("RELAY_LOG_POS=%s" % relay_log_pos)
if primary_ssl is not None:
if primary_ssl:
chm.append("SOURCE_SSL=1")
else:
chm.append("SOURCE_SSL=0")
if primary_ssl_ca is not None:
chm.append("SOURCE_SSL_CA='%s'" % primary_ssl_ca)
if primary_ssl_capath is not None:
chm.append("SOURCE_SSL_CAPATH='%s'" % primary_ssl_capath)
if primary_ssl_cert is not None:
chm.append("SOURCE_SSL_CERT='%s'" % primary_ssl_cert)
if primary_ssl_key is not None:
chm.append("SOURCE_SSL_KEY='%s'" % primary_ssl_key)
if primary_ssl_cipher is not None:
chm.append("SOURCE_SSL_CIPHER='%s'" % primary_ssl_cipher)
if primary_ssl_verify_server_cert:
chm.append("SOURCE_SSL_VERIFY_SERVER_CERT=1")
if primary_auto_position:
chm.append("SOURCE_AUTO_POSITION=1")
try:
changereplication(cursor, chm, channel)
except mysql_driver.Warning as e:
result['warning'] = to_native(e)
except Exception as e:
module.fail_json(msg='%s. Query == CHANGE REPLICATION SOURCE TO %s' % (to_native(e), chm))
result['changed'] = True
module.exit_json(queries=executed_queries, **result)
warnings.simplefilter("ignore") warnings.simplefilter("ignore")

View file

@ -1,7 +1,7 @@
#!/usr/bin/python #!/usr/bin/python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright: (c) 2021, Andrew Klychkov <andrew.a.klychkov@gmail.com> # Copyright: (c) 2021, Andrew Klychkov <aaklychkov@mail.ru>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # 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 from __future__ import absolute_import, division, print_function
@ -11,10 +11,10 @@ DOCUMENTATION = r'''
--- ---
module: mysql_role module: mysql_role
short_description: Adds, removes, or updates a MySQL or MariaDB role short_description: Adds, removes, or updates a MySQL role
description: description:
- Adds, removes, or updates a MySQL or MariaDB role. - Adds, removes, or updates a MySQL role.
- Roles are supported since MySQL 8.0.0 and MariaDB 10.0.5. - Roles are supported since MySQL 8.0.0 and MariaDB 10.0.5.
version_added: '2.2.0' version_added: '2.2.0'
@ -51,16 +51,7 @@ options:
append_privs: append_privs:
description: description:
- Append the privileges defined by the I(priv) option to the existing ones - Append the privileges defined by the I(priv) option to the existing ones
for this role instead of overwriting them. Mutually exclusive with I(subtract_privs). for this role instead of overwriting them.
type: bool
default: false
subtract_privs:
description:
- Revoke the privileges defined by the I(priv) option and keep other existing privileges.
If set, invalid privileges in I(priv) are ignored.
Mutually exclusive with I(append_privs).
version_added: '3.2.0'
type: bool type: bool
default: false default: false
@ -114,25 +105,7 @@ options:
type: bool type: bool
default: false default: false
members_must_exist:
description:
- When C(yes), the module fails if any user in I(members) does not exist.
- When C(no), users in I(members) which don't exist are simply skipped.
type: bool
default: true
column_case_sensitive:
description:
- The default is C(false).
- When C(true), the module will not uppercase the field in the privileges.
- When C(false), the field names will be upper-cased. This was the default before this
feature was introduced but since MySQL/MariaDB is case sensitive you should set this
to C(true) in most cases.
type: bool
version_added: '3.8.0'
notes: notes:
- Roles are supported since MySQL 8.0.0 and MariaDB 10.0.5.
- Pay attention that the module runs C(SET DEFAULT ROLE ALL TO) - Pay attention that the module runs C(SET DEFAULT ROLE ALL TO)
all the I(members) passed by default when the state has changed. all the I(members) passed by default when the state has changed.
If you want to avoid this behavior, set I(set_default_role_all) to C(no). If you want to avoid this behavior, set I(set_default_role_all) to C(no).
@ -150,8 +123,6 @@ seealso:
author: author:
- Andrew Klychkov (@Andersson007) - Andrew Klychkov (@Andersson007)
- Felix Hamme (@betanummeric) - Felix Hamme (@betanummeric)
- kmarse (@kmarse)
- Laurent Indermühle (@laurent-indermuehle)
extends_documentation_fragment: extends_documentation_fragment:
- community.mysql.mysql - community.mysql.mysql
@ -269,34 +240,6 @@ EXAMPLES = r'''
name: business name: business
members: members:
- marketing - marketing
- name: Ensure the role foo does not have the DELETE privilege
community.mysql.mysql_role:
state: present
name: foo
subtract_privs: true
priv:
'db1.*': DELETE
- name: Add some members to a role and skip not-existent users
community.mysql.mysql_role:
state: present
name: foo
append_members: true
members_must_exist: false
members:
- 'existing_user@localhost'
- 'not_existing_user@localhost'
- name: Detach some members from a role and ignore not-existent users
community.mysql.mysql_role:
state: present
name: foo
detach_members: true
members_must_exist: false
members:
- 'existing_user@localhost'
- 'not_existing_user@localhost'
''' '''
RETURN = '''#''' RETURN = '''#'''
@ -310,10 +253,11 @@ from ansible_collections.community.mysql.plugins.module_utils.mysql import (
) )
from ansible_collections.community.mysql.plugins.module_utils.user import ( from ansible_collections.community.mysql.plugins.module_utils.user import (
convert_priv_dict_to_str, convert_priv_dict_to_str,
get_user_implementation, get_impl,
get_mode, get_mode,
user_mod, user_mod,
privileges_grant, privileges_grant,
get_valid_privs,
privileges_unpack, privileges_unpack,
) )
from ansible.module_utils._text import to_native from ansible.module_utils._text import to_native
@ -428,11 +372,6 @@ class DbServer():
msg = 'User / role `%s` with host `%s` does not exist' % (user[0], user[1]) msg = 'User / role `%s` with host `%s` does not exist' % (user[0], user[1])
self.module.fail_json(msg=msg) self.module.fail_json(msg=msg)
def filter_existing_users(self, users):
for user in users:
if user in self.users:
yield user
def __get_users(self): def __get_users(self):
"""Get users. """Get users.
@ -890,9 +829,9 @@ class Role():
return True return True
def update(self, users, privs, check_mode=False, def update(self, users, privs, check_mode=False,
append_privs=False, subtract_privs=False, append_privs=False, append_members=False,
append_members=False, detach_members=False, detach_members=False, admin=False,
admin=False, set_default_role_all=True): set_default_role_all=True):
"""Update a role. """Update a role.
Update a role if needed. Update a role if needed.
@ -906,8 +845,6 @@ class Role():
check_mode (bool): If True, just checks and does nothing. check_mode (bool): If True, just checks and does nothing.
append_privs (bool): If True, adds new privileges passed through privs append_privs (bool): If True, adds new privileges passed through privs
not touching current privileges. not touching current privileges.
subtract_privs (bool): If True, revoke the privileges passed through privs
not touching other existing privileges.
append_members (bool): If True, adds new members passed through users append_members (bool): If True, adds new members passed through users
not touching current members. not touching current members.
detach_members (bool): If True, removes members passed through users from a role. detach_members (bool): If True, removes members passed through users from a role.
@ -930,13 +867,10 @@ class Role():
set_default_role_all=set_default_role_all) set_default_role_all=set_default_role_all)
if privs: if privs:
result = user_mod(cursor=self.cursor, user=self.name, host=self.host, changed, msg = user_mod(self.cursor, self.name, self.host,
host_all=None, password=None, encrypted=None, plugin=None, None, None, None, None, None, None,
plugin_auth_string=None, plugin_hash_string=None, salt=None, privs, append_privs, None,
new_priv=privs, append_privs=append_privs, subtract_privs=subtract_privs, self.module, role=True, maria_role=self.is_mariadb)
attributes=None, tls_requires=None, module=self.module, password_expire=None,
password_expire_interval=None, role=True, maria_role=self.is_mariadb)
changed = result['changed']
if admin: if admin:
self.role_impl.set_admin(admin) self.role_impl.set_admin(admin)
@ -966,14 +900,11 @@ def main():
admin=dict(type='str'), admin=dict(type='str'),
priv=dict(type='raw'), priv=dict(type='raw'),
append_privs=dict(type='bool', default=False), append_privs=dict(type='bool', default=False),
subtract_privs=dict(type='bool', default=False),
members=dict(type='list', elements='str'), members=dict(type='list', elements='str'),
append_members=dict(type='bool', default=False), append_members=dict(type='bool', default=False),
detach_members=dict(type='bool', default=False), detach_members=dict(type='bool', default=False),
check_implicit_admin=dict(type='bool', default=False), check_implicit_admin=dict(type='bool', default=False),
set_default_role_all=dict(type='bool', default=True), set_default_role_all=dict(type='bool', default=True),
members_must_exist=dict(type='bool', default=True),
column_case_sensitive=dict(type='bool', default=None), # TODO 4.0.0 add default=True
) )
module = AnsibleModule( module = AnsibleModule(
argument_spec=argument_spec, argument_spec=argument_spec,
@ -983,7 +914,6 @@ def main():
('admin', 'members'), ('admin', 'members'),
('admin', 'append_members'), ('admin', 'append_members'),
('admin', 'detach_members'), ('admin', 'detach_members'),
('append_privs', 'subtract_privs'),
), ),
) )
@ -997,7 +927,6 @@ def main():
connect_timeout = module.params['connect_timeout'] connect_timeout = module.params['connect_timeout']
config_file = module.params['config_file'] config_file = module.params['config_file']
append_privs = module.params['append_privs'] append_privs = module.params['append_privs']
subtract_privs = module.boolean(module.params['subtract_privs'])
members = module.params['members'] members = module.params['members']
append_members = module.params['append_members'] append_members = module.params['append_members']
detach_members = module.params['detach_members'] detach_members = module.params['detach_members']
@ -1007,8 +936,6 @@ def main():
check_hostname = module.params['check_hostname'] check_hostname = module.params['check_hostname']
db = '' db = ''
set_default_role_all = module.params['set_default_role_all'] set_default_role_all = module.params['set_default_role_all']
members_must_exist = module.params['members_must_exist']
column_case_sensitive = module.params['column_case_sensitive']
if priv and not isinstance(priv, (str, dict)): if priv and not isinstance(priv, (str, dict)):
msg = ('The "priv" parameter must be str or dict ' msg = ('The "priv" parameter must be str or dict '
@ -1021,13 +948,6 @@ def main():
if mysql_driver is None: if mysql_driver is None:
module.fail_json(msg=mysql_driver_fail_msg) module.fail_json(msg=mysql_driver_fail_msg)
# TODO Release 4.0.0 : Remove this test and variable assignation
if column_case_sensitive is None:
column_case_sensitive = False
module.warn("Option column_case_sensitive is not provided. "
"The default is now false, so the column's name will be uppercased. "
"The default will be changed to true in community.mysql 4.0.0.")
cursor = None cursor = None
try: try:
if check_implicit_admin: if check_implicit_admin:
@ -1035,8 +955,7 @@ def main():
cursor, db_conn = mysql_connect(module, 'root', '', config_file, cursor, db_conn = mysql_connect(module, 'root', '', config_file,
ssl_cert, ssl_key, ssl_ca, db, ssl_cert, ssl_key, ssl_ca, db,
connect_timeout=connect_timeout, connect_timeout=connect_timeout,
check_hostname=check_hostname, check_hostname=check_hostname)
autocommit=True)
except Exception: except Exception:
pass pass
@ -1044,8 +963,7 @@ def main():
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, config_file, ssl_cert, ssl_key,
ssl_ca, db, connect_timeout=connect_timeout, ssl_ca, db, connect_timeout=connect_timeout,
check_hostname=check_hostname, check_hostname=check_hostname)
autocommit=True)
except Exception as e: except Exception as e:
module.fail_json(msg='unable to connect to database, ' module.fail_json(msg='unable to connect to database, '
@ -1056,7 +974,7 @@ def main():
# Set defaults # Set defaults
changed = False changed = False
impl = get_user_implementation(cursor) get_impl(cursor)
if priv is not None: if priv is not None:
try: try:
@ -1065,7 +983,8 @@ def main():
module.fail_json(msg=to_native(e)) module.fail_json(msg=to_native(e))
try: try:
priv = privileges_unpack(priv, mode, column_case_sensitive, ensure_usage=not subtract_privs) valid_privs = get_valid_privs(cursor)
priv = privileges_unpack(priv, mode, valid_privs)
except Exception as e: except Exception as e:
module.fail_json(msg='Invalid privileges string: %s' % to_native(e)) module.fail_json(msg='Invalid privileges string: %s' % to_native(e))
@ -1086,10 +1005,7 @@ def main():
if members: if members:
members = normalize_users(module, members, server.is_mariadb()) members = normalize_users(module, members, server.is_mariadb())
if members_must_exist: server.check_users_in_db(members)
server.check_users_in_db(members)
else:
members = list(server.filter_existing_users(members))
# Main job starts here # Main job starts here
role = Role(module, cursor, name, server) role = Role(module, cursor, name, server)
@ -1097,15 +1013,13 @@ def main():
try: try:
if state == 'present': if state == 'present':
if not role.exists: if not role.exists:
if subtract_privs:
priv = None # avoid granting unwanted privileges
if detach_members: if detach_members:
members = None # avoid adding unwanted members members = None # avoid adding unwanted members
changed = role.add(members, priv, module.check_mode, admin, changed = role.add(members, priv, module.check_mode, admin,
set_default_role_all) set_default_role_all)
else: else:
changed = role.update(members, priv, module.check_mode, append_privs, subtract_privs, changed = role.update(members, priv, module.check_mode, append_privs,
append_members, detach_members, admin, append_members, detach_members, admin,
set_default_role_all) set_default_role_all)

View file

@ -11,16 +11,15 @@ __metaclass__ = type
DOCUMENTATION = r''' DOCUMENTATION = r'''
--- ---
module: mysql_user module: mysql_user
short_description: Adds or removes a user from a MySQL or MariaDB database short_description: Adds or removes a user from a MySQL database
description: description:
- Adds or removes a user from a MySQL or MariaDB database. - Adds or removes a user from a MySQL database.
options: options:
name: name:
description: description:
- Name of the user (role) to add or remove. - Name of the user (role) to add or remove.
type: str type: str
required: true required: true
aliases: ['user']
password: password:
description: description:
- Set the user's password. Only for C(mysql_native_password) authentication. - Set the user's password. Only for C(mysql_native_password) authentication.
@ -46,7 +45,6 @@ options:
priv: priv:
description: description:
- "MySQL privileges string in the format: C(db.table:priv1,priv2)." - "MySQL privileges string in the format: C(db.table:priv1,priv2)."
- Additionally, there must be no spaces between the table and the privilege as this will yield a non-idempotent check mode.
- "Multiple privileges can be specified by separating each one using - "Multiple privileges can be specified by separating each one using
a forward slash: C(db.table1:priv/db.table2:priv)." a forward slash: C(db.table1:priv/db.table2:priv)."
- The format is based on MySQL C(GRANT) statement. - The format is based on MySQL C(GRANT) statement.
@ -66,15 +64,7 @@ options:
append_privs: append_privs:
description: description:
- Append the privileges defined by priv to the existing ones for this - Append the privileges defined by priv to the existing ones for this
user instead of overwriting existing ones. Mutually exclusive with I(subtract_privs). user instead of overwriting existing ones.
type: bool
default: false
subtract_privs:
description:
- Revoke the privileges defined by the I(priv) option and keep other existing privileges.
If set, invalid privileges in I(priv) are ignored.
Mutually exclusive with I(append_privs).
version_added: '3.2.0'
type: bool type: bool
default: false default: false
tls_requires: tls_requires:
@ -90,19 +80,6 @@ options:
- Whether binary logging should be enabled or disabled for the connection. - Whether binary logging should be enabled or disabled for the connection.
type: bool type: bool
default: true default: true
force_context:
description:
- Sets the С(mysql) system database as context for the executed statements (it will be used
as a database to connect to). Useful if you use binlog / replication filters in MySQL as
per default the statements can not be caught by a binlog / replication filter, they require
a database to be set to work, otherwise the replication can break down.
- See U(https://dev.mysql.com/doc/refman/8.0/en/replication-options-binary-log.html#option_mysqld_binlog-ignore-db)
for a description on how binlog filters work (filtering on the primary).
- See U(https://dev.mysql.com/doc/refman/8.0/en/replication-options-replica.html#option_mysqld_replicate-ignore-db)
for a description on how replication filters work (filtering on the replica).
type: bool
default: false
version_added: '3.1.0'
state: state:
description: description:
- Whether the user should exist. - Whether the user should exist.
@ -119,13 +96,9 @@ options:
update_password: update_password:
description: description:
- C(always) will update passwords if they differ. This affects I(password) and the combination of I(plugin), I(plugin_hash_string), I(plugin_auth_string). - C(always) will update passwords if they differ. This affects I(password) and the combination of I(plugin), I(plugin_hash_string), I(plugin_auth_string).
- C(on_create) will only set the password or the combination of I(plugin), I(plugin_hash_string), I(plugin_auth_string) for newly created users. - C(on_create) will only set the password or the combination of plugin, plugin_hash_string, plugin_auth_string for newly created users.
- "C(on_new_username) works like C(on_create), but it tries to reuse an existing password: If one different user
with the same username exists, or multiple different users with the same username and equal C(plugin) and
C(authentication_string) attribute, the existing C(plugin) and C(authentication_string) are used for the
new user instead of the I(password), I(plugin), I(plugin_hash_string) or I(plugin_auth_string) argument."
type: str type: str
choices: [ always, on_create, on_new_username ] choices: [ always, on_create ]
default: always default: always
plugin: plugin:
description: description:
@ -140,74 +113,18 @@ options:
plugin_auth_string: plugin_auth_string:
description: description:
- User's plugin auth_string (``CREATE USER user IDENTIFIED WITH plugin BY plugin_auth_string``). - User's plugin auth_string (``CREATE USER user IDENTIFIED WITH plugin BY plugin_auth_string``).
- If I(plugin) is ``pam`` (MariaDB) or ``auth_pam`` (MySQL) an optional I(plugin_auth_string) can be used to choose a specific PAM service.
- You need to define a I(salt) to have idempotence on password change with ``caching_sha2_password`` and ``sha256_password`` plugins.
type: str type: str
version_added: '0.1.0' version_added: '0.1.0'
salt:
description:
- Salt used to generate password hash from I(plugin_auth_string).
- Salt length must be 20 characters.
- Salt only support ``caching_sha2_password`` or ``sha256_password`` authentication I(plugin).
type: str
version_added: '3.10.0'
resource_limits: resource_limits:
description: description:
- Limit the user for certain server resources. Provided since MySQL 5.6 / MariaDB 10.2. - Limit the user for certain server resources. Provided since MySQL 5.6 / MariaDB 10.2.
- "Available options are C(MAX_QUERIES_PER_HOUR: num), C(MAX_UPDATES_PER_HOUR: num), - "Available options are C(MAX_QUERIES_PER_HOUR: num), C(MAX_UPDATES_PER_HOUR: num),
C(MAX_CONNECTIONS_PER_HOUR: num), C(MAX_USER_CONNECTIONS: num), C(MAX_STATEMENT_TIME: num) (supported only for MariaDB since collection version 3.7.0)." C(MAX_CONNECTIONS_PER_HOUR: num), C(MAX_USER_CONNECTIONS: num)."
- Used when I(state=present), ignored otherwise. - Used when I(state=present), ignored otherwise.
type: dict type: dict
version_added: '0.1.0' version_added: '0.1.0'
session_vars:
description:
- "Dictionary of session variables in form of C(variable: value) to set at the beginning of module execution."
- Cannot be used to set global variables, use the M(community.mysql.mysql_variables) module instead.
type: dict
version_added: '3.6.0'
password_expire:
description:
- C(never) - I(password) will never expire.
- C(default) - I(password) is defined using global system variable I(default_password_lifetime) setting.
- C(interval) - I(password) will expire in days which is defined in I(password_expire_interval).
- C(now) - I(password) will expire immediately.
type: str
choices: [ now, never, default, interval ]
version_added: '3.9.0'
password_expire_interval:
description:
- Number of days I(password) will expire. Requires I(password_expire=interval).
type: int
version_added: '3.9.0'
column_case_sensitive:
description:
- The default is C(false).
- When C(true), the module will not uppercase the field names in the privileges.
- When C(false), the field names will be upper-cased. This is the default
- This feature was introduced because MySQL 8 and above uses case sensitive
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."
- MySQL server must support the INFORMATION_SCHEMA.USER_ATTRIBUTES table. Provided since MySQL 8.0.
- To delete an existing attribute, set its value to null.
type: dict
version_added: '3.9.0'
notes: notes:
- Compatible with MySQL or MariaDB.
- "MySQL server installs with default I(login_user) of C(root) and no password. - "MySQL server installs with default I(login_user) of C(root) and no password.
To secure this user as part of an idempotent playbook, you must create at least two tasks: To secure this user as part of an idempotent playbook, you must create at least two tasks:
1) change the root user's password, without providing any I(login_user)/I(login_password) details, 1) change the root user's password, without providing any I(login_user)/I(login_password) details,
@ -232,10 +149,6 @@ author:
- Jonathan Mainguy (@Jmainguy) - Jonathan Mainguy (@Jmainguy)
- Benjamin Malynovytch (@bmalynovytch) - Benjamin Malynovytch (@bmalynovytch)
- Lukasz Tomaszkiewicz (@tomaszkiewicz) - Lukasz Tomaszkiewicz (@tomaszkiewicz)
- kmarse (@kmarse)
- Laurent Indermühle (@laurent-indermuehle)
- E.S. Rosenberg (@Keeper-of-the-Keys)
extends_documentation_fragment: extends_documentation_fragment:
- community.mysql.mysql - community.mysql.mysql
''' '''
@ -271,15 +184,12 @@ EXAMPLES = r'''
priv: '*.*:ALL' priv: '*.*:ALL'
state: present state: present
# Set session var wsrep_on=off before creating the user
- name: Create database user with password and all database privileges and 'WITH GRANT OPTION' - name: Create database user with password and all database privileges and 'WITH GRANT OPTION'
community.mysql.mysql_user: community.mysql.mysql_user:
name: bob name: bob
password: 12345 password: 12345
priv: '*.*:ALL,GRANT' priv: '*.*:ALL,GRANT'
state: present state: present
session_vars:
wsrep_on: 'off'
- name: Create user with password, all database privileges and 'WITH GRANT OPTION' in db1 and db2 - name: Create user with password, all database privileges and 'WITH GRANT OPTION' in db1 and db2
community.mysql.mysql_user: community.mysql.mysql_user:
@ -299,12 +209,15 @@ EXAMPLES = r'''
FUNCTION my_db.my_function: EXECUTE FUNCTION my_db.my_function: EXECUTE
state: present state: present
- name: Modify user attributes, creating the attribute 'foo' and removing the attribute 'bar' # Note that REQUIRESSL is a special privilege that should only apply to *.* by itself.
# Setting this privilege in this manner is deprecated.
# Use 'tls_requires' instead.
- name: Modify user to require SSL connections
community.mysql.mysql_user: community.mysql.mysql_user:
name: bob name: bob
attributes: append_privs: yes
foo: "foo" priv: '*.*:REQUIRESSL'
bar: null state: present
- name: Modify user to require TLS connection with a valid client certificate - name: Modify user to require TLS connection with a valid client certificate
community.mysql.mysql_user: community.mysql.mysql_user:
@ -389,13 +302,6 @@ EXAMPLES = r'''
priv: '*.*:ALL' priv: '*.*:ALL'
state: present state: present
- name: Create user 'bob' authenticated with plugin 'caching_sha2_password' and static salt
community.mysql.mysql_user:
name: bob
plugin: caching_sha2_password
plugin_auth_string: password
salt: 1234567890abcdefghij
- name: Limit bob's resources to 10 queries per hour and 5 connections per hour - name: Limit bob's resources to 10 queries per hour and 5 connections per hour
community.mysql.mysql_user: community.mysql.mysql_user:
name: bob name: bob
@ -403,20 +309,6 @@ EXAMPLES = r'''
MAX_QUERIES_PER_HOUR: 10 MAX_QUERIES_PER_HOUR: 10
MAX_CONNECTIONS_PER_HOUR: 5 MAX_CONNECTIONS_PER_HOUR: 5
- name: Ensure bob does not have the DELETE privilege
community.mysql.mysql_user:
name: bob
subtract_privs: true
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 # Example .my.cnf file for setting the root password
# [client] # [client]
# user=root # user=root
@ -428,17 +320,16 @@ RETURN = '''#'''
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 ( from ansible_collections.community.mysql.plugins.module_utils.mysql import (
mysql_connect, mysql_connect, mysql_driver, mysql_driver_fail_msg, mysql_common_argument_spec
mysql_driver,
mysql_driver_fail_msg,
mysql_common_argument_spec,
set_session_vars,
) )
from ansible_collections.community.mysql.plugins.module_utils.user import ( from ansible_collections.community.mysql.plugins.module_utils.user import (
convert_priv_dict_to_str, convert_priv_dict_to_str,
get_impl,
get_mode, get_mode,
handle_requiressl_in_priv_string,
InvalidPrivsError, InvalidPrivsError,
limit_resources, limit_resources,
get_valid_privs,
privileges_unpack, privileges_unpack,
sanitize_requires, sanitize_requires,
user_add, user_add,
@ -457,13 +348,7 @@ from ansible.module_utils._text import to_native
def main(): def main():
argument_spec = mysql_common_argument_spec() argument_spec = mysql_common_argument_spec()
argument_spec.update( argument_spec.update(
name=dict(type='str', required=True, aliases=['user'], deprecated_aliases=[ user=dict(type='str', required=True, aliases=['name']),
{
'name': 'user',
'version': '5.0.0',
'collection_name': 'community.mysql',
}],
),
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),
host=dict(type='str', default='localhost'), host=dict(type='str', default='localhost'),
@ -472,31 +357,21 @@ def main():
priv=dict(type='raw'), priv=dict(type='raw'),
tls_requires=dict(type='dict'), tls_requires=dict(type='dict'),
append_privs=dict(type='bool', default=False), append_privs=dict(type='bool', default=False),
subtract_privs=dict(type='bool', default=False),
attributes=dict(type='dict'),
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', 'on_new_username'], no_log=False), update_password=dict(type='str', default='always', choices=['always', 'on_create'], no_log=False),
sql_log_bin=dict(type='bool', default=True), sql_log_bin=dict(type='bool', default=True),
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'),
salt=dict(default=None, type='str'),
resource_limits=dict(type='dict'), resource_limits=dict(type='dict'),
force_context=dict(type='bool', default=False),
session_vars=dict(type='dict'),
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( module = AnsibleModule(
argument_spec=argument_spec, argument_spec=argument_spec,
supports_check_mode=True, supports_check_mode=True,
mutually_exclusive=(('append_privs', 'subtract_privs'),)
) )
login_user = module.params["login_user"] login_user = module.params["login_user"]
login_password = module.params["login_password"] login_password = module.params["login_password"]
user = module.params["name"] user = module.params["user"]
password = module.params["password"] password = module.params["password"]
encrypted = module.boolean(module.params["encrypted"]) encrypted = module.boolean(module.params["encrypted"])
host = module.params["host"].lower() host = module.params["host"].lower()
@ -508,107 +383,72 @@ def main():
connect_timeout = module.params["connect_timeout"] connect_timeout = module.params["connect_timeout"]
config_file = module.params["config_file"] config_file = module.params["config_file"]
append_privs = module.boolean(module.params["append_privs"]) append_privs = module.boolean(module.params["append_privs"])
subtract_privs = module.boolean(module.params['subtract_privs'])
update_password = module.params['update_password'] update_password = module.params['update_password']
attributes = module.params['attributes']
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"] check_hostname = module.params["check_hostname"]
db = '' db = ''
if module.params["force_context"]:
db = 'mysql'
sql_log_bin = module.params["sql_log_bin"] sql_log_bin = module.params["sql_log_bin"]
plugin = module.params["plugin"] plugin = module.params["plugin"]
plugin_hash_string = module.params["plugin_hash_string"] plugin_hash_string = module.params["plugin_hash_string"]
plugin_auth_string = module.params["plugin_auth_string"] plugin_auth_string = module.params["plugin_auth_string"]
salt = module.params["salt"]
resource_limits = module.params["resource_limits"] resource_limits = module.params["resource_limits"]
session_vars = module.params["session_vars"]
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)): 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)) module.fail_json(msg="priv parameter must be str or dict but %s was passed" % type(priv))
if priv and isinstance(priv, dict): if priv and isinstance(priv, dict):
priv = convert_priv_dict_to_str(priv) priv = convert_priv_dict_to_str(priv)
if priv and "REQUIRESSL" in priv:
priv, tls_requires = handle_requiressl_in_priv_string(module, priv, tls_requires)
if mysql_driver is None: if mysql_driver is None:
module.fail_json(msg=mysql_driver_fail_msg) module.fail_json(msg=mysql_driver_fail_msg)
if password_expire_interval and password_expire_interval < 1:
module.fail_json(msg="password_expire_interval value \
should be positive number")
if salt:
if not plugin_auth_string:
module.fail_json(msg="salt requires plugin_auth_string")
if len(salt) != 20:
module.fail_json(msg="salt must be 20 characters long")
if plugin not in ['caching_sha2_password', 'sha256_password']:
module.fail_json(msg="salt requires caching_sha2_password or sha256_password plugin")
cursor = None cursor = None
try: try:
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, check_hostname=check_hostname, autocommit=True) 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, check_hostname=check_hostname, autocommit=True) 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)))
# TODO Release 4.0.0 : Remove this test and variable assignation
if column_case_sensitive is None:
column_case_sensitive = False
module.warn("Option column_case_sensitive is not provided. "
"The default is now false, so the column's name will be uppercased. "
"The default will be changed to true in community.mysql 4.0.0.")
if not sql_log_bin: if not sql_log_bin:
cursor.execute("SET SQL_LOG_BIN=0;") cursor.execute("SET SQL_LOG_BIN=0;")
if session_vars: get_impl(cursor)
set_session_vars(module, cursor, session_vars)
if priv is not None: if priv is not None:
try: try:
mode = get_mode(cursor) mode = get_mode(cursor)
except Exception as e: except Exception as e:
module.fail_json(msg=to_native(e)) module.fail_json(msg=to_native(e))
try:
valid_privs = get_valid_privs(cursor)
priv = privileges_unpack(priv, mode, valid_privs)
except Exception as e:
module.fail_json(msg="invalid privileges string: %s" % to_native(e))
priv = privileges_unpack(priv, mode, column_case_sensitive, ensure_usage=not subtract_privs)
password_changed = False
final_attributes = None
if state == "present": if state == "present":
if user_exists(cursor, user, host, host_all): if user_exists(cursor, user, host, host_all):
try: try:
if update_password == "always": if update_password == "always":
result = user_mod(cursor, user, host, host_all, password, encrypted, changed, msg = user_mod(cursor, user, host, host_all, password, encrypted,
plugin, plugin_hash_string, plugin_auth_string, salt, plugin, plugin_hash_string, plugin_auth_string,
priv, append_privs, subtract_privs, attributes, tls_requires, module, priv, append_privs, tls_requires, module)
password_expire, password_expire_interval, locked=locked)
else: else:
result = user_mod(cursor=cursor, user=user, host=host, host_all=host_all, password=None, changed, msg = user_mod(cursor, user, host, host_all, None, encrypted,
encrypted=encrypted, plugin=None, plugin_hash_string=None, plugin_auth_string=None, None, None, None,
salt=None, new_priv=priv, append_privs=append_privs, subtract_privs=subtract_privs, priv, append_privs, tls_requires, module)
attributes=attributes, tls_requires=tls_requires, module=module,
password_expire=password_expire, password_expire_interval=password_expire_interval,
locked=locked)
changed = result['changed']
msg = result['msg']
password_changed = result['password_changed']
final_attributes = result['attributes']
except (SQLParseError, InvalidPrivsError, mysql_driver.Error) as e: except (SQLParseError, InvalidPrivsError, mysql_driver.Error) as e:
module.fail_json(msg=to_native(e)) module.fail_json(msg=to_native(e))
@ -616,16 +456,9 @@ def main():
if host_all: if host_all:
module.fail_json(msg="host_all parameter cannot be used when adding a user") module.fail_json(msg="host_all parameter cannot be used when adding a user")
try: try:
if subtract_privs: changed = user_add(cursor, user, host, host_all, password, encrypted,
priv = None # avoid granting unwanted privileges plugin, plugin_hash_string, plugin_auth_string,
reuse_existing_password = update_password == 'on_new_username' priv, tls_requires, module.check_mode)
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)
changed = result['changed']
password_changed = result['password_changed']
final_attributes = result['attributes']
if changed: if changed:
msg = "User added" msg = "User added"
@ -642,7 +475,7 @@ def main():
else: else:
changed = False changed = False
msg = "User doesn't exist" msg = "User doesn't exist"
module.exit_json(changed=changed, user=user, msg=msg, password_changed=password_changed, attributes=final_attributes) module.exit_json(changed=changed, user=user, msg=msg)
if __name__ == '__main__': if __name__ == '__main__':

View file

@ -12,9 +12,9 @@ DOCUMENTATION = r'''
--- ---
module: mysql_variables module: mysql_variables
short_description: Manage MySQL or MariaDB global variables short_description: Manage MySQL global variables
description: description:
- Query / Set MySQL or MariaDB variables. - Query / Set MySQL variables.
author: author:
- Balazs Pocze (@banyek) - Balazs Pocze (@banyek)
options: options:
@ -26,7 +26,6 @@ options:
value: value:
description: description:
- If set, then sets variable value to this. - If set, then sets variable value to this.
- With boolean values, use C(0)|C(1) or quoted C("ON")|C("OFF").
type: str type: str
mode: mode:
description: description:
@ -55,9 +54,6 @@ seealso:
description: Complete reference of the MySQL SET command documentation. description: Complete reference of the MySQL SET command documentation.
link: https://dev.mysql.com/doc/refman/8.0/en/set-statement.html link: https://dev.mysql.com/doc/refman/8.0/en/set-statement.html
notes:
- Compatible with MariaDB or MySQL.
extends_documentation_fragment: extends_documentation_fragment:
- community.mysql.mysql - community.mysql.mysql
''' '''
@ -75,11 +71,6 @@ EXAMPLES = r'''
variable: read_only variable: read_only
value: 1 value: 1
mode: persist mode: persist
- name: Set a boolean using ON/OFF notation
mysql_variables:
variable: log_slow_replica_statements
value: "ON" # Make sure it's quoted
''' '''
RETURN = r''' RETURN = r'''
@ -182,22 +173,10 @@ def setvariable(cursor, mysqlvar, value, mode='global'):
return result return result
def convert_bool_setting_value_wanted(val):
"""Converts passed value from 0,1,on,off to ON/OFF
as it's represented in the server.
"""
if val in ('on', 1):
val = 'ON'
elif val in ('off', 0):
val = 'OFF'
return val
def main(): def main():
argument_spec = mysql_common_argument_spec() argument_spec = mysql_common_argument_spec()
argument_spec.update( argument_spec.update(
variable=dict(type='str', required=True), variable=dict(type='str'),
value=dict(type='str'), value=dict(type='str'),
mode=dict(type='str', choices=['global', 'persist', 'persist_only'], default='global'), mode=dict(type='str', choices=['global', 'persist', 'persist_only'], default='global'),
) )
@ -221,7 +200,7 @@ def main():
if mysqlvar is None: if mysqlvar is None:
module.fail_json(msg="Cannot run without variable to operate with") module.fail_json(msg="Cannot run without variable to operate with")
if match('^[0-9A-Za-z_.]+$', mysqlvar) is None: if match('^[0-9a-z_.]+$', mysqlvar) is None:
module.fail_json(msg="invalid variable name \"%s\"" % mysqlvar) module.fail_json(msg="invalid variable name \"%s\"" % mysqlvar)
if mysql_driver is None: if mysql_driver is None:
module.fail_json(msg=mysql_driver_fail_msg) module.fail_json(msg=mysql_driver_fail_msg)
@ -261,9 +240,6 @@ def main():
# Type values before using them # Type values before using them
value_wanted = typedvalue(value) value_wanted = typedvalue(value)
value_actual = typedvalue(mysqlvar_val) value_actual = typedvalue(mysqlvar_val)
if value_actual in ('ON', 'OFF') and value_wanted not in ('ON', 'OFF'):
value_wanted = convert_bool_setting_value_wanted(value_wanted)
value_in_auto_cnf = None value_in_auto_cnf = None
if var_in_mysqld_auto_cnf is not None: if var_in_mysqld_auto_cnf is not None:
value_in_auto_cnf = typedvalue(var_in_mysqld_auto_cnf) value_in_auto_cnf = typedvalue(var_in_mysqld_auto_cnf)

View file

@ -0,0 +1,21 @@
FROM quay.io/ansible/ubuntu2204-test-container:main
# ubuntu2204 comes with mariadb-client-10.6
# iproute2 # To grab docker network gateway address
# python3.10-dev # Reqs for mysqlclient
# default-libmysqlclient-dev # Reqs for mysqlclient
# build-essential # Reqs for mysqlclient
RUN apt-get update -y && \
DEBIAN_FRONTEND=noninteractive apt-get upgrade -y --no-install-recommends && \
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
python3.10 \
python3.10-dev \
mariadb-client \
iproute2 \
default-libmysqlclient-dev \
build-essential
RUN python3.10 -m pip install --disable-pip-version-check --no-cache-dir mysqlclient==2.1.1
ENV container=docker
CMD ["/sbin/init"]

View file

@ -0,0 +1,15 @@
FROM quay.io/ansible/ubuntu2204-test-container:main
# ubuntu2204 comes with mariadb-client-10.6
# iproute2 # To grab docker network gateway address
RUN apt-get update -y && \
DEBIAN_FRONTEND=noninteractive apt-get upgrade -y --no-install-recommends && \
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
python3.10 \
mariadb-client \
iproute2
RUN python3.10 -m pip install --disable-pip-version-check --no-cache-dir pymysql==1.0.2
ENV container=docker
CMD ["/sbin/init"]

View file

@ -0,0 +1,21 @@
FROM quay.io/ansible/ubuntu2004-test-container:main
# ubuntu2004 comes with mariadb-client-10.3
# iproute2 # To grab docker network gateway address
# python3.8-dev # Reqs for mysqlclient
# default-libmysqlclient-dev # Reqs for mysqlclient
# build-essential # Reqs for mysqlclient
RUN apt-get update -y && \
DEBIAN_FRONTEND=noninteractive apt-get upgrade -y --no-install-recommends && \
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
python3.8 \
python3.8-dev \
mariadb-client \
iproute2 \
default-libmysqlclient-dev \
build-essential
RUN python3.8 -m pip install --disable-pip-version-check --no-cache-dir mysqlclient==2.0.1
ENV container=docker
CMD ["/sbin/init"]

View file

@ -0,0 +1,15 @@
FROM quay.io/ansible/ubuntu2004-test-container:main
# ubuntu2004 comes with mariadb-client-10.3
# iproute2 # To grab docker network gateway address
RUN apt-get update -y && \
DEBIAN_FRONTEND=noninteractive apt-get upgrade -y --no-install-recommends && \
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
python3.8 \
mariadb-client \
iproute2
RUN python3.8 -m pip install --disable-pip-version-check --no-cache-dir pymysql==0.9.3
ENV container=docker
CMD ["/sbin/init"]

View file

@ -0,0 +1,21 @@
FROM quay.io/ansible/ubuntu2004-test-container:main
# ubuntu2004 comes with mariadb-client-10.3
# iproute2 # To grab docker network gateway address
# python3.9-dev # Reqs for mysqlclient
# default-libmysqlclient-dev # Reqs for mysqlclient
# build-essential # Reqs for mysqlclient
RUN apt-get update -y && \
DEBIAN_FRONTEND=noninteractive apt-get upgrade -y --no-install-recommends && \
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
python3.9 \
python3.9-dev \
mariadb-client \
iproute2 \
default-libmysqlclient-dev \
build-essential
RUN python3.9 -m pip install --disable-pip-version-check --no-cache-dir mysqlclient==2.0.3
ENV container=docker
CMD ["/sbin/init"]

View file

@ -0,0 +1,15 @@
FROM quay.io/ansible/ubuntu2004-test-container:main
# ubuntu2004 comes with mariadb-client-10.3
# iproute2 # To grab docker network gateway address
RUN apt-get update -y && \
DEBIAN_FRONTEND=noninteractive apt-get upgrade -y --no-install-recommends && \
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
python3.9 \
mariadb-client \
iproute2
RUN python3.9 -m pip install --disable-pip-version-check --no-cache-dir pymysql==0.9.3
ENV container=docker
CMD ["/sbin/init"]

View file

@ -0,0 +1,21 @@
FROM quay.io/ansible/ubuntu1804-test-container:main
# ubuntu1804 comes with mysql-client-5.7
# iproute2 # To grab docker network gateway address
# python3.8-dev # Reqs for mysqlclient
# default-libmysqlclient-dev # Reqs for mysqlclient
# build-essential # Reqs for mysqlclient
RUN apt-get update -y && \
DEBIAN_FRONTEND=noninteractive apt-get upgrade -y --no-install-recommends && \
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
python3.8 \
python3.8-dev \
mysql-client \
iproute2 \
default-libmysqlclient-dev \
build-essential
RUN python3.8 -m pip install --disable-pip-version-check --no-cache-dir mysqlclient==2.0.1
ENV container=docker
CMD ["/sbin/init"]

View file

@ -0,0 +1,21 @@
FROM quay.io/ansible/ubuntu1804-test-container:main
# ubuntu1804 comes with mysql-client-5.7
# iproute2 # To grab docker network gateway address
# python3.8-dev # Reqs for mysqlclient
# default-libmysqlclient-dev # Reqs for mysqlclient
# build-essential # Reqs for mysqlclient
RUN apt-get update -y && \
DEBIAN_FRONTEND=noninteractive apt-get upgrade -y --no-install-recommends && \
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
python3.8 \
python3.8-dev \
mysql-client \
iproute2 \
default-libmysqlclient-dev \
build-essential
RUN python3.8 -m pip install --disable-pip-version-check --no-cache-dir pymysql==0.7.11
ENV container=docker
CMD ["/sbin/init"]

View file

@ -0,0 +1,15 @@
FROM quay.io/ansible/ubuntu1804-test-container:main
# ubuntu1804 comes with mysql-client-5.7
# iproute2 # To grab docker network gateway address
RUN apt-get update -y && \
DEBIAN_FRONTEND=noninteractive apt-get upgrade -y --no-install-recommends && \
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
python3.8 \
mysql-client \
iproute2
RUN python3.8 -m pip install --disable-pip-version-check --no-cache-dir pymysql==0.9.3
ENV container=docker
CMD ["/sbin/init"]

View file

@ -0,0 +1,21 @@
FROM quay.io/ansible/ubuntu2204-test-container:main
# ubuntu2204 comes with mysql-client-8
# iproute2 # To grab docker network gateway address
# python3.10-dev # Reqs for mysqlclient
# default-libmysqlclient-dev # Reqs for mysqlclient
# build-essential # Reqs for mysqlclient
RUN apt-get update -y && \
DEBIAN_FRONTEND=noninteractive apt-get upgrade -y --no-install-recommends && \
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
python3.10 \
python3.10-dev \
mysql-client \
iproute2 \
default-libmysqlclient-dev \
build-essential
RUN python3.10 -m pip install --disable-pip-version-check --no-cache-dir mysqlclient==2.1.1
ENV container=docker
CMD ["/sbin/init"]

View file

@ -0,0 +1,15 @@
FROM quay.io/ansible/ubuntu2204-test-container:main
# ubuntu2204 comes with mysql-client-8
# iproute2 # To grab docker network gateway address
RUN apt-get update -y && \
DEBIAN_FRONTEND=noninteractive apt-get upgrade -y --no-install-recommends && \
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
python3.10 \
mysql-client \
iproute2
RUN python3.10 -m pip install --disable-pip-version-check --no-cache-dir pymysql==1.0.2
ENV container=docker
CMD ["/sbin/init"]

View file

@ -0,0 +1,21 @@
FROM quay.io/ansible/ubuntu2004-test-container:main
# ubuntu2004 comes with mysql-client-8
# iproute2 # To grab docker network gateway address
# python3.8-dev # Reqs for mysqlclient
# default-libmysqlclient-dev # Reqs for mysqlclient
# build-essential # Reqs for mysqlclient
RUN apt-get update -y && \
DEBIAN_FRONTEND=noninteractive apt-get upgrade -y --no-install-recommends && \
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
python3.8 \
python3.8-dev \
mysql-client \
iproute2 \
default-libmysqlclient-dev \
build-essential
RUN python3.8 -m pip install --disable-pip-version-check --no-cache-dir mysqlclient==2.0.1
ENV container=docker
CMD ["/sbin/init"]

View file

@ -0,0 +1,15 @@
FROM quay.io/ansible/ubuntu2004-test-container:main
# ubuntu2004 comes with mysql-client-8
# iproute2 # To grab docker network gateway address
RUN apt-get update -y && \
DEBIAN_FRONTEND=noninteractive apt-get upgrade -y --no-install-recommends && \
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
python3.8 \
mysql-client \
iproute2
RUN python3.8 -m pip install --disable-pip-version-check --no-cache-dir pymysql==0.9.3
ENV container=docker
CMD ["/sbin/init"]

View file

@ -0,0 +1,21 @@
FROM quay.io/ansible/ubuntu2004-test-container:main
# ubuntu2004 comes with mysql-client-8
# iproute2 # To grab docker network gateway address
# python3.9-dev # Reqs for mysqlclient
# default-libmysqlclient-dev # Reqs for mysqlclient
# build-essential # Reqs for mysqlclient
RUN apt-get update -y && \
DEBIAN_FRONTEND=noninteractive apt-get upgrade -y --no-install-recommends && \
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
python3.9 \
python3.9-dev \
mysql-client \
iproute2 \
default-libmysqlclient-dev \
build-essential
RUN python3.9 -m pip install --disable-pip-version-check --no-cache-dir mysqlclient==2.0.3
ENV container=docker
CMD ["/sbin/init"]

View file

@ -0,0 +1,16 @@
FROM quay.io/ansible/ubuntu2004-test-container:main
# ubuntu2004 comes with mysql-client-8
# iproute2 # To grab docker network gateway address
RUN apt-get update -y && \
DEBIAN_FRONTEND=noninteractive apt-get upgrade -y --no-install-recommends && \
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
python3.9 \
mysql-client \
iproute2
# cffi # To connect to MySQL 8 with Python3.9 and PyMySQL
RUN python3.9 -m pip install --disable-pip-version-check --no-cache-dir cffi pymysql==0.9.3
ENV container=docker
CMD ["/sbin/init"]

View file

@ -1,4 +1,4 @@
# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) <andrew.a.klychkov@gmail.com> # 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) # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# Initial CI tests of mysql_replication module # Initial CI tests of mysql_replication module

View file

@ -1,4 +1,4 @@
# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) <andrew.a.klychkov@gmail.com> # 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) # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# Tests for master_use_gtid parameter. # Tests for master_use_gtid parameter.

View file

@ -1,4 +1,4 @@
# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) <andrew.a.klychkov@gmail.com> # 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) # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# Needs for further tests: # Needs for further tests:

View file

@ -1,4 +1,4 @@
# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) <andrew.a.klychkov@gmail.com> # 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) # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# Preparation: # Preparation:

View file

@ -1,49 +0,0 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: SKS 1.1.6
Comment: Hostname: pgp.mit.edu
mQINBGU2rNoBEACSi5t0nL6/Hj3d0PwsbdnbY+SqLUIZ3uWZQm6tsNhvTnahvPPZBGdl99iW
YTt2KmXp0KeN2s9pmLKkGAbacQP1RqzMFnoHawSMf0qTUVjAvhnI4+qzMDjTNSBq9fa3nHmO
YxownnrRkpiQUM/yD7/JmVENgwWb6akZeGYrXch9jd4XV3t8OD6TGzTedTki0TDNr6YZYhC7
jUm9fK9Zs299pzOXSxRRNGd+3H9gbXizrBu4L/3lUrNf//rM7OvV9Ho7u9YYyAQ3L3+OABK9
FKHNhrpi8Q0cbhvWkD4oCKJ+YZ54XrOG0YTg/YUAs5/3//FATI1sWdtLjJ5pSb0onV3LIbar
RTN8lC4Le/5kd3lcot9J8b3EMXL5p9OGW7wBfmNVRSUI74Vmwt+v9gyp0Hd0keRCUn8lo/1V
0YD9i92KsE+/IqoYTjnya/5kX41jB8vr1ebkHFuJ404+G6ETd0owwxq64jLIcsp/GBZHGU0R
KKAo9DRLH7rpQ7PVlnw8TDNlOtWt5EJlBXFcPL+NgWbqkADAyA/XSNeWlqonvPlYfmasnAHA
pMd9NhPQhC7hJTjCiAwG8UyWpV8Dj07DHFQ5xBbkTnKH2OrJtguPqSNYtTASbsWz09S8ujoT
DXFT17NbFM2dMIiq0a4VQB3SzH13H2io9Cbg/TzJrJGmwgoXgwARAQABtDZNeVNRTCBSZWxl
YXNlIEVuZ2luZWVyaW5nIDxteXNxbC1idWlsZEBvc3Mub3JhY2xlLmNvbT6JAlQEEwEIAD4W
IQS8pDQXw7SF3RKOxtS3s7eIqNN4XAUCZTas2gIbAwUJA8JnAAULCQgHAgYVCgkICwIEFgID
AQIeAQIXgAAKCRC3s7eIqNN4XLzoD/9PlpWtfHlI8eQTHwGsGIwFA+fgipyDElapHw3MO+K9
VOEYRZCZSuBXHJe9kjGEVCGUDrfImvgTuNuqYmVUV+wyhP+w46W/cWVkqZKAW0hNp0TTvu3e
Dwap7gdk80VF24Y2Wo0bbiGkpPiPmB59oybGKaJ756JlKXIL4hTtK3/hjIPFnb64Ewe4YLZy
oJu0fQOyA8gXuBoalHhUQTbRpXI0XI3tpZiQemNbfBfJqXo6LP3/LgChAuOfHIQ8alvnhCwx
hNUSYGIRqx+BEbJw1X99Az8XvGcZ36VOQAZztkW7mEfH9NDPz7MXwoEvduc61xwlMvEsUIaS
fn6SGLFzWPClA98UMSJgF6sKb+JNoNbzKaZ8V5w13msLb/pq7hab72HH99XJbyKNliYj3+KA
3q0YLf+Hgt4Y4EhIJ8x2+g690Np7zJF4KXNFbi1BGloLGm78akY1rQlzpndKSpZq5KWw8FY/
1PEXORezg/BPD3Etp0AVKff4YdrDlOkNB7zoHRfFHAvEuuqti8aMBrbRnRSG0xunMUOEhbYS
/wOOTl0g3bF9NpAkfU1Fun57N96Us2T9gKo9AiOY5DxMe+IrBg4zaydEOovgqNi2wbU0MOBQ
b23Puhj7ZCIXcpILvcx9ygjkONr75w+XQrFDNeux4Znzay3ibXtAPqEykPMZHsZ2sbkCDQRl
NqzaARAAsdvBo8WRqZ5WVVk6lReD8b6Zx83eJUkV254YX9zn5t8KDRjYOySwS75mJIaZLsv0
YQjJk+5rt10tejyCrJIFo9CMvCmjUKtVbgmhfS5+fUDRrYCEZBBSa0Dvn68EBLiHugr+SPXF
6o1hXEUqdMCpB6oVp6X45JVQroCKIH5vsCtw2jU8S2/IjjV0V+E/zitGCiZaoZ1f6NG7ozyF
ep1CSAReZu/sssk0pCLlfCebRd9Rz3QjSrQhWYuJa+eJmiF4oahnpUGktxMD632I9aG+IMfj
tNJNtX32MbO+Se+cCtVc3cxSa/pR+89a3cb9IBA5tFF2Qoekhqo/1mmLi93Xn6uDUhl5tVxT
nB217dBT27tw+p0hjd9hXZRQbrIZUTyh3+8EMfmAjNSIeR+th86xRd9XFRr9EOqrydnALOUr
9cT7TfXWGEkFvn6ljQX7f4RvjJOTbc4jJgVFyu8K+VU6u1NnFJgDiNGsWvnYxAf7gDDbUSXE
uC2anhWvxPvpLGmsspngge4yl+3nv+UqZ9sm6LCebR/7UZ67tYz3p6xzAOVgYsYcxoIUuEZX
jHQtsYfTZZhrjUWBJ09jrMvlKUHLnS437SLbgoXVYZmcqwAWpVNOLZf+fFm4IE5aGBG5Dho2
CZ6ujngW9Zkn98T1d4N0MEwwXa2V6T1ijzcqD7GApZUAEQEAAYkCPAQYAQgAJhYhBLykNBfD
tIXdEo7G1Lezt4io03hcBQJlNqzaAhsMBQkDwmcAAAoJELezt4io03hcXqMP/01aPT3A3Sg7
oTQoHdCxj04ELkzrezNWGM+YwbSKrR2LoXR8zf2tBFzc2/Tl98V0+68f/eCvkvqCuOtq4392
Ps23j9W3r5XG+GDOwDsx0gl0E+Qkw07pwdJctA6efsmnRkjF2YVO0N9MiJA1tc8NbNXpEEHJ
Z7F8Ri5cpQrGUz/AY0eae2b7QefyP4rpUELpMZPjc8Px39Fe1DzRbT+5E19TZbrpbwlSYs1i
CzS5YGFmpCRyZcLKXo3zS6N22+82cnRBSPPipiO6WaQawcVMlQO1SX0giB+3/DryfN9VuIYd
1EWCGQa3O0MVu6o5KVHwPgl9R1P6xPZhurkDpAd0b1s4fFxin+MdxwmG7RslZA9CXRPpzo7/
fCMW8sYOH15DP+YfUckoEreBt+zezBxbIX2CGGWEV9v3UBXadRtwxYQ6sN9bqW4jm1b41vNA
17b6CVH6sVgtU3eN+5Y9an1e5jLD6kFYx+OIeqIIId/TEqwS61csY9aav4j4KLOZFCGNU0FV
ji7NQewSpepTcJwfJDOzmtiDP4vol1ApJGLRwZZZ9PB6wsOgDOoP6sr0YrDI/NNX2RyXXbgl
nQ1yJZVSH3/3eo6knG2qTthUKHCRDNKdy9Qqc1x4WWWtSRjh+zX8AvJK2q1rVLH2/3ilxe9w
cAZUlaj3id3TxquAlud4lWDz
=h5nH
-----END PGP PUBLIC KEY BLOCK-----

View file

@ -4,18 +4,15 @@
# and should not be used as examples of how to write Ansible roles # # and should not be used as examples of how to write Ansible roles #
#################################################################### ####################################################################
- name: "{{ role_name }} | Main | Prepare the fake root folder" - name: Prepare the fake root folder
ansible.builtin.import_tasks: ansible.builtin.import_tasks:
file: fake_root.yml file: fake_root.yml
- name: "{{ role_name }} | Main | Set variables" # setvars.yml requires the iproute2 package installed by install.yml
- name: Set variables
ansible.builtin.import_tasks: ansible.builtin.import_tasks:
file: setvars.yml file: setvars.yml
- name: "{{ role_name }} | Main | Install requirements" - name: Verify all components version under test
ansible.builtin.import_tasks:
file: requirements.yml
- name: "{{ role_name }} | Main | Verify all components version under test"
ansible.builtin.import_tasks: ansible.builtin.import_tasks:
file: verify.yml file: verify.yml

View file

@ -1,54 +0,0 @@
---
# 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:
- bzip2 # To test mysql_db dump compression
- "{{ db_engine }}-client"
# The command mysql-config must be present for mysqlclient python package.
# The package libmysqlclient-dev that provides this command have a
# different name between Ubuntu 20.04 and 22.04. Luckily, libmysql++ is
# available on both.
- "{{ 'libmysql++-dev' if db_engine == 'mysql' else 'libmariadb-dev' }}"
state: present
- name: "{{ role_name }} | Requirements | Install Python packages"
ansible.builtin.pip:
name:
- "{{ connector_name }}=={{ connector_version }}"
state: present

View file

@ -1,17 +1,13 @@
--- ---
- name: "{{ role_name }} | Setvars | Install tools gather network facts" - name: "{{ role_name }} | Setvars | Extract Podman/Docker Network Gateway"
ansible.builtin.package: ansible.builtin.shell:
name: cmd: ip route|grep default|awk '{print $3}'
- iproute2 register: ip_route_output
state: present
- name: "{{ role_name }} | Setvars | Gather facts"
ansible.builtin.setup:
- name: "{{ role_name }} | Setvars | Set Fact" - name: "{{ role_name }} | Setvars | Set Fact"
ansible.builtin.set_fact: ansible.builtin.set_fact:
gateway_addr: "{{ ansible_default_ipv4.gateway }}" gateway_addr: "{{ ip_route_output.stdout }}"
connector_name_lookup: >- connector_name_lookup: >-
{{ lookup( {{ lookup(
'file', 'file',
@ -32,6 +28,11 @@
'file', 'file',
'/root/ansible_collections/community/mysql/tests/integration/db_engine_version' '/root/ansible_collections/community/mysql/tests/integration/db_engine_version'
) }} ) }}
python_version_lookup: >-
{{ lookup(
'file',
'/root/ansible_collections/community/mysql/tests/integration/python'
) }}
ansible_version_lookup: >- ansible_version_lookup: >-
{{ lookup( {{ lookup(
'file', 'file',
@ -44,6 +45,7 @@
connector_version: "{{ connector_version_lookup.strip() }}" connector_version: "{{ connector_version_lookup.strip() }}"
db_engine: "{{ db_engine_name_lookup.strip() }}" db_engine: "{{ db_engine_name_lookup.strip() }}"
db_version: "{{ db_engine_version_lookup.strip() }}" db_version: "{{ db_engine_version_lookup.strip() }}"
python_version: "{{ python_version_lookup.strip() }}"
test_ansible_version: >- test_ansible_version: >-
{%- if ansible_version_lookup == 'devel' -%} {%- if ansible_version_lookup == 'devel' -%}
{{ ansible_version_lookup }} {{ ansible_version_lookup }}
@ -71,6 +73,7 @@
connector_version: {{ connector_version }} connector_version: {{ connector_version }}
db_engine: {{ db_engine }} db_engine: {{ db_engine }}
db_version: {{ db_version }} db_version: {{ db_version }}
python_version: {{ python_version }}
test_ansible_version: {{ test_ansible_version }} test_ansible_version: {{ test_ansible_version }}
ansible.builtin.debug: ansible.builtin.debug:
msg: "{{ msg.split('\n') }}" msg: "{{ msg.split('\n') }}"

View file

@ -41,20 +41,16 @@
when: when:
- connector_name == 'mysqlclient' - connector_name == 'mysqlclient'
- name: Get the python version in use
ansible.builtin.command:
cmd: python -V
changed_when: false
failed_when: false
register: python_version_in_use
- name: Display the python version in use - name: Display the python version in use
ansible.builtin.debug: command:
msg: > cmd: python{{ python_version }} -V
Python in use inside the test container: changed_when: false
${{ python_version_in_use }} register: python_in_use
when:
- python_version_in_use is defined - name: Assert that expected Python is installed
assert:
that:
- python_in_use.stdout is search(python_version)
- name: Assert that we run the expected ansible version - name: Assert that we run the expected ansible version
assert: assert:

View file

@ -1,9 +1,5 @@
- name: delete temporary directory - name: delete temporary directory
include_tasks: default-cleanup.yml include_tasks: default-cleanup.yml
tags:
- setup_remote_tmp_dir
- name: delete temporary directory (windows) - name: delete temporary directory (windows)
include_tasks: windows-cleanup.yml include_tasks: windows-cleanup.yml
tags:
- setup_remote_tmp_dir

View file

@ -7,13 +7,9 @@
setup: setup:
gather_subset: distribution gather_subset: distribution
when: ansible_facts == {} when: ansible_facts == {}
tags:
- setup_remote_tmp_dir
- include_tasks: "{{ lookup('first_found', files)}}" - include_tasks: "{{ lookup('first_found', files)}}"
vars: vars:
files: files:
- "{{ ansible_os_family | lower }}.yml" - "{{ ansible_os_family | lower }}.yml"
- "default.yml" - "default.yml"
tags:
- setup_remote_tmp_dir

View file

@ -93,9 +93,7 @@
- name: Config overrides | Add fake host to config file - name: Config overrides | Add fake host to config file
shell: 'echo "host = {{ fake_host }}" >> {{ config_file }}' shell: 'echo "host = {{ fake_host }}" >> {{ config_file }}'
- name: >- - name: Config overrides | Remove database using fake login_host
Config overrides | Fail to Remove database using fake login_host
because its default has been overriden by wrong value from config file
mysql_db: mysql_db:
login_user: '{{ mysql_user }}' login_user: '{{ mysql_user }}'
login_password: '{{ mysql_password }}' login_password: '{{ mysql_password }}'
@ -104,17 +102,15 @@
name: '{{ db_to_create }}' name: '{{ db_to_create }}'
state: absent state: absent
config_file: '{{ config_file }}' config_file: '{{ config_file }}'
config_overrides_defaults: true config_overrides_defaults: yes
register: result register: result
failed_when: ignore_errors: yes
- result is succeeded
- result.msg is not search(pattern1) - name: Config overrides | Must fail because login_host default has beed overriden by wrong value from config file
- result.msg is not search(pattern2) assert:
- result.msg is not search(pattern3) that:
vars: - result is failed
pattern1: 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 }}'")
pattern2: Unknown MySQL server host '{{ fake_host }}'
pattern3: Unknown server host '{{ fake_host }}'
- name: Config overrides | Clean up test database - name: Config overrides | Clean up test database
mysql_db: mysql_db:

View file

@ -1,149 +0,0 @@
---
# When mysqldump encountered an issue, mysql_db was still happy. But the
# dump produced was empty or worse, only contained `DROP TABLE IF EXISTS...`
- module_defaults:
community.mysql.mysql_db: &mysql_defaults
login_user: '{{ mysql_user }}'
login_password: '{{ mysql_password }}'
login_host: '{{ mysql_host }}'
login_port: '{{ mysql_primary_port }}'
community.mysql.mysql_query: *mysql_defaults
block:
- name: Dumps errors | Setup test | Create 2 schemas
community.mysql.mysql_db:
name:
- "db1"
- "db2"
state: present
- name: Dumps errors | Setup test | Create 2 tables
community.mysql.mysql_query:
query:
- "CREATE TABLE db1.t1 (id int)"
- "CREATE TABLE db1.t2 (id int)"
- "CREATE VIEW db2.v1 AS SELECT id from db1.t1"
- name: Dumps errors | Full dump without compression
community.mysql.mysql_db:
state: dump
name: all
target: /tmp/full-dump.sql
register: full_dump
- name: Dumps errors | Full dump with gunzip
community.mysql.mysql_db:
state: dump
name: all
target: /tmp/full-dump.sql.gz
register: full_dump_gz
- name: Dumps errors | Distinct dump without compression
community.mysql.mysql_db:
state: dump
name: db2
target: /tmp/dump-db2.sql
register: dump_db2
- name: Dumps errors | Distinct dump with gunzip
community.mysql.mysql_db:
state: dump
name: db2
target: /tmp/dump-db2.sql.gz
register: dump_db2_gz
- name: Dumps errors | Check distinct dumps are changed
ansible.builtin.assert:
that:
- dump_db2 is changed
- dump_db2_gz is changed
# Now db2.v1 targets an inexistant table so mysqldump will fail
- name: Dumps errors | Drop t1
community.mysql.mysql_query:
query:
- "DROP TABLE db1.t1"
- name: Dumps errors | Full dump after drop t1 without compression
community.mysql.mysql_db:
state: dump
name: all
target: /tmp/full-dump-without-t1.sql
pipefail: true # This should do nothing
register: full_dump_without_t1
ignore_errors: true
- name: Dumps errors | Full dump after drop t1 with gzip without the fix
community.mysql.mysql_db:
state: dump
name: all
target: /tmp/full-dump-without-t1.sql.gz
register: full_dump_without_t1_gz_without_fix
ignore_errors: true
- name: Dumps errors | Full dump after drop t1 with gzip with the fix
community.mysql.mysql_db:
state: dump
name: all
target: /tmp/full-dump-without-t1.sql.gz
pipefail: true
register: full_dump_without_t1_gz_with_fix
ignore_errors: true
- name: Dumps errors | Check full dump
ansible.builtin.assert:
that:
- full_dump_without_t1 is failed
- full_dump_without_t1.msg is search(
'references invalid table')
- full_dump_without_t1_gz_without_fix is changed
- full_dump_without_t1_gz_with_fix is failed
- full_dump_without_t1_gz_with_fix.msg is search(
'references invalid table')
- name: Dumps errors | Distinct dump after drop t1 without compression
community.mysql.mysql_db:
state: dump
name: db2
target: /tmp/dump-db2-without_t1.sql
pipefail: true # This should do nothing
register: dump_db2_without_t1
ignore_errors: true
- name: Dumps errors | Distinct dump after drop t1 with gzip without the fix
community.mysql.mysql_db:
state: dump
name: db2
target: /tmp/dump-db2-without_t1.sql.gz
register: dump_db2_without_t1_gz_without_fix
ignore_errors: true
- name: Dumps errors | Distinct dump after drop t1 with gzip with the fix
community.mysql.mysql_db:
state: dump
name: db2
target: /tmp/dump-db2-without_t1.sql.gz
pipefail: true
register: dump_db2_without_t1_gz_with_fix
ignore_errors: true
- name: Dumps errors | Check distinct dump
ansible.builtin.assert:
that:
- dump_db2_without_t1 is failed
- dump_db2_without_t1.msg is search(
'references invalid table')
- dump_db2_without_t1_gz_without_fix is changed
- dump_db2_without_t1_gz_with_fix is failed
- dump_db2_without_t1_gz_with_fix.msg is search(
'references invalid table')
- name: Dumps errors | Cleanup
community.mysql.mysql_db:
name:
- "db1"
- "db2"
state: absent

View file

@ -60,6 +60,3 @@
vars: vars:
db_name: "{{ item }}" db_name: "{{ item }}"
loop: "{{ db_names }}" loop: "{{ db_names }}"
- name: Check errors from mysqldump are seen issue 256
ansible.builtin.include_tasks: issue_256_mysqldump_errors.yml

View file

@ -111,24 +111,11 @@
check_implicit_admin: no check_implicit_admin: no
register: result register: result
- name: Dump and Import | Assert successful completion of dump operation for MariaDB and MySQL < 8.2 - name: Dump and Import | Assert successful completion of dump operation
assert: assert:
that: that:
- result is changed - result is changed
- result.executed_commands[0] is search(".department --master-data=1 --skip-triggers") - 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) - name: Dump and Import | State dump/import - file name should exist (db_file_name)
file: file:
@ -432,51 +419,6 @@
that: that:
- result is changed - result is changed
########################
# Test import with chdir
- name: Dump and Import | Create dir
file:
path: ~/subdir
state: directory
- name: Dump and Import | Create test dump
shell: 'echo "SOURCE ./subdir_test.sql" > ~/original_test.sql'
- name: Dump and Import | Create test source
shell: 'echo "SELECT 1" > ~/subdir/subdir_test.sql'
- name: Dump and Import | Try to restore without chdir argument, must fail
mysql_db:
login_user: '{{ mysql_user }}'
login_password: '{{ mysql_password }}'
login_host: '{{ mysql_host }}'
login_port: '{{ mysql_primary_port }}'
name: '{{ db_name }}'
state: import
target: '~/original_test.sql'
ignore_errors: yes
register: result
- assert:
that:
- result is failed
- result.msg is search('Failed to open file')
- name: Dump and Import | Restore with chdir argument, must pass
mysql_db:
login_user: '{{ mysql_user }}'
login_password: '{{ mysql_password }}'
login_host: '{{ mysql_host }}'
login_port: '{{ mysql_primary_port }}'
name: '{{ db_name }}'
state: import
target: '~/original_test.sql'
chdir: ~/subdir
register: result
- assert:
that:
- result is succeeded
########## ##########
# Clean up # Clean up
########## ##########

View file

@ -1,7 +0,0 @@
DELIMITER //
DROP PROCEDURE IF EXISTS users_info_db.get_all_items;
CREATE PROCEDURE users_info_db.get_all_items()
BEGIN
SELECT * from users_info_db.t1;
END //
DELIMITER ;

View file

@ -1,5 +1,5 @@
--- ---
# Added in 3.6.0 in # Added in 1.5.0, 2.4.0 and 3.6.0 in
# https://github.com/ansible-collections/community.mysql/pull/497 # https://github.com/ansible-collections/community.mysql/pull/497
- name: Connector info | Assert connector_name exists and has expected values - name: Connector info | Assert connector_name exists and has expected values

View file

@ -1,161 +0,0 @@
---
- module_defaults:
community.mysql.mysql_db: &mysql_defaults
login_user: "{{ mysql_user }}"
login_password: "{{ mysql_password }}"
login_host: "{{ mysql_host }}"
login_port: "{{ mysql_primary_port }}"
community.mysql.mysql_query: *mysql_defaults
community.mysql.mysql_info: *mysql_defaults
community.mysql.mysql_user: *mysql_defaults
block:
# ================================ Prepare ==============================
- name: Mysql_info databases | Prepare | Create databases
community.mysql.mysql_db:
name:
- db_tables_count_empty
- db_tables_count_1
- db_tables_count_2
- db_only_views # https://github.com/ansible-Getions/community.mysql/issues/204
state: present
- name: Mysql_info databases | Prepare | Create tables
community.mysql.mysql_query:
query:
- >-
CREATE TABLE IF NOT EXISTS db_tables_count_1.t1
(id int, name varchar(9))
- >-
CREATE TABLE IF NOT EXISTS db_tables_count_2.t1
(id int, name1 varchar(9))
- >-
CREATE TABLE IF NOT EXISTS db_tables_count_2.t2
(id int, name1 varchar(9))
- >-
CREATE VIEW db_only_views.v_today (today) AS SELECT CURRENT_DATE
# ================================== Tests ==============================
- name: Mysql_info databases | Get all non-empty databases fields
community.mysql.mysql_info:
filter:
- databases
register: result
failed_when:
- >
result.databases['db_tables_count_1'].size != 16384 or
result.databases['db_tables_count_1'].tables != 1 or
result.databases['db_tables_count_2'].size != 32768 or
result.databases['db_tables_count_2'].tables != 2 or
result.databases['db_only_views'].size != 0 or
result.databases['db_only_views'].tables != 1 or
'db_tables_count_empty' in result.databases | dict2items
| map(attribute='key')
- name: Mysql_info databases | Get all dbs fields except db_size
community.mysql.mysql_info:
filter:
- databases
exclude_fields:
- db_size
register: result
failed_when:
- >
result.databases['db_tables_count_1'].size is defined or
result.databases['db_tables_count_1'].tables != 1 or
result.databases['db_tables_count_2'].size is defined or
result.databases['db_tables_count_2'].tables != 2 or
result.databases['db_only_views'].size is defined or
result.databases['db_only_views'].tables != 1 or
'db_tables_count_empty' in result.databases | dict2items
| map(attribute='key')
# 'unsupported' element is passed to check that an unsupported value
# won't break anything (will be ignored regarding to the module's
# documentation).
- name: Mysql_info databases | Get all dbs fields with unsupported value
community.mysql.mysql_info:
filter:
- databases
exclude_fields:
- db_size
- unsupported
register: result
failed_when:
- >
result.databases['db_tables_count_1'].size is defined or
result.databases['db_tables_count_1'].tables != 1 or
result.databases['db_tables_count_2'].size is defined or
result.databases['db_tables_count_2'].tables != 2 or
result.databases['db_only_views'].size is defined or
result.databases['db_only_views'].tables != 1 or
'db_tables_count_empty' in result.databases | dict2items
| map(attribute='key')
- name: Mysql_info databases | Get all dbs fields except tables
community.mysql.mysql_info:
filter:
- databases
exclude_fields:
- db_table_count
register: result
failed_when:
- >
result.databases['db_tables_count_1'].size != 16384 or
result.databases['db_tables_count_1'].tables is defined or
result.databases['db_tables_count_2'].size != 32768 or
result.databases['db_tables_count_2'].tables is defined or
result.databases['db_only_views'].size != 0 or
result.databases['db_only_views'].tables is defined or
'db_tables_count_empty' in result.databases | dict2items
| map(attribute='key')
- name: Mysql_info databases | Get all dbs even empty ones
community.mysql.mysql_info:
filter:
- databases
return_empty_dbs: true
register: result
failed_when:
- >
result.databases['db_tables_count_1'].size != 16384 or
result.databases['db_tables_count_1'].tables != 1 or
result.databases['db_tables_count_2'].size != 32768 or
result.databases['db_tables_count_2'].tables != 2 or
result.databases['db_only_views'].size != 0 or
result.databases['db_only_views'].tables != 1 or
result.databases['db_tables_count_empty'].size != 0 or
result.databases['db_tables_count_empty'].tables != 0
- name: Mysql_info databases | Get all dbs even empty ones without size
community.mysql.mysql_info:
filter:
- databases
exclude_fields:
- db_size
return_empty_dbs: true
register: result
failed_when:
- >
result.databases['db_tables_count_1'].size is defined or
result.databases['db_tables_count_1'].tables != 1 or
result.databases['db_tables_count_2'].size is defined or
result.databases['db_tables_count_2'].tables != 2 or
result.databases['db_only_views'].size is defined or
result.databases['db_only_views'].tables != 1 or
result.databases['db_tables_count_empty'].size is defined or
result.databases['db_tables_count_empty'].tables != 0
# ================================== Cleanup ============================
- name: Mysql_info databases | Cleanup databases
community.mysql.mysql_db:
name:
- db_tables_count_empty
- db_tables_count_1
- db_tables_count_2
- db_only_views
state: absent

View file

@ -1,321 +0,0 @@
---
- module_defaults:
community.mysql.mysql_db: &mysql_defaults
login_user: "{{ mysql_user }}"
login_password: "{{ mysql_password }}"
login_host: "{{ mysql_host }}"
login_port: "{{ mysql_primary_port }}"
community.mysql.mysql_query: *mysql_defaults
community.mysql.mysql_info: *mysql_defaults
community.mysql.mysql_user: *mysql_defaults
block:
# ================================ Prepare ==============================
- name: Mysql_info users_info | Create databases
community.mysql.mysql_db:
name:
- users_info_db
- users_info_db2
- users_info_db3
state: present
- name: Mysql_info users_info | Create tables
community.mysql.mysql_query:
query:
- >-
CREATE TABLE IF NOT EXISTS users_info_db.t1
(id int, name varchar(9))
- >-
CREATE TABLE IF NOT EXISTS users_info_db.T_UPPER
(id int, name1 varchar(9), NAME2 varchar(9), Name3 varchar(9))
# I failed to create a procedure using community.mysql.mysql_query.
# Maybe it's because we must changed the delimiter.
- name: Mysql_info users_info | Create procedure SQL file
ansible.builtin.template:
src: files/users_info_create_procedure.sql
dest: /root/create_procedure.sql
owner: root
group: root
mode: '0700'
- name: Mysql_info users_info | Create a procedure
community.mysql.mysql_db:
name: all
state: import
target: /root/create_procedure.sql
# Use a query instead of mysql_user, because we want to catch differences
# at the end and a bug in mysql_user would be invisible to this tests
- name: Mysql_info users_info | Prepare common tests users
community.mysql.mysql_query:
query:
- >-
CREATE USER users_info_adm@'users_info.com' IDENTIFIED WITH
mysql_native_password AS '*6C387FC3893DBA1E3BA155E74754DA6682D04747'
- >
GRANT ALL ON *.* to users_info_adm@'users_info.com' WITH GRANT
OPTION
- >-
CREATE USER users_info_schema@'users_info.com' IDENTIFIED WITH
mysql_native_password AS '*6C387FC3893DBA1E3BA155E74754DA6682D04747'
- >-
GRANT SELECT, INSERT, UPDATE, DELETE ON users_info_db.* TO
users_info_schema@'users_info.com'
- >-
CREATE USER users_info_table@'users_info.com' IDENTIFIED WITH
mysql_native_password AS '*6C387FC3893DBA1E3BA155E74754DA6682D04747'
- >-
GRANT SELECT, INSERT, UPDATE ON users_info_db.t1 TO
users_info_table@'users_info.com'
- >-
CREATE USER users_info_col@'users_info.com' IDENTIFIED WITH
mysql_native_password AS '*6C387FC3893DBA1E3BA155E74754DA6682D04747'
WITH MAX_USER_CONNECTIONS 100
- >-
GRANT SELECT (id) ON users_info_db.t1 TO
users_info_col@'users_info.com'
- >-
CREATE USER users_info_proc@'users_info.com' IDENTIFIED WITH
mysql_native_password AS '*6C387FC3893DBA1E3BA155E74754DA6682D04747'
WITH MAX_USER_CONNECTIONS 2 MAX_CONNECTIONS_PER_HOUR 60
- >-
GRANT EXECUTE ON PROCEDURE users_info_db.get_all_items TO
users_info_proc@'users_info.com'
- >-
CREATE USER users_info_multi@'users_info.com' IDENTIFIED WITH
mysql_native_password AS '*6C387FC3893DBA1E3BA155E74754DA6682D04747'
- >-
GRANT SELECT ON mysql.* TO
users_info_multi@'users_info.com'
- >-
GRANT ALL ON users_info_db.* TO
users_info_multi@'users_info.com'
- >-
GRANT ALL ON users_info_db2.* TO
users_info_multi@'users_info.com'
- >-
GRANT ALL ON users_info_db3.* TO
users_info_multi@'users_info.com'
- >-
CREATE USER users_info_usage_only@'users_info.com' IDENTIFIED WITH
mysql_native_password AS '*6C387FC3893DBA1E3BA155E74754DA6682D04747'
- >-
GRANT USAGE ON *.* TO
users_info_usage_only@'users_info.com'
- >-
CREATE USER users_info_columns_uppercase@'users_info.com'
IDENTIFIED WITH mysql_native_password AS
'*6C387FC3893DBA1E3BA155E74754DA6682D04747'
- >-
GRANT SELECT,UPDATE(name1,NAME2,Name3) ON users_info_db.T_UPPER TO
users_info_columns_uppercase@'users_info.com'
- >-
CREATE USER users_info_multi_hosts@'%'
IDENTIFIED WITH mysql_native_password AS
'*6C387FC3893DBA1E3BA155E74754DA6682D04747'
- GRANT SELECT ON users_info_db.* TO users_info_multi_hosts@'%'
- >-
CREATE USER users_info_multi_hosts@'localhost'
IDENTIFIED WITH mysql_native_password AS
'*6C387FC3893DBA1E3BA155E74754DA6682D04747'
- >-
GRANT SELECT ON users_info_db.* TO
users_info_multi_hosts@'localhost'
- >-
CREATE USER users_info_multi_hosts@'host1'
IDENTIFIED WITH mysql_native_password AS
'*6C387FC3893DBA1E3BA155E74754DA6682D04747'
- GRANT SELECT ON users_info_db.* TO users_info_multi_hosts@'host1'
# Different password than the others users_info_multi_hosts
- >-
CREATE USER users_info_multi_hosts@'host2'
IDENTIFIED WITH mysql_native_password AS
'*CB3326D5279DE7915FE5D743232165EE887883CA'
- GRANT SELECT ON users_info_db.* TO users_info_multi_hosts@'host2'
- >-
CREATE USER users_info_tls_none@'host'
IDENTIFIED WITH mysql_native_password AS
'*CB3326D5279DE7915FE5D743232165EE887883CA' REQUIRE NONE
- GRANT SELECT ON users_info_db.* TO users_info_tls_none@'host'
- >-
CREATE USER users_info_tls_ssl@'host'
IDENTIFIED WITH mysql_native_password AS
'*CB3326D5279DE7915FE5D743232165EE887883CA' REQUIRE SSL
- GRANT SELECT ON users_info_db.* TO users_info_tls_ssl@'host'
- >-
CREATE USER users_info_tls_cipher@'host'
IDENTIFIED WITH mysql_native_password AS
'*CB3326D5279DE7915FE5D743232165EE887883CA'
REQUIRE CIPHER 'ECDH-RSA-AES256-SHA384'
- GRANT SELECT ON users_info_db.* TO users_info_tls_cipher@'host'
- >-
CREATE USER users_info_tls_x509@'host'
IDENTIFIED WITH mysql_native_password AS
'*CB3326D5279DE7915FE5D743232165EE887883CA' REQUIRE X509
- GRANT SELECT ON users_info_db.* TO users_info_tls_x509@'host'
- >-
CREATE USER users_info_tls_subject@'host'
IDENTIFIED WITH mysql_native_password AS
'*CB3326D5279DE7915FE5D743232165EE887883CA'
REQUIRE SUBJECT '/CN=Bob/O=MyDom/C=US/ST=Oregon/L=Portland'
- GRANT SELECT ON users_info_db.* TO users_info_tls_subject@'host'
- >-
CREATE USER users_info_tls_issuer@'host'
IDENTIFIED WITH mysql_native_password AS
'*CB3326D5279DE7915FE5D743232165EE887883CA'
REQUIRE ISSUER '/C=FI/ST=Somewhere/L=City/
O=CompanyX/CN=Bob/emailAddress=bob@companyx.com'
- GRANT SELECT ON users_info_db.* TO users_info_tls_issuer@'host'
- >-
CREATE USER users_info_tls_subject_issuer@'host'
IDENTIFIED WITH mysql_native_password AS
'*CB3326D5279DE7915FE5D743232165EE887883CA'
REQUIRE SUBJECT '/CN=Bob/O=MyDom/C=US/ST=Oregon/L=Portland'
AND ISSUER '/C=FI/ST=Somewhere/L=City/
O=CompanyX/CN=Bob/emailAddress=bob@companyx.com'
- >-
GRANT SELECT ON users_info_db.*
TO users_info_tls_subject_issuer@'host'
- >-
CREATE USER users_info_tls_sub_issu_ciph@'host'
IDENTIFIED WITH mysql_native_password AS
'*CB3326D5279DE7915FE5D743232165EE887883CA'
REQUIRE SUBJECT '/CN=Bob/O=MyDom/C=US/ST=Oregon/L=Portland'
AND ISSUER '/C=FI/ST=Somewhere/L=City/
O=CompanyX/CN=Bob/emailAddress=bob@companyx.com'
AND CIPHER 'ECDH-RSA-AES256-SHA384'
- >-
GRANT SELECT ON users_info_db.*
TO users_info_tls_sub_issu_ciph@'host'
- name: Mysql_info users_info | Prepare tests users for MariaDB
community.mysql.mysql_query:
query:
- >-
CREATE USER users_info_socket@'users_info.com' IDENTIFIED WITH
unix_socket
- GRANT ALL ON *.* to users_info_socket@'users_info.com'
when:
- db_engine == 'mariadb'
- name: Mysql_info users_info | Prepare tests users for MySQL
community.mysql.mysql_query:
query:
- >-
CREATE USER users_info_sha256@'users_info.com' IDENTIFIED WITH
sha256_password BY 'msandbox'
- GRANT ALL ON *.* to users_info_sha256@'users_info.com'
when:
- db_engine == 'mysql'
- name: Mysql_info users_info | Prepare tests users for MySQL 8+
community.mysql.mysql_query:
query:
- >-
CREATE USER users_info_caching_sha2@'users_info.com' IDENTIFIED WITH
caching_sha2_password BY 'msandbox'
- GRANT ALL ON *.* to users_info_caching_sha2@'users_info.com'
when:
- db_engine == 'mysql'
- db_version is version('8.0', '>=')
# ================================== Tests ==============================
- name: Mysql_info users_info | Collect users_info
community.mysql.mysql_info:
filter:
- users_info
register: result
- name: Mysql_info users_info | Recreate users from mysql_info result
community.mysql.mysql_user:
name: "{{ item.name }}"
host: "{{ item.host }}"
plugin: "{{ item.plugin | default(omit) }}"
plugin_auth_string: "{{ item.plugin_auth_string | default(omit) }}"
plugin_hash_string: "{{ item.plugin_hash_string | default(omit) }}"
tls_requires: "{{ item.tls_requires | default(omit) }}"
priv: "{{ item.priv | default(omit) }}"
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 }}"
register: recreate_users_result
failed_when:
- >-
recreate_users_result is changed or
recreate_users_result.msg != 'User unchanged'
when:
- item.name != 'root'
- item.name != 'mysql'
- item.name != 'mariadb.sys'
- item.name != 'mysql.sys'
- item.name != 'mysql.infoschema'
- item.name != 'mysql.session'
# ================================== Cleanup ============================
- name: Mysql_info users_info | Cleanup users_info
community.mysql.mysql_user:
name: "{{ item }}"
host_all: true
column_case_sensitive: true
state: absent
loop:
- users_info_adm
- users_info_schema
- users_info_table
- users_info_col
- users_info_proc
- users_info_multi
- users_info_db
- users_info_usage_only
- users_info_columns_uppercase
- users_info_multi_hosts
- users_info_tls_none
- users_info_tls_ssl
- users_info_tls_cipher
- users_info_tls_x509
- users_info_tls_subject
- users_info_tls_issuer
- users_info_tls_subject_issuer
- users_info_tls_sub_issu_ciph
- name: Mysql_info users_info | Cleanup databases
community.mysql.mysql_db:
name:
- users_info_db
- users_info_db2
- users_info_db3
state: absent
- name: Mysql_info users_info | Cleanup sql file for the procedure
ansible.builtin.file:
path: /root/create_procedure.sql
state: absent

View file

@ -5,7 +5,7 @@
#################################################################### ####################################################################
# Test code for mysql_info module # Test code for mysql_info module
# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) <andrew.a.klychkov@gmail.com> # 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) # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
################### ###################
@ -56,7 +56,6 @@
- result.databases != {} - result.databases != {}
- result.engines != {} - result.engines != {}
- result.users != {} - result.users != {}
- result.server_engine == 'MariaDB' or result.server_engine == 'MySQL'
- name: mysql_info - Test connector informations display - name: mysql_info - Test connector informations display
ansible.builtin.import_tasks: ansible.builtin.import_tasks:
@ -132,12 +131,91 @@
- result.global_status is not defined - result.global_status is not defined
- result.users is not defined - result.users is not defined
- include_tasks: issue-28.yml # Test exclude_fields: db_size
# 'unsupported' element is passed to check that an unsupported value
# won't break anything (will be ignored regarding to the module's documentation).
- name: Collect info about databases excluding their sizes
mysql_info:
<<: *mysql_params
filter:
- databases
exclude_fields:
- db_size
- unsupported
register: result
- name: Import tasks file to tests tables count in database filter - assert:
ansible.builtin.import_tasks: that:
file: filter_databases.yml - result is not changed
- result.databases != {}
- result.databases.mysql == {}
- name: Import tasks file to tests users_info filter ########################################################
ansible.builtin.import_tasks: # Issue #65727, empty databases must be in returned dict
file: filter_users_info.yml #
- name: Create empty database acme
mysql_db:
<<: *mysql_params
name: acme
- name: Collect info about databases
mysql_info:
<<: *mysql_params
filter:
- databases
return_empty_dbs: true
register: result
# Check acme is in returned dict
- assert:
that:
- result is not changed
- result.databases.acme.size == 0
- result.databases.mysql != {}
- name: Collect info about databases excluding their sizes
mysql_info:
<<: *mysql_params
filter:
- databases
exclude_fields:
- db_size
return_empty_dbs: true
register: result
# Check acme is in returned dict
- assert:
that:
- result is not changed
- result.databases.acme == {}
- result.databases.mysql == {}
- name: Remove acme database
mysql_db:
<<: *mysql_params
name: acme
state: absent
- include: issue-28.yml
# https://github.com/ansible-collections/community.mysql/issues/204
- name: Create database containing only views
mysql_db:
<<: *mysql_params
name: allviews
- name: Create view
mysql_query:
<<: *mysql_params
login_db: allviews
query: 'CREATE VIEW v_today (today) AS SELECT CURRENT_DATE'
- name: Fetch info
mysql_info:
<<: *mysql_params
register: result
- name: Check
assert:
that:
- result.databases.allviews.size == 0

View file

@ -6,4 +6,4 @@
# 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_tasks: issue-28.yml - include: issue-28.yml

View file

@ -1,6 +1,6 @@
--- ---
# Test code for mysql_query module # Test code for mysql_query module
# Copyright: (c) 2020, Andrew Klychkov (@Andersson007) <andrew.a.klychkov@gmail.com> # Copyright: (c) 2020, Andrew Klychkov (@Andersson007) <aaklychkov@mail.ru>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
- vars: - vars:
mysql_parameters: &mysql_params mysql_parameters: &mysql_params
@ -35,7 +35,6 @@
that: that:
- result is changed - result is changed
- result.executed_queries == ['CREATE TABLE {{ test_table1 }} (id int)'] - result.executed_queries == ['CREATE TABLE {{ test_table1 }} (id int)']
- result.execution_time_ms[0] > 0
- name: Insert test data - name: Insert test data
mysql_query: mysql_query:
@ -53,8 +52,6 @@
- result is changed - result is changed
- result.rowcount == [2, 1] - result.rowcount == [2, 1]
- result.executed_queries == ['INSERT INTO {{ test_table1 }} VALUES (1), (2)', 'INSERT INTO {{ test_table1 }} VALUES (3)'] - 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 }} - name: Check data in {{ test_table1 }}
mysql_query: mysql_query:
@ -366,27 +363,21 @@
- name: Assert that create table IF NOT EXISTS is not changed with pymysql - name: Assert that create table IF NOT EXISTS is not changed with pymysql
assert: assert:
that: that:
# PyMySQL driver throws a warning for version before 0.10.0 # PyMySQL driver throws a warning, so the following is correct
- result is not changed - result is not changed
when: when:
- connector_name == 'pymysql' - connector_name == 'pymysql'
- connector_version is version('0.10.0', '<')
# Issue https://github.com/ansible-collections/community.mysql/issues/268 # Issue https://github.com/ansible-collections/community.mysql/issues/268
- name: Assert that create table IF NOT EXISTS is changed with mysqlclient - name: Assert that create table IF NOT EXISTS is changed with mysqlclient
assert: assert:
that: that:
# Mysqlclient 2.0.1 and pymysql 0.10.0+ drivers throws no warning, # Mysqlclient 2.0.1, driver throws nothing with mysql, so it's
# so it's impossible to figure out if the state was changed or not. # impossible to figure out if the state was changed or not.
# We assume that it was for DDL queries by default in the code # We assume that it was for DDL queries by default in the code
- result is changed - result is changed
when: when:
- > - connector_name == 'mysqlclient'
connector_name == 'mysqlclient'
or (
connector_name == 'pymysql'
and connector_version is version('0.10.0', '>')
)
- name: Drop db {{ test_db }} - name: Drop db {{ test_db }}
mysql_query: mysql_query:

View file

@ -1,167 +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:
# start replica so it is available for testing
- name: Start replica
mysql_replication:
<<: *mysql_params
login_port: '{{ mysql_replica1_port }}'
mode: startreplica
register: result
- assert:
that:
- result is changed
- result.queries == ["START SLAVE"] or result.queries == ["START REPLICA"]
- name: Drop {{ user_name_1 }} if exists
mysql_user:
<<: *mysql_params
name: '{{ user_name_1 }}'
host: '{{ gateway_addr }}'
state: absent
ignore_errors: yes
# First test
# check if user creation works with force_context and is replicated
- name: create user with force_context
mysql_user:
<<: *mysql_params
name: "{{ user_name_1 }}"
host: '{{ gateway_addr }}'
password: "{{ user_password_1 }}"
priv: '*.*:ALL,GRANT'
force_context: yes
- name: attempt connection on replica1 with newly created user (expect success)
mysql_replication:
mode: getprimary
login_user: '{{ user_name_1 }}'
login_password: '{{ user_password_1 }}'
login_host: '{{ mysql_host }}'
login_port: '{{ mysql_replica1_port }}'
register: result
ignore_errors: yes
- assert:
that:
- result is succeeded
- name: Drop user
mysql_user:
<<: *mysql_params
name: '{{ user_name_1 }}'
host: '{{ gateway_addr }}'
state: absent
force_context: yes
- name: attempt connection on replica with freshly removed user (expect failure)
mysql_replication:
mode: getprimary
login_user: '{{ user_name_1 }}'
login_password: '{{ user_password_1 }}'
login_host: '{{ mysql_host }}'
login_port: '{{ mysql_replica1_port }}'
register: result
ignore_errors: yes
- assert:
that:
- result is failed
# Prepare replica1 for testing with a replication filter in place
# Stop replication, create a filter and restart replication on replica1.
- name: Stop replica
mysql_replication:
<<: *mysql_params
login_port: '{{ mysql_replica1_port }}'
mode: stopreplica
register: result
- assert:
that:
- result is changed
- result.queries == ["STOP SLAVE"] or result.queries == ["STOP REPLICA"]
- name: Create replication filter MySQL
shell: "echo \"CHANGE REPLICATION FILTER REPLICATE_IGNORE_DB = (mysql);\" | {{ mysql_command_wo_port }} -P{{ mysql_replica1_port }}"
when: db_engine == 'mysql'
- name: Create replication filter MariaDB
shell: "echo \"SET GLOBAL replicate_ignore_db = 'mysql';\" | {{ mysql_command_wo_port }} -P{{ mysql_replica1_port }}"
when: db_engine == 'mariadb'
- name: Start replica
mysql_replication:
<<: *mysql_params
login_port: '{{ mysql_replica1_port }}'
mode: startreplica
register: result
- assert:
that:
- result is changed
- result.queries == ["START SLAVE"] or result.queries == ["START REPLICA"]
# Second test
# Filter in place, ready to test if user creation is filtered with force_context
- name: create user with force_context
mysql_user:
<<: *mysql_params
name: "{{ user_name_1 }}"
host: "{{ gateway_addr }}"
password: "{{ user_password_1 }}"
priv: '*.*:ALL,GRANT'
force_context: yes
- name: attempt connection on replica with newly created user (expect failure)
mysql_replication:
mode: getprimary
login_user: '{{ user_name_1 }}'
login_password: '{{ user_password_1 }}'
login_host: '{{ mysql_host }}'
login_port: '{{ mysql_replica1_port }}'
register: result
ignore_errors: yes
- assert:
that:
- result is failed
- name: Drop user
mysql_user:
<<: *mysql_params
name: '{{ user_name_1 }}'
host: "{{ gateway_addr }}"
state: absent
force_context: yes
# restore normal replica1 operation
# Stop replication and remove the filter
- name: Stop replica
mysql_replication:
<<: *mysql_params
login_port: '{{ mysql_replica1_port }}'
mode: stopreplica
register: result
- assert:
that:
- result is changed
- result.queries == ["STOP SLAVE"] or result.queries == ["STOP REPLICA"]
- name: Remove replication filter MySQL
shell: "echo \"CHANGE REPLICATION FILTER REPLICATE_IGNORE_DB = ();\" | {{ mysql_command_wo_port }} -P{{ mysql_replica1_port }}"
when: db_engine == 'mysql'
- name: Remove replication filter MariaDB
shell: "echo \"SET GLOBAL replicate_ignore_db = '';\" | {{ mysql_command_wo_port }} -P{{ mysql_replica1_port }}"
when: db_engine == 'mariadb'

View file

@ -1,33 +1,24 @@
---
#################################################################### ####################################################################
# WARNING: These are designed specifically for Ansible tests # # WARNING: These are designed specifically for Ansible tests #
# and should not be used as examples of how to write Ansible roles # # and should not be used as examples of how to write Ansible roles #
#################################################################### ####################################################################
# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) <andrew.a.klychkov@gmail.com> # 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) # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# Initial CI tests of mysql_replication module: # Initial CI tests of mysql_replication module:
- import_tasks: mysql_replication_initial.yml - import_tasks: mysql_replication_initial.yml
# Tests of replication filters and force_context
- include_tasks: issue-265.yml
# Tests of primary_delay parameter: # Tests of primary_delay parameter:
- import_tasks: mysql_replication_primary_delay.yml - import_tasks: mysql_replication_primary_delay.yml
# Tests of channel parameter: # Tests of channel parameter:
- import_tasks: mysql_replication_channel.yml - import_tasks: mysql_replication_channel.yml
when: when:
- db_engine == 'mysql' # FIXME: mariadb introduces FOR CHANNEL in 10.7 - 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?
# Tests of resetprimary mode: # Tests of resetprimary mode:
- import_tasks: mysql_replication_resetprimary_mode.yml - import_tasks: mysql_replication_resetprimary_mode.yml
- include_tasks: issue-28.yml - include: issue-28.yml
# Tests of changereplication mode:
- import_tasks: mysql_replication_changereplication_mode.yml
when:
- db_engine == 'mysql'
- db_version is version('8.0.23', '>=')

View file

@ -1,65 +0,0 @@
---
- vars:
mysql_params: &mysql_params
login_user: '{{ mysql_user }}'
login_password: '{{ mysql_password }}'
login_host: '{{ mysql_host }}'
block:
# Get primary log file and log pos:
- name: Get primary status
mysql_replication:
<<: *mysql_params
login_port: '{{ mysql_primary_port }}'
mode: getprimary
register: mysql_primary_status
# Test changereplication mode:
- name: Run replication
mysql_replication:
<<: *mysql_params
login_port: '{{ mysql_replica1_port }}'
mode: changereplication
primary_host: '{{ mysql_host }}'
primary_port: '{{ mysql_primary_port }}'
primary_user: '{{ replication_user }}'
primary_password: '{{ replication_pass }}'
primary_log_file: '{{ mysql_primary_status.File }}'
primary_log_pos: '{{ mysql_primary_status.Position }}'
primary_ssl_ca: ''
primary_ssl: no
register: result
- name: Assert that changereplication is changed and return expected query
assert:
that:
- result is changed
- result.queries == expected_queries
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 changereplication mode with channel:
- name: Run replication
mysql_replication:
<<: *mysql_params
login_port: '{{ mysql_replica1_port }}'
mode: changereplication
primary_user: '{{ replication_user }}'
primary_password: '{{ replication_pass }}'
channel: '{{ test_channel }}'
register: with_channel_result_queries
- name: Assert that changereplication is changed and is called correctly with channel
assert:
that:
- with_channel_result_queries is changed
- with_channel_result_queries.queries == expected_queries
vars:
expected_queries: ["CHANGE REPLICATION SOURCE TO SOURCE_USER='{{ replication_user }}',\
SOURCE_PASSWORD='********' FOR CHANNEL '{{ test_channel }}'"]

View file

@ -1,5 +1,5 @@
--- ---
# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) <andrew.a.klychkov@gmail.com> # 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) # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
- vars: - vars:
@ -32,15 +32,10 @@
channel: '{{ test_channel }}' channel: '{{ test_channel }}'
register: result register: result
- name: Assert that run replication with channel is changed and query matches for MariaDB and MySQL < 8.0.23 - assert:
ansible.builtin.assert:
that: that:
- result is changed - result is changed
- result.queries == result_query - result.queries == result_query
when:
- >
db_engine == 'mariadb' or
(db_engine == 'mysql' and db_version is version('8.0.23', '<'))
vars: vars:
result_query: ["CHANGE MASTER TO MASTER_HOST='{{ mysql_host }}',\ result_query: ["CHANGE MASTER TO MASTER_HOST='{{ mysql_host }}',\
MASTER_USER='{{ replication_user }}',MASTER_PASSWORD='********',\ MASTER_USER='{{ replication_user }}',MASTER_PASSWORD='********',\
@ -48,21 +43,6 @@
'{{ mysql_primary_status.File }}',MASTER_LOG_POS=\ '{{ mysql_primary_status.File }}',MASTER_LOG_POS=\
{{ mysql_primary_status.Position }} FOR CHANNEL '{{ test_channel }}'"] {{ 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: # Test startreplica mode:
- name: Start replica with channel - name: Start replica with channel
mysql_replication: mysql_replication:
@ -103,10 +83,7 @@
mysql_host_value: '{{ mysql_host }}' mysql_host_value: '{{ mysql_host }}'
mysql_primary_port_value: '{{ mysql_primary_port }}' mysql_primary_port_value: '{{ mysql_primary_port }}'
test_channel_value: '{{ test_channel }}' test_channel_value: '{{ test_channel }}'
when: when: mysql8022_and_higher == false
- >
db_engine == 'mariadb' or
(db_engine == 'mysql' and db_version is version('8.0.22', '<'))
- assert: - assert:
that: that:
@ -122,9 +99,7 @@
mysql_host_value: '{{ mysql_host }}' mysql_host_value: '{{ mysql_host }}'
mysql_primary_port_value: '{{ mysql_primary_port }}' mysql_primary_port_value: '{{ mysql_primary_port }}'
test_channel_value: '{{ test_channel }}' test_channel_value: '{{ test_channel }}'
when: when: mysql8022_and_higher == true
- db_engine == 'mysql'
- db_version is version('8.0.22', '>=')
# Test stopreplica mode: # Test stopreplica mode:

View file

@ -1,5 +1,5 @@
--- ---
# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) <andrew.a.klychkov@gmail.com> # 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) # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
- vars: - vars:
@ -9,6 +9,16 @@
login_host: '{{ mysql_host }}' login_host: '{{ mysql_host }}'
block: 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: # We use iF NOT EXISTS because the GITHUB Action:
# "ansible-community/ansible-test-gh-action" uses "--retry-on-error". # "ansible-community/ansible-test-gh-action" uses "--retry-on-error".
@ -83,12 +93,11 @@
- mysql_primary_status is not changed - mysql_primary_status is not changed
# Test startreplica fails without changeprimary first. This needs fail_on_error # Test startreplica fails without changeprimary first. This needs fail_on_error
- name: Start replica and fail because primary is not specified; failing on error as requested - name: Start replica (using deprecated startslave choice) and fail because primary is not specified; failing on error as requested
mysql_replication: mysql_replication:
<<: *mysql_params <<: *mysql_params
login_port: '{{ mysql_replica1_port }}' login_port: '{{ mysql_replica1_port }}'
mode: startreplica mode: startslave
primary_use_gtid: replica_pos
fail_on_error: yes fail_on_error: yes
register: result register: result
ignore_errors: yes ignore_errors: yes
@ -126,10 +135,11 @@
that: that:
- result is not failed - result is not failed
# Test changeprimary mode:
# primary_ssl_ca will be set as '' to check the module's behaviour for #23976, # primary_ssl_ca will be set as '' to check the module's behaviour for #23976,
# must be converted to an empty string # must be converted to an empty string
- name: Test changeprimary mode with empty primary_ssl_ca - name: Run replication
community.mysql.mysql_replication: mysql_replication:
<<: *mysql_params <<: *mysql_params
login_port: '{{ mysql_replica1_port }}' login_port: '{{ mysql_replica1_port }}'
mode: changeprimary mode: changeprimary
@ -140,40 +150,19 @@
primary_log_file: '{{ mysql_primary_status.File }}' primary_log_file: '{{ mysql_primary_status.File }}'
primary_log_pos: '{{ mysql_primary_status.Position }}' primary_log_pos: '{{ mysql_primary_status.Position }}'
primary_ssl_ca: '' primary_ssl_ca: ''
primary_ssl: false
register: result register: result
- name: Assert that changeprimmary is changed and return expected query for MariaDB and MySQL < 8.0.23 - name: Assert that changeprimmary is changed and return expected query
ansible.builtin.assert: assert:
that: that:
- result is changed - result is changed
- result.queries == expected_queries - result.queries == expected_queries
when:
- >
db_engine == 'mariadb' or
(db_engine == 'mysql' and db_version is version('8.0.23', '<'))
vars: vars:
expected_queries: ["CHANGE MASTER TO MASTER_HOST='{{ mysql_host }}',\ expected_queries: ["CHANGE MASTER TO MASTER_HOST='{{ mysql_host }}',\
MASTER_USER='{{ replication_user }}',MASTER_PASSWORD='********',\ MASTER_USER='{{ replication_user }}',MASTER_PASSWORD='********',\
MASTER_PORT={{ mysql_primary_port }},MASTER_LOG_FILE=\ MASTER_PORT={{ mysql_primary_port }},MASTER_LOG_FILE=\
'{{ mysql_primary_status.File }}',MASTER_LOG_POS=\ '{{ mysql_primary_status.File }}',MASTER_LOG_POS=\
{{ mysql_primary_status.Position }},MASTER_SSL=0,MASTER_SSL_CA=''"] {{ mysql_primary_status.Position }},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: # Test startreplica mode:
- name: Start replica - name: Start replica
@ -190,11 +179,11 @@
- result.queries == ["START SLAVE"] or result.queries == ["START REPLICA"] - result.queries == ["START SLAVE"] or result.queries == ["START REPLICA"]
# Test getreplica mode: # Test getreplica mode:
- name: Get replica status - name: Get replica status using deprecated getslave choice
mysql_replication: mysql_replication:
<<: *mysql_params <<: *mysql_params
login_port: '{{ mysql_replica1_port }}' login_port: '{{ mysql_replica1_port }}'
mode: getreplica mode: getslave
register: replica_status register: replica_status
- name: Assert that getreplica returns expected values for MySQL older than 8.0.22 and Mariadb - name: Assert that getreplica returns expected values for MySQL older than 8.0.22 and Mariadb
@ -210,10 +199,7 @@
vars: vars:
mysql_host_value: "{{ mysql_host }}" mysql_host_value: "{{ mysql_host }}"
mysql_primary_port_value: "{{ mysql_primary_port }}" mysql_primary_port_value: "{{ mysql_primary_port }}"
when: when: mysql8022_and_higher is falsy(convert_bool=True)
- >
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 - name: Assert that getreplica returns expected values for MySQL newer than 8.0.22
assert: assert:
@ -228,9 +214,7 @@
vars: vars:
mysql_host_value: "{{ mysql_host }}" mysql_host_value: "{{ mysql_host }}"
mysql_primary_port_value: "{{ mysql_primary_port }}" mysql_primary_port_value: "{{ mysql_primary_port }}"
when: when: mysql8022_and_higher is truthy(convert_bool=True)
- db_engine == 'mysql'
- db_version is version('8.0.22', '>=')
# Create test table and add data to it: # Create test table and add data to it:
- name: Create test table - name: Create test table
@ -257,18 +241,13 @@
assert: assert:
that: that:
- replica_status.Exec_Master_Log_Pos != mysql_primary_status.Position - replica_status.Exec_Master_Log_Pos != mysql_primary_status.Position
when: when: mysql8022_and_higher == false
- >
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 - name: Assert that getreplica Log_Pos is different for MySQL newer than 8.0.22
assert: assert:
that: that:
- replica_status.Exec_Source_Log_Pos != mysql_primary_status.Position - replica_status.Exec_Source_Log_Pos != mysql_primary_status.Position
when: when: mysql8022_and_higher == true
- db_engine == 'mysql'
- db_version is version('8.0.22', '>=')
- name: Start replica that is already running - name: Start replica that is already running
mysql_replication: mysql_replication:
@ -278,21 +257,20 @@
fail_on_error: true fail_on_error: true
register: result register: result
# mysqlclient 2.0.1 and pymysql 0.10.0+ always return "changed" # mysqlclient 2.0.1 always return "changed"
- name: Assert that startreplica is not changed - name: Assert that startreplica is not changed
assert: assert:
that: that:
- result is not changed - result is not changed
when: when:
- connector_name == 'pymysql' - connector_name == 'pymysql'
- connector_version is version('0.10.0', '<')
# Test stopreplica mode: # Test stopreplica mode:
- name: Stop replica - name: Stop replica using deprecated stopslave choice
mysql_replication: mysql_replication:
<<: *mysql_params <<: *mysql_params
login_port: '{{ mysql_replica1_port }}' login_port: '{{ mysql_replica1_port }}'
mode: stopreplica mode: stopslave
register: result register: result
- name: Assert that stopreplica is changed and returns expected query - name: Assert that stopreplica is changed and returns expected query
@ -306,7 +284,7 @@
timeout: 2 timeout: 2
# Test stopreplica mode: # Test stopreplica mode:
# mysqlclient 2.0.1 and pymysql 0.10.0+ always return "changed" # mysqlclient 2.0.1 always return "changed"
- name: Stop replica that is no longer running - name: Stop replica that is no longer running
mysql_replication: mysql_replication:
<<: *mysql_params <<: *mysql_params
@ -321,21 +299,3 @@
- result is not changed - result is not changed
when: when:
- connector_name == 'pymysql' - connector_name == 'pymysql'
- connector_version is version('0.10.0', '<')
# master / slave related choices were removed in 3.0.0
# https://github.com/ansible-collections/community.mysql/pull/252
- name: Test invoking the module with unsupported choice
mysql_replication:
<<: *mysql_params
login_port: '{{ mysql_replica1_port }}'
mode: stopslave
fail_on_error: true
register: result
ignore_errors: yes
- name: Assert that stopslave returns expected error message
assert:
that:
- result.msg == "value of mode must be one of{{ ":" }} getprimary, getreplica, changeprimary, stopreplica, startreplica, resetprimary, resetreplica, resetreplicaall, changereplication, got{{ ":" }} stopslave"
- result is failed

View file

@ -1,4 +1,4 @@
# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) <andrew.a.klychkov@gmail.com> # 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) # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
- vars: - vars:
@ -18,24 +18,10 @@
primary_delay: '{{ test_primary_delay }}' primary_delay: '{{ test_primary_delay }}'
register: result register: result
- name: Assert that run replication is changed and query match expectation for MariaDB and MySQL < 8.0.23 - assert:
ansible.builtin.assert:
that: that:
- result is changed - result is changed
- result.queries == ["CHANGE MASTER TO MASTER_DELAY=60"] - 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: # Auxiliary step:
- name: Start replica - name: Start replica

View file

@ -1,5 +1,4 @@
--- # Copyright: (c) 2019, Andrew Klychkov (@Andersson007) <aaklychkov@mail.ru>
# 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) # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
- vars: - vars:
@ -39,24 +38,10 @@
mode: resetprimary mode: resetprimary
register: result register: result
- name: Assert that reset primary is changed and query matches for MariaDB and MySQL < 8.4 - assert:
ansible.builtin.assert:
that: that:
- result is changed - result is changed
- result.queries == ["RESET MASTER"] - 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: # Get primary final status:
- name: Get primary status - name: Get primary status

View file

@ -9,16 +9,3 @@
- include_tasks: mysql_role_initial.yml - include_tasks: mysql_role_initial.yml
when: when:
- db_engine == 'mysql' - db_engine == 'mysql'
# Test that subtract_privs will only revoke the grants given by priv
# (https://github.com/ansible-collections/community.mysql/issues/331)
- include_tasks: test_priv_subtract.yml
vars:
enable_check_mode: no
- include_tasks: test_priv_subtract.yml
vars:
enable_check_mode: yes
- name: Test column case sensitive
ansible.builtin.import_tasks:
file: test_column_case_sensitive.yml

View file

@ -1283,75 +1283,6 @@
that: that:
- "'role3' not in result.query_result.0.0" - "'role3' not in result.query_result.0.0"
# test members_must_exist
- name: Try failing on not-existing user in check-mode
mysql_role:
<<: *mysql_params
name: 'role0'
state: present
members_must_exist: yes
append_members: yes
members:
- 'not_existent@%'
register: result
ignore_errors: yes
check_mode: yes
- name: Assert nonexistent user in check-mode is failed
assert:
that:
- result is failed
- name: Try failing on not-existing user in check-mode
mysql_role:
<<: *mysql_params
name: 'role0'
state: present
members_must_exist: no
append_members: yes
members:
- 'not_existent@%'
register: result
check_mode: yes
- name: Check for lack of change
assert:
that:
- result is not changed
- name: Try failing on not-existing user
mysql_role:
<<: *mysql_params
name: 'role0'
state: present
members_must_exist: yes
append_members: yes
members:
- 'not_existent@%'
register: result
ignore_errors: yes
- name: Assert nonexistent user with members_must_exist is failed
assert:
that:
- result is failed
- name: Try failing on not-existing user
mysql_role:
<<: *mysql_params
name: 'role0'
state: present
members_must_exist: no
append_members: yes
members:
- 'not_existent@%'
register: result
- name: Assert nonexistent user with members_must_exist=no is not changed
assert:
that:
- result is not changed
# ########## # ##########
# Test privs # Test privs
# ########## # ##########
@ -1495,6 +1426,7 @@
that: that:
- result is changed - result is changed
# TODO This is not idempotent on MySQL 8+ and MariaDB 10.5+
- name: Append privs again - name: Append privs again
mysql_role: mysql_role:
<<: *mysql_params <<: *mysql_params
@ -1508,6 +1440,10 @@
assert: assert:
that: that:
- result is not changed - result is not changed
when:
# TODO remove this when stable-2 role append priv is fixed
- (db_engine == 'mysql' and db_version is version('5.7', '<=')
or db_engine == 'mariadb' and db_version is version('10.4', '<='))
- name: Rewrite privs - name: Rewrite privs
mysql_role: mysql_role:

View file

@ -1,149 +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 =======================================
# We use query to prevent our module of changing the case
- name: Mysql_role Column case sensitive | Create a test table
community.mysql.mysql_query:
<<: *mysql_params
query:
- CREATE DATABASE mysql_role_column_case
- >-
CREATE TABLE mysql_role_column_case.t1
(a int, B int, cC int, Dd int)
- >-
INSERT INTO mysql_role_column_case.t1
(a, B, cC, Dd) VALUES (1,2,3,4)
- name: Mysql_role Column case sensitive | Create users
community.mysql.mysql_user:
<<: *mysql_params
name: column_case_sensitive
host: '%'
password: 'msandbox'
# ================= Reproduce failure =====================================
- name: Mysql_role Column case sensitive | Create role
community.mysql.mysql_role:
<<: *mysql_params
name: 'role_column_case_sensitive'
state: present
members:
- 'column_case_sensitive@%'
priv:
'mysql_role_column_case.t1': 'SELECT(a, B, cC, Dd)'
- name: Mysql_role Column case sensitive | Assert role privileges are all caps
community.mysql.mysql_query:
<<: *mysql_params
query:
- SHOW GRANTS FOR role_column_case_sensitive
register: column_case_insensitive_grants
failed_when:
# Column order may vary, thus test each separately
- >-
column_case_insensitive_grants.query_result[0][1]
is not search("A", ignorecase=false)
or column_case_insensitive_grants.query_result[0][1]
is not search("B", ignorecase=false)
or column_case_insensitive_grants.query_result[0][1]
is not search("CC", ignorecase=false)
or column_case_insensitive_grants.query_result[0][1]
is not search("DD", ignorecase=false)
- name: Mysql_role Column case sensitive | Assert 1 column is accessible on MySQL
community.mysql.mysql_query:
<<: *mysql_params
login_user: column_case_sensitive
query:
- DESC mysql_role_column_case.t1
register: assert_1_col_accessible
failed_when:
- assert_1_col_accessible.rowcount[0] | int != 1
when:
- db_engine == 'mysql'
- name: Mysql_role Column case sensitive | Assert 4 column are accessible on MariaDB
community.mysql.mysql_query:
<<: *mysql_params
login_user: column_case_sensitive
query:
- SET ROLE role_column_case_sensitive
- DESC mysql_role_column_case.t1
register: assert_4_col_accessible
failed_when:
- assert_4_col_accessible.rowcount[1] | int != 4
when:
- db_engine == 'mariadb'
# ====================== Test the fix =====================================
- name: Mysql_role Column case sensitive | Recreate role with case sensitive
community.mysql.mysql_role:
<<: *mysql_params
name: 'role_column_case_sensitive'
state: present
members:
- 'column_case_sensitive@%'
priv:
'mysql_role_column_case.t1': 'SELECT(a, B, cC, Dd)'
column_case_sensitive: true
- name: Mysql_role Column case sensitive | Assert role privileges are case sensitive
community.mysql.mysql_query:
<<: *mysql_params
query:
- SHOW GRANTS FOR role_column_case_sensitive
register: column_case_sensitive_grants
failed_when:
# Column order may vary, thus test each separately
- >-
column_case_sensitive_grants.query_result[0][1]
is not search("a", ignorecase=false)
or column_case_sensitive_grants.query_result[0][1]
is not search("B", ignorecase=false)
or column_case_sensitive_grants.query_result[0][1]
is not search("cC", ignorecase=false)
or column_case_sensitive_grants.query_result[0][1]
is not search("Dd", ignorecase=false)
- name: Mysql_role Column case sensitive | Assert 4 columns are accessible
community.mysql.mysql_query:
<<: *mysql_params
login_user: column_case_sensitive
query:
- SET ROLE role_column_case_sensitive
- DESC mysql_role_column_case.t1
register: assert_4_col_accessible
failed_when:
- assert_4_col_accessible.rowcount[1] | int != 4
# ========================= Teardown ======================================
- name: Mysql_role Column case sensitive | Delete test users
community.mysql.mysql_user:
<<: *mysql_params
name: column_case_sensitive
host_all: true
state: absent
- name: Mysql_role Column case sensitive | Delete role
community.mysql.mysql_role:
<<: *mysql_params
name: 'role_column_case_sensitive'
state: absent
- name: Mysql_role Column case sensitive | Delete test database
community.mysql.mysql_db:
<<: *mysql_params
name: mysql_role_column_case
state: absent

View file

@ -15,7 +15,7 @@
dest: /tmp/cert.pem dest: /tmp/cert.pem
delegate_to: localhost delegate_to: localhost
- name: Issue-121 | Drop mysql user if exists - name: Issue-121 | Setup | Drop mysql user if exists
mysql_user: mysql_user:
<<: *mysql_params <<: *mysql_params
name: '{{ item }}' name: '{{ item }}'
@ -26,42 +26,80 @@
- "{{ user_name_1 }}" - "{{ user_name_1 }}"
- "{{ user_name_2 }}" - "{{ user_name_2 }}"
- name: Issue-121 | Create user with REQUIRESSL privilege (expect failure) - name: Issue-121 | Create user with REQUIRESSL privilege
mysql_user:
<<: *mysql_params
name: "{{ user_name_1 }}"
password: "{{ user_password_1 }}"
priv: '*.*:SELECT,CREATE USER,REQUIRESSL,GRANT'
- name: Issue-121 | Verify REQUIRESSL is assigned to the user
mysql_query:
<<: *mysql_params
query: "SHOW CREATE USER '{{ user_name_1}}'@'localhost'"
register: result
- name: Issue-121 | Assert that requiressl is assigned to the user
assert:
that:
- result is succeeded and 'REQUIRE SSL' in (result.query_result | string)
- name: Issue-121 | Create user with equivalent ssl requirement in tls_requires (expect unchanged)
mysql_user:
<<: *mysql_params
name: "{{ user_name_1 }}"
password: "{{ user_password_1 }}"
priv: '*.*:SELECT,CREATE USER,GRANT'
tls_requires:
SSL:
register: result
- name: Issue-121 | Assert that create user with equivalent ssl is not changed
assert:
that:
- result is not changed
- name: Issue-121 | Create the same user again, with REQUIRESSL privilege once more
mysql_user: mysql_user:
<<: *mysql_params <<: *mysql_params
name: "{{ user_name_1 }}" name: "{{ user_name_1 }}"
password: "{{ user_password_1 }}" password: "{{ user_password_1 }}"
priv: '*.*:SELECT,CREATE USER,REQUIRESSL,GRANT' priv: '*.*:SELECT,CREATE USER,REQUIRESSL,GRANT'
register: result register: result
ignore_errors: true
- name: Issue-121 | Assert error granting privileges - name: Issue-121 | Assert error granting privileges
assert: assert:
that: that:
- result is failed - result is not changed
- result.msg is search('Error granting privileges')
- name: >- # Disabled on stable-2
Issue-121 | Create user with both REQUIRESSL privilege and an incompatible # This test makes no sense to me and the second task is changed making the
tls_requires option # assertion fails.
mysql_user: # - name: >-
<<: *mysql_params # Issue-121 | Create user with both REQUIRESSL privilege and an incompatible
name: "{{ user_name_1 }}" # tls_requires option
host: '{{ gateway_addr }}' # mysql_user:
password: "{{ user_password_1 }}" # <<: *mysql_params
priv: '*.*:SELECT,CREATE USER,REQUIRESSL,GRANT' # name: "{{ user_name_1 }}"
tls_requires: # host: '{{ gateway_addr }}'
X509: # password: "{{ user_password_1 }}"
register: result # priv: '*.*:SELECT,CREATE USER,REQUIRESSL,GRANT'
ignore_errors: true # tls_requires:
# X509:
- name: >- # - name: Issue-121 | Create same user again without REQUIRESSL privilege
Issue-121 | Assert error granting privileges with incompatible tls_requires # mysql_user:
option # <<: *mysql_params
assert: # name: "{{ user_name_1 }}"
that: # password: "{{ user_password_1 }}"
- result is failed # priv: '*.*:SELECT,CREATE USER,GRANT'
- result.msg is search('Error granting privileges') # tls_requires:
# X509:
# register: result
# - name: Issue-121 | Assert that create same user again without REQUIRESSL is not changed
# assert:
# that: result is not changed
- name: Issue-121 | Teardown | Drop mysql user - name: Issue-121 | Teardown | Drop mysql user
mysql_user: mysql_user:

View file

@ -1,181 +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:
- name: Issue-265 | Drop mysql user if exists
mysql_user:
<<: *mysql_params
name: '{{ user_name_1 }}'
host_all: true
state: absent
ignore_errors: yes
# Tests with force_context: yes
# Test user creation
- name: Issue-265 | Create mysql user {{ user_name_1 }}
mysql_user:
<<: *mysql_params
name: "{{ user_name_1 }}"
password: "{{ user_password_1 }}"
state: present
force_context: yes
register: result
- name: Issue-265 | Assert user was created
assert:
that:
- result is changed
- include_tasks: utils/assert_user.yml
vars:
user_name: "{{ user_name_1 }}"
user_host: localhost
# Test user removal
- name: Issue-265 | remove mysql user {{ user_name_1 }}
mysql_user:
<<: *mysql_params
name: "{{ user_name_1 }}"
host_all: true
password: "{{ user_password_1 }}"
state: absent
force_context: yes
register: result
- name: Issue-265 | Assert user was removed
assert:
that:
- result is changed
# Test blank user removal
- name: Issue-265 | Create blank mysql user to be removed later
mysql_user:
<<: *mysql_params
name: ""
state: present
force_context: yes
password: 'KJFDY&D*Sfuydsgf'
- name: Issue-265 | Remove blank mysql user with hosts=all (expect changed)
mysql_user:
<<: *mysql_params
name: ""
host_all: true
state: absent
force_context: yes
register: result
- name: Issue-265 | Assert changed is true for removing all blank users
assert:
that:
- result is changed
- name: Issue-265 | Remove blank mysql user with hosts=all (expect ok)
mysql_user:
<<: *mysql_params
name: ""
host_all: true
force_context: yes
state: absent
register: result
- name: Issue-265 | Assert changed is true for removing all blank users
assert:
that:
- result is not changed
- include_tasks: utils/assert_no_user.yml
vars:
user_name: "{{user_name_1}}"
# Tests with force_context: no
# Test user creation
- name: Issue-265 | Drop mysql user if exists
mysql_user:
<<: *mysql_params
name: "{{ user_name_1 }}"
host_all: true
state: absent
ignore_errors: yes
# Tests with force_context: yes
# Test user creation
- name: Issue-265 | Create mysql user {{user_name_1}}
mysql_user:
<<: *mysql_params
name: "{{ user_name_1 }}"
password: "{{ user_password_1 }}"
state: present
force_context: yes
register: result
- name: Issue-265 | Assert output message mysql user was created
assert:
that:
- result is changed
- include_tasks: utils/assert_user.yml
vars:
user_name: "{{ user_name_1 }}"
user_host: localhost
# Test user removal
- name: Issue-265 | Remove mysql user {{ user_name_1 }}
mysql_user:
<<: *mysql_params
name: "{{ user_name_1 }}"
password: "{{ user_password_1 }}"
state: absent
force_context: no
register: result
- name: Issue-265 | Assert output message mysql user was removed
assert:
that:
- result is changed
# Test blank user removal
- name: Issue-265 | Create blank mysql user to be removed later
mysql_user:
<<: *mysql_params
name: ""
state: present
force_context: no
password: 'KJFDY&D*Sfuydsgf'
- name: Issue-265 | Remove blank mysql user with hosts=all (expect changed)
mysql_user:
<<: *mysql_params
name: ""
host_all: true
state: absent
force_context: no
register: result
- name: Issue-265 | Assert changed is true for removing all blank users
assert:
that:
- result is changed
- name: Issue-265 | Remove blank mysql user with hosts=all (expect ok)
mysql_user:
<<: *mysql_params
name: ""
host_all: true
force_context: no
state: absent
register: result
- name: Issue-265 | Assert changed is true for removing all blank users
assert:
that:
- result is not changed
- include_tasks: utils/assert_no_user.yml
vars:
user_name: "{{ user_name_1 }}"

View file

@ -79,6 +79,4 @@
- foo - foo
- bar - bar
- include_tasks: utils/remove_user.yml - include: utils/remove_user.yml user_name="{{ user_name_2 }}"
vars:
user_name: "{{ user_name_2 }}"

View file

@ -1,112 +0,0 @@
---
# Due to https://bugs.mysql.com/bug.php?id=115953, in Mysql 8, if ANSI_QUOTES is enabled,
# backticks will be used instead of double quotes to quote functions or procedures name.
# As a consequence, mysql_user and mysql_roles will always report "changed" for functions
# and procedures no matter the privileges are granted or not.
# Workaround for the mysql bug 116953 is removing ANSI_QUOTES from the module's session
# sql_mode. But because issue 671, ANSI_QUOTES is always got from GLOBAL sql_mode, thus
# this workaround can't work. Even without the Mysql bug, because sql_mode in session
# precedes GLOBAL sql_mode. we should check for sql_mode in session variable instead of
# the GLOBAL one.
- vars:
mysql_parameters: &mysql_params
login_user: '{{ mysql_user }}'
login_password: '{{ mysql_password }}'
login_host: '{{ mysql_host }}'
login_port: '{{ mysql_primary_port }}'
block:
- name: Issue-671| test setup | drop database
community.mysql.mysql_db:
<<: *mysql_params
name: "{{ item }}"
state: absent
loop:
- foo
- bar
- name: Issue-671| test setup | create database
community.mysql.mysql_db:
<<: *mysql_params
name: "{{ item }}"
state: present
loop:
- foo
- bar
- name: Issue-671| test setup | get value of GLOBAL.sql_mode
community.mysql.mysql_query:
<<: *mysql_params
query: 'select @@GLOBAL.sql_mode AS sql_mode'
register: sql_mode_orig
- name: Issue-671| Assert sql_mode_orig
ansible.builtin.assert:
that:
- sql_mode_orig.query_result[0][0].sql_mode != None
- name: Issue-671| enable sql_mode ANSI_QUOTES
community.mysql.mysql_variables:
<<: *mysql_params
variable: sql_mode
value: '{{ sql_mode_orig.query_result[0][0].sql_mode }},ANSI_QUOTES'
mode: "{% if db_engine == 'mariadb' %}global{% else %}persist{% endif %}"
- name: Issue-671| Copy SQL scripts to remote
ansible.builtin.copy:
src: "{{ item }}"
dest: "{{ remote_tmp_dir }}/{{ item | basename }}"
loop:
- create-function.sql
- create-procedure.sql
- name: Issue-671| Create function for test
ansible.builtin.shell:
cmd: "{{ mysql_command }} < {{ remote_tmp_dir }}/create-function.sql"
- name: Issue-671| Create procedure for test
ansible.builtin.shell:
cmd: "{{ mysql_command }} < {{ remote_tmp_dir }}/create-procedure.sql"
- name: Issue-671| Create user with FUNCTION and PROCEDURE privileges
community.mysql.mysql_user:
<<: *mysql_params
name: '{{ user_name_2 }}'
password: '{{ user_password_2 }}'
state: present
priv: 'FUNCTION foo.function:EXECUTE/foo.*:SELECT/PROCEDURE bar.procedure:EXECUTE'
- name: Issue-671| Grant the privileges again, remove ANSI_QUOTES from the session variable
community.mysql.mysql_user:
<<: *mysql_params
session_vars:
sql_mode: ""
name: '{{ user_name_2 }}'
password: '{{ user_password_2 }}'
state: present
priv: 'FUNCTION foo.function:EXECUTE/foo.*:SELECT/PROCEDURE bar.procedure:EXECUTE'
register: result
failed_when:
- result is failed or result is changed
- name: Issue-671| Test teardown | cleanup databases
community.mysql.mysql_db:
<<: *mysql_params
name: "{{ item }}"
state: absent
loop:
- foo
- bar
- name: Issue-671| set sql_mode back to original value
community.mysql.mysql_variables:
<<: *mysql_params
variable: sql_mode
value: '{{ sql_mode_orig.query_result[0][0].sql_mode }}'
mode: "{% if db_engine == 'mariadb' %}global{% else %}persist{% endif %}"
- name: Issue-671| Teardown user_name_2
ansible.builtin.include_tasks:
file: utils/remove_user.yml
vars:
user_name: "{{ user_name_2 }}"

View file

@ -35,15 +35,13 @@
block: block:
- include_tasks: issue-121.yml - include: issue-121.yml
- include_tasks: issue-28.yml - include: issue-28.yml
- include_tasks: test_resource_limits.yml - include: test_resource_limits.yml
- include_tasks: test_idempotency.yml - include: test_idempotency.yml
- include_tasks: test_password_expire.yml
# ============================================================ # ============================================================
# Create user with no privileges and verify default privileges are assign # Create user with no privileges and verify default privileges are assign
@ -56,20 +54,11 @@
state: present state: present
register: result register: result
- include_tasks: utils/assert_user.yml - include: utils/assert_user.yml user_name={{ user_name_1 }} user_host=localhost priv=USAGE
vars:
user_name: "{{ user_name_1 }}"
user_host: localhost
priv: USAGE
- include_tasks: utils/remove_user.yml - include: utils/remove_user.yml user_name={{ user_name_1 }}
vars:
user_name: "{{ user_name_1 }}"
- include_tasks: utils/assert_no_user.yml
vars:
user_name: "{{ user_name_1 }}"
- include: utils/assert_no_user.yml user_name={{ user_name_1 }}
# ============================================================ # ============================================================
# Create user with select privileges and verify select privileges are assign # Create user with select privileges and verify select privileges are assign
@ -83,20 +72,11 @@
priv: '*.*:SELECT' priv: '*.*:SELECT'
register: result register: result
- include_tasks: utils/assert_user.yml - include: utils/assert_user.yml user_name={{ user_name_2 }} user_host=localhost priv=SELECT
vars:
user_name: "{{ user_name_2 }}"
user_host: localhost
priv: SELECT
- include_tasks: utils/remove_user.yml - include: utils/remove_user.yml user_name={{ user_name_2 }}
vars:
user_name: "{{ user_name_2 }}"
- include_tasks: utils/assert_no_user.yml
vars:
user_name: "{{ user_name_2 }}"
- include: utils/assert_no_user.yml user_name={{ user_name_2 }}
# ============================================================ # ============================================================
# Assert user has access to multiple databases # Assert user has access to multiple databases
@ -132,13 +112,9 @@
- item in result.stdout - item in result.stdout
loop: "{{db_names}}" loop: "{{db_names}}"
- include_tasks: utils/remove_user.yml - include: utils/remove_user.yml user_name={{ user_name_1 }}
vars:
user_name: "{{ user_name_1 }}"
- include_tasks: utils/remove_user.yml - include: utils/remove_user.yml user_name={{ user_name_2 }}
vars:
user_name: "{{ user_name_2 }}"
- name: Give user SELECT access to database via wildcard - name: Give user SELECT access to database via wildcard
mysql_user: mysql_user:
@ -196,84 +172,50 @@
- "'%db' in result.stdout" - "'%db' in result.stdout"
- "'INSERT' in result.stdout" - "'INSERT' in result.stdout"
- include_tasks: utils/remove_user.yml - include: utils/remove_user.yml user_name={{user_name_1}}
vars:
user_name: "{{user_name_1}}"
# ============================================================ # ============================================================
# Test plaintext and encrypted password scenarios. # Test plaintext and encrypted password scenarios.
# #
- include_tasks: test_user_password.yml - include: test_user_password.yml
# ============================================================ # ============================================================
# Test plugin authentication scenarios. # Test plugin authentication scenarios.
# #
# FIXME: mariadb sql syntax for create/update user is not compatible # FIXME: mariadb sql syntax for create/update user is not compatible
- include_tasks: test_user_plugin_auth.yml - include: test_user_plugin_auth.yml
when: db_engine == 'mysql' when: db_engine == 'mysql'
# ============================================================ # ============================================================
# Assert create user with SELECT privileges, attempt to create database and update privileges to create database # Assert create user with SELECT privileges, attempt to create database and update privileges to create database
# #
- include_tasks: test_privs.yml - include: test_privs.yml current_privilege=SELECT current_append_privs=no
vars:
current_privilege: SELECT
current_append_privs: no
# ============================================================ # ============================================================
# Assert creating user with SELECT privileges, attempt to create database and append privileges to create database # Assert creating user with SELECT privileges, attempt to create database and append privileges to create database
# #
- include_tasks: test_privs.yml - include: test_privs.yml current_privilege=DROP current_append_privs=yes
vars:
current_privilege: DROP
current_append_privs: yes
# ============================================================ # ============================================================
# Assert create user with SELECT privileges, attempt to create database and update privileges to create database # Assert create user with SELECT privileges, attempt to create database and update privileges to create database
# #
- include_tasks: test_privs.yml - include: test_privs.yml current_privilege='UPDATE,ALTER' current_append_privs=no
vars:
current_privilege: 'UPDATE,ALTER'
current_append_privs: no
# ============================================================ # ============================================================
# Assert creating user with SELECT privileges, attempt to create database and append privileges to create database # Assert creating user with SELECT privileges, attempt to create database and append privileges to create database
# #
- include_tasks: test_privs.yml - include: test_privs.yml current_privilege='INSERT,DELETE' current_append_privs=yes
vars:
current_privilege: 'INSERT,DELETE'
current_append_privs: yes
# Tests for the priv parameter with dict value (https://github.com/ansible/ansible/issues/57533) # Tests for the priv parameter with dict value (https://github.com/ansible/ansible/issues/57533)
- include_tasks: test_priv_dict.yml - include: test_priv_dict.yml
# Test that append_privs will not attempt to make a change where current privileges are a superset of new privileges # Test that append_privs will not attempt to make a change where current privileges are a superset of new privileges
# (https://github.com/ansible-collections/community.mysql/issues/69) # (https://github.com/ansible-collections/community.mysql/issues/69)
- include_tasks: test_priv_append.yml - include: test_priv_append.yml enable_check_mode=no
vars: - include: test_priv_append.yml enable_check_mode=yes
enable_check_mode: no
- include_tasks: test_priv_append.yml
vars:
enable_check_mode: yes
# Test that subtract_privs will only revoke the grants given by priv
# (https://github.com/ansible-collections/community.mysql/issues/331)
- include_tasks: test_priv_subtract.yml
vars:
enable_check_mode: no
- include_tasks: test_priv_subtract.yml
vars:
enable_check_mode: yes
- import_tasks: test_privs_issue_465.yml
tags:
- issue_465
# Tests for user attributes
- include_tasks: test_user_attributes.yml
# Tests for the TLS requires dictionary # Tests for the TLS requires dictionary
- include_tasks: test_tls_requirements.yml - include: test_tls_requirements.yml
- import_tasks: issue-29511.yaml - import_tasks: issue-29511.yaml
tags: tags:
@ -282,30 +224,6 @@
- import_tasks: issue-64560.yaml - import_tasks: issue-64560.yaml
tags: tags:
- issue-64560 - issue-64560
- name: Test ANSI_QUOTES
ansible.builtin.import_tasks:
file: issue-671.yaml
tags:
- issue-671
# Test that mysql_user still works with force_context enabled (database set to "mysql")
# (https://github.com/ansible-collections/community.mysql/issues/265)
- include_tasks: issue-265.yml
# https://github.com/ansible-collections/community.mysql/issues/231 # https://github.com/ansible-collections/community.mysql/issues/231
- include_tasks: test_user_grants_with_roles_applied.yml - include: test_user_grants_with_roles_applied.yml
- include_tasks: test_revoke_only_grant.yml
- name: Mysql_user - test column case sensitive
ansible.builtin.import_tasks:
file: test_column_case_sensitive.yml
- 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,134 +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 =======================================
# We use query to prevent our module of changing the case
- name: Mysql_user Column case sensitive | Create a test table
community.mysql.mysql_query:
<<: *mysql_params
query:
- CREATE DATABASE mysql_user_column_case
- >-
CREATE TABLE mysql_user_column_case.t1
(a int, B int, cC int, Dd int)
- >-
INSERT INTO mysql_user_column_case.t1
(a, B, cC, Dd) VALUES (1,2,3,4)
# ================= Reproduce failure =====================================
- name: Mysql_user Column case sensitive | Create test user
community.mysql.mysql_user:
<<: *mysql_params
name: column_case_sensitive
host: '%'
password: 'msandbox'
priv:
'mysql_user_column_case.t1': 'SELECT(a, B, cC, Dd)'
- name: Mysql_user Column case sensitive | Assert user privileges are all caps
community.mysql.mysql_query:
<<: *mysql_params
query:
- SHOW GRANTS FOR column_case_sensitive@'%'
register: column_case_insensitive_grants
failed_when:
# Column order may vary, thus test each separately
- >-
column_case_insensitive_grants.query_result[0][1]
is not search("A", ignorecase=false)
or column_case_insensitive_grants.query_result[0][1]
is not search("B", ignorecase=false)
or column_case_insensitive_grants.query_result[0][1]
is not search("CC", ignorecase=false)
or column_case_insensitive_grants.query_result[0][1]
is not search("DD", ignorecase=false)
- name: Mysql_user Column case sensitive | Assert 1 column is accessible on MySQL 5.7
community.mysql.mysql_query:
<<: *mysql_params
login_user: column_case_sensitive
query:
- DESC mysql_user_column_case.t1
register: assert_1_col_accessible
failed_when:
- assert_1_col_accessible.rowcount[0] | int != 1
when:
- db_engine == 'mysql' and db_version is version('5.7', '<=')
- name: Mysql_user Column case sensitive | Assert 4 column are accessible on MariaDB and MySQL 8+
community.mysql.mysql_query:
<<: *mysql_params
login_user: column_case_sensitive
query:
- DESC mysql_user_column_case.t1
register: assert_4_col_accessible
failed_when:
- assert_4_col_accessible.rowcount[0] | int != 4
when:
- >-
db_engine == 'mariadb'
or (db_engine == 'mysql' and db_version is version('8.0', '>='))
# ======================== Test fix ======================================
- name: Mysql_user Column case sensitive | Create users with case sensitive
community.mysql.mysql_user:
<<: *mysql_params
name: column_case_sensitive
host: '%'
password: 'msandbox'
priv:
'mysql_user_column_case.t1': 'SELECT(a, B, cC, Dd)'
column_case_sensitive: true
- name: Mysql_user Column case sensitive | Assert user privileges are case sensitive
community.mysql.mysql_query:
<<: *mysql_params
query:
- SHOW GRANTS FOR column_case_sensitive@'%'
register: column_case_sensitive_grants
failed_when:
# Column order may vary, thus test each separately
- >-
column_case_sensitive_grants.query_result[0][1]
is not search("a", ignorecase=false)
or column_case_sensitive_grants.query_result[0][1]
is not search("B", ignorecase=false)
or column_case_sensitive_grants.query_result[0][1]
is not search("cC", ignorecase=false)
or column_case_sensitive_grants.query_result[0][1]
is not search("Dd", ignorecase=false)
- name: Mysql_user Column case sensitive | Assert 4 columns are accessible
community.mysql.mysql_query:
<<: *mysql_params
login_user: column_case_sensitive
query:
- DESC mysql_user_column_case.t1
register: assert_4_col_accessible
failed_when:
- assert_4_col_accessible.rowcount[0] | int != 4
# ========================= Teardown ======================================
- name: Mysql_user Column case sensitive | Delete test users
community.mysql.mysql_user:
<<: *mysql_params
name: column_case_sensitive
host_all: true
state: absent
- name: Mysql_user Column case sensitive | Delete test database
community.mysql.mysql_db:
<<: *mysql_params
name: mysql_user_column_case
state: absent

View file

@ -10,10 +10,7 @@
# ======================================================================== # ========================================================================
# Creation # Creation
# ======================================================================== # ========================================================================
- include_tasks: utils/create_user.yml - include: utils/create_user.yml user_name={{ user_name_1 }} user_password={{ user_password_1 }}
vars:
user_name: "{{ user_name_1 }}"
user_password: "{{ user_password_1 }}"
- name: Idempotency | Create user that already exist (expect changed=false) - name: Idempotency | Create user that already exist (expect changed=false)
mysql_user: mysql_user:
@ -58,15 +55,12 @@
# ======================================================================== # ========================================================================
# Create blank user to be removed later # Create blank user to be removed later
- include_tasks: utils/create_user.yml - include: utils/create_user.yml user_name="" user_password='KJFDY&D*Sfuysf'
vars:
user_name: ""
user_password: 'KJFDY&D*Sfuysf'
- name: Idempotency | Remove blank user with hosts=all (expect changed) - name: Idempotency | Remove blank user with hosts=all (expect changed)
mysql_user: mysql_user:
<<: *mysql_params <<: *mysql_params
name: "" user: ""
host_all: true host_all: true
state: absent state: absent
register: result register: result
@ -79,7 +73,7 @@
- name: Idempotency | Remove blank user with hosts=all (expect ok) - name: Idempotency | Remove blank user with hosts=all (expect ok)
mysql_user: mysql_user:
<<: *mysql_params <<: *mysql_params
name: "" user: ""
host_all: true host_all: true
state: absent state: absent
register: result register: result

View file

@ -1,174 +0,0 @@
---
# Tests scenarios for password_expire
- vars:
mysql_parameters: &mysql_params
login_user: "{{ mysql_user }}"
login_password: "{{ mysql_password }}"
login_host: "{{ mysql_host }}"
login_port: "{{ mysql_primary_port }}"
block:
- include_tasks: utils/assert_user_password_expire.yml
vars:
username: "{{ item.username }}"
host: "{{ item.host | default('localhost')}}"
password_expire: "{{ item.password_expire }}"
password: "{{ user_password_1 }}"
expect_change: "{{ item.expect_change }}"
expect_password_expire_change: "{{ item.expect_password_expire_change }}"
expected_password_lifetime: "{{ item.expected_password_lifetime }}"
password_expire_interval: "{{ item.password_expire_interval | default(omit) }}"
expected_password_expired: "{{ item.expected_password_expired }}"
check_mode: "{{ item.check_mode | default(omit) }}"
loop:
# all variants set the password when nothing exists
# never expires
- username: "{{ user_name_1 }}"
host: "%"
password_expire: never
expect_change: true
expected_password_lifetime: "0"
expected_password_expired: "N"
# expires ussing default policy
- username: "{{ user_name_2 }}"
password_expire: default
expect_change: true
expected_password_lifetime: "-1"
expected_password_expired: "N"
# expires ussing interval
- username: "{{ user_name_3 }}"
password_expire: interval
password_expire_interval: "10"
expect_change: true
expected_password_lifetime: "10"
expected_password_expired: "N"
# assert idempotency
- username: "{{ user_name_1 }}"
host: "%"
password_expire: never
expect_change: false
expected_password_lifetime: "0"
expected_password_expired: "N"
- username: "{{ user_name_2 }}"
password_expire: default
expect_change: false
expected_password_lifetime: "-1"
expected_password_expired: "N"
- username: "{{ user_name_3 }}"
password_expire: interval
password_expire_interval: "10"
expect_change: false
expected_password_lifetime: "10"
expected_password_expired: "N"
# assert change is made
- username: "{{ user_name_3 }}"
password_expire: never
expect_change: true
expected_password_lifetime: "0"
expected_password_expired: "N"
- username: "{{ user_name_1 }}"
host: "%"
password_expire: default
expect_change: true
expected_password_lifetime: "-1"
expected_password_expired: "N"
- username: "{{ user_name_2 }}"
password_expire: interval
password_expire_interval: "100"
expect_change: true
expected_password_lifetime: "100"
expected_password_expired: "N"
# assert password expires now
- username: "{{ user_name_1 }}"
host: "%"
password_expire: now
expect_change: true
expected_password_lifetime: "-1" # password lifetime should be the same
expected_password_expired: "Y"
- username: "{{ user_name_2 }}"
password_expire: now
expect_change: true
expected_password_lifetime: "100" # password lifetime should be the same
expected_password_expired: "Y"
# assert idempotency password expires now
- username: "{{ user_name_1 }}"
host: "%"
password_expire: now
expect_change: false
expected_password_lifetime: "-1" # password lifetime should be the same
expected_password_expired: "Y"
- username: "{{ user_name_2 }}"
password_expire: now
expect_change: false
expected_password_lifetime: "100" # password lifetime should be the same
expected_password_expired: "Y"
# assert check_mode
- username: "{{ user_name_3 }}"
password_expire: interval
password_expire_interval: 10
check_mode: true
expect_change: false
expected_password_lifetime: "0"
expected_password_expired: "N"
- name: password_expire | Set password_expire = interval without password_expire_interval
community.mysql.mysql_user:
<<: *mysql_params
name: '{{ user_name_4 }}'
host: '%'
password: '{{ user_password_4 }}'
password_expire: interval
state: present
register: result
ignore_errors: true
- name: password_expire | Assert that action fails if 'password_expire_interval' not set
ansible.builtin.assert:
that:
- result is failed
- name: password_expire | Set password_expire_interval < 1
community.mysql.mysql_user:
<<: *mysql_params
name: '{{ user_name_4 }}'
host: '%'
password: '{{ user_password_4 }}'
password_expire: interval
password_expire_interval: -1
state: present
register: result
ignore_errors: true
- name: password_expire | Assert that action fails if 'password_expire_interval' is < 1
ansible.builtin.assert:
that:
- result is failed
- "'should be positive number' in result.msg"
- name: password_expire | check mode for user creation
community.mysql.mysql_user:
<<: *mysql_params
name: '{{ user_name_4 }}'
host: '%'
password: '{{ user_password_4 }}'
password_expire: interval
password_expire_interval: 20
state: present
register: result
check_mode: True
failed_when: result is changed
- include_tasks: utils/remove_user.yml
vars:
user_name: "{{ item.username }}"
loop:
- username: "{{ user_name_1 }}"
- username: "{{ user_name_2 }}"
- username: "{{ user_name_3 }}"
- username: "{{ user_name_4 }}"

View file

@ -100,26 +100,6 @@
- "'GRANT SELECT, DELETE ON `data2`.*' in result.stdout" - "'GRANT SELECT, DELETE ON `data2`.*' in result.stdout"
when: enable_check_mode == 'yes' when: enable_check_mode == 'yes'
- name: Priv append | Try to append invalid privileges
mysql_user:
<<: *mysql_params
name: '{{ user_name_4 }}'
host: '%'
password: '{{ user_password_4 }}'
priv: 'data1.*:INVALID/data2.*:SELECT'
append_privs: yes
state: present
check_mode: '{{ enable_check_mode }}'
register: result
ignore_errors: true
- name: Priv append | Assert that there wasn't a change in privileges if check_mode is set to 'no'
assert:
that:
- result is failed
- "'Error granting privileges' in result.msg"
when: enable_check_mode == 'no'
########## ##########
# Clean up # Clean up
- name: Drop test databases - name: Drop test databases
@ -131,6 +111,4 @@
- data1 - data1
- data2 - data2
- include_tasks: utils/remove_user.yml - include: utils/remove_user.yml user_name={{ user_name_4 }}
vars:
user_name: "{{ user_name_4 }}"

View file

@ -151,6 +151,4 @@
- data2 - data2
- data3 - data3
- include_tasks: utils/remove_user.yml - include: utils/remove_user.yml user_name="{{ user_name_3 }}"
vars:
user_name: "{{ user_name_3 }}"

Some files were not shown because too many files have changed in this diff Show more