2013-11-26 22:16:42 +00:00
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
'''
|
|
|
|
:codeauthor: :email:`Mike Place (mp@saltstack.com)`
|
|
|
|
|
|
|
|
|
|
|
|
tests.unit.modules.mysql
|
|
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
'''
|
|
|
|
|
2014-11-21 19:05:13 +00:00
|
|
|
# Import Python libs
|
|
|
|
from __future__ import absolute_import
|
|
|
|
|
2013-11-26 22:16:42 +00:00
|
|
|
# Import Salt Testing libs
|
|
|
|
from salttesting import skipIf, TestCase
|
|
|
|
from salttesting.helpers import ensure_in_syspath
|
|
|
|
from salttesting.mock import NO_MOCK, NO_MOCK_REASON, MagicMock, patch, call
|
|
|
|
|
|
|
|
ensure_in_syspath('../../')
|
|
|
|
|
|
|
|
# Import salt libs
|
|
|
|
from salt.modules import mysql
|
|
|
|
|
|
|
|
NO_MYSQL = False
|
|
|
|
try:
|
2014-02-08 08:05:42 +00:00
|
|
|
import MySQLdb # pylint: disable=W0611
|
2013-11-26 22:16:42 +00:00
|
|
|
except Exception:
|
|
|
|
NO_MYSQL = True
|
|
|
|
|
|
|
|
mysql.__salt__ = {}
|
|
|
|
|
|
|
|
DEBUG = True
|
|
|
|
|
|
|
|
|
|
|
|
@skipIf(NO_MOCK, NO_MOCK_REASON)
|
|
|
|
@skipIf(NO_MYSQL, 'Install MySQL bindings before running MySQL unit tests.')
|
|
|
|
class MySQLTestCase(TestCase):
|
2013-11-28 17:24:27 +00:00
|
|
|
|
2013-12-11 13:19:29 +00:00
|
|
|
def test_user_exists(self):
|
2013-11-26 22:16:42 +00:00
|
|
|
'''
|
2013-12-11 13:19:29 +00:00
|
|
|
Test to see if mysql module properly forms the MySQL query to see if a user exists
|
|
|
|
|
|
|
|
Do it before test_user_create_when_user_exists mocks the user_exists call
|
2013-11-26 22:16:42 +00:00
|
|
|
'''
|
2013-12-11 13:19:29 +00:00
|
|
|
self._test_call(mysql.user_exists,
|
2013-12-13 03:51:28 +00:00
|
|
|
{'sql': ('SELECT User,Host FROM mysql.user WHERE '
|
2013-12-11 13:19:29 +00:00
|
|
|
'User = %(user)s AND Host = %(host)s AND '
|
|
|
|
'Password = PASSWORD(%(password)s)'),
|
|
|
|
'sql_args': {'host': 'localhost',
|
|
|
|
'password': 'BLUECOW',
|
|
|
|
'user': 'mytestuser'
|
|
|
|
}
|
|
|
|
},
|
|
|
|
user='mytestuser',
|
|
|
|
host='localhost',
|
|
|
|
password='BLUECOW'
|
|
|
|
)
|
|
|
|
|
|
|
|
# test_user_create_when_user_exists(self):
|
|
|
|
# ensure we don't try to create a user when one already exists
|
2013-11-26 22:16:42 +00:00
|
|
|
mock = MagicMock(return_value=True)
|
|
|
|
mysql.user_exists = mock
|
|
|
|
with patch.dict(mysql.__salt__, {'config.option': MagicMock()}):
|
|
|
|
ret = mysql.user_create('testuser')
|
|
|
|
self.assertEqual(False, ret)
|
|
|
|
|
|
|
|
def test_user_create(self):
|
|
|
|
'''
|
|
|
|
Test the creation of a MySQL user in mysql exec module
|
|
|
|
'''
|
2013-11-29 16:52:07 +00:00
|
|
|
self._test_call(mysql.user_create,
|
2013-12-03 09:42:29 +00:00
|
|
|
{'sql': 'CREATE USER %(user)s@%(host)s IDENTIFIED BY %(password)s',
|
|
|
|
'sql_args': {'password': 'BLUECOW',
|
|
|
|
'user': 'testuser',
|
|
|
|
'host': 'localhost',
|
|
|
|
}
|
|
|
|
},
|
2013-11-29 16:52:07 +00:00
|
|
|
'testuser',
|
2013-12-03 09:42:29 +00:00
|
|
|
password='BLUECOW'
|
|
|
|
)
|
2013-11-26 22:16:42 +00:00
|
|
|
|
|
|
|
def test_user_chpass(self):
|
|
|
|
'''
|
|
|
|
Test changing a MySQL user password in mysql exec module
|
|
|
|
'''
|
|
|
|
connect_mock = MagicMock()
|
|
|
|
mysql._connect = connect_mock
|
|
|
|
with patch.dict(mysql.__salt__, {'config.option': MagicMock()}):
|
|
|
|
mysql.user_chpass('testuser', password='BLUECOW')
|
|
|
|
calls = (
|
|
|
|
call().cursor().execute(
|
[develop] Merge forward from 2016.3 to develop (#32494)
* fix sorting by latest version when called with an attribute
* remove reference to master_alive_check
* Fixes saltstack/salt#28262
* Resolve memory leak in authentication
* outputter virt_list does not exist anymore
* Update proxmox documentation
* Fix documentation on boto_asg and boto_elb modules and states
* modules.win_timezone: don't list all zones in debug log
* Correcty index glusterfs bricks
Fixes issue #32311
* Cleaner deprecation process with decorators
* Add deprecation decorator scaffold
* Capture type error and unhandled exceptions while function calls
* Aware of the current and future version of deprecation
* Implement initially is_deprecated decorator
* Add an alias for the capitalization
* Fix capitalization easier way
* Remove an extra line
* Add successor name to the deprecation decorator.
* Granulate logging and error messages.
* Implement function swapper
* Raise later the caught exception
* Clarify exception message
* Save function original name
* Remove an extra line
* Hide an alternative hidden function name in the error message, preserving the error itself
* Rename variable as private
* Add a method to detect if a function is using its previous version
* Message to the log and/or raise an exception accordingly to the status of used function
* Log an error along with the exception
* Add internal method documentation
* Add documentation and usage process for decorator "is_deprecated"
* Add documentation and process usage for the decorator "with_deprecated"
* Hide private method name
* Fix PEP8, re-word the error message
* Deprecate basic uptime function
* Add initial decorator unit test
* Rename old/new functions, mock versions
* Move frequent data to the test setup
* Add logging on EOL exception
* Rename and document high to low version test on is_deprecated
* Implement a test on low to high version of is_deprecated decorator
* Add a correction to the test description
* Remove a dead code
* Implement a test for high to low version on is_deprecated, using with_successor param
* Correct typso adn mistaeks
* Implement high to low version with successor param on is_deprecated
* Setup a virtual name for the module
* Implement test for with_deprecated should raise an exception if same deprecated function not found
* Implement test for with_deprecated an old function is picked up if configured
* Correct test description purpose
* Implement test with_deprecated when no deprecation is requested
* Add logging test to the configured deprecation request
* Add logging testing when deprecated version wasn't requested
* Implement test EOL for with_deprecated decorator
* Correct test explanation
* Rename the test
* Implement with_deprecated no EOL, deprecated other function name
* Implement with_deprecated, deprecated other function name, EOL reached
* Add test description for the with_deprecated + with_name + EOL
* Fix confusing test names
* Add logging test to the is_deprecated decorator when function as not found.
* Add more test point to each test, remove empty lines
* Bugfix: at certain conditions a wrong alias name is reported to the log
* Fix a typo in a comment
* Add test for the logging
* Disable a pylint: None will _never_ be raised
* Fix test for the deprecated "status.uptime" version
* Bugfix: Do not yank raised exceptions
* Remove unnecessary decorator
* Add test for the new uptime
* Add test for the new uptime fails when /proc/uptime does not exists
* Rename old test case
* Skip test for the UTC time, unless freeze time is used.
* Fix pylint
* Fix documentation
* Bugfix: proxy-pass the docstring of the decorated function
* Lint fix
* Fixes saltstack/salt#28262 for 2015.5 branch
* Update master config docs
* Improve git_pillar documentation/logging
* Add note about different behavior of top file in git_pillar
* Make log entry for a missing pillar SLS file more accurate for git_pillar
* FreeBSD supports packages in format java/openjdk7 so the prior commit broke that functionality. Check freebsd/pkg#1409 for more info.
* FreeBSD supports packages in format java/openjdk7 so the prior commit broke that functionality. Check freebsd/pkg#1409 for more info.
* Update glusterfs_test to be inline with #32312
* Fix salt-cloud paralell provisioning
Closes #31632
* Ignore Raspbian in service.py __virtual__ (#32421)
* Ignore Raspbian in service.py __virtual__
This prevents more than one execution module from trying to load as the
service virtual module.
Refs: #32413
* pack __salt__ before loading provider overrides
We can (and should) pack here since we're just packing a reference to the
object. __salt__ needs to be available when we're loading our provider
overrides
* Fix broken __salt__ dict in provider override
Using ret.items() here sets ``__salt__`` to its items (tuple containing
function name and reference), breaking usage of ``__salt__`` inside
overridden functions.
* Merge #32293 with test fixes (#32418)
* Fix issue #11497
* Remove check for working directory presence in tests
* Fix Domainname introspection
Default value needs to be extracted from the container itself,
because dockerd set Domainname value when network_mode=host.
* Add pgjsonb_queue to queue doc index
* Pylint fixes
* Pass parser options into batch mode
Resolves #31738
* Changed the target file in file.symlink test (#32443)
* Argument name in docs should match actual arg name (#32445)
Fixes #31851
* tests.integration: bypass MacOS TMPDIR, gettempdir (#32447)
Updates 0edd532, 8f558a5.
When logging in as root over `ssh root@host`, `$TMPDIR` and
`tempfile.gettempdir()` are both set to a variation of:
```
/private/var/folders/zz/zyxvpxvq6csfxvn_n0000000000000/T/
```
When logging in as root over `sudo -i`, `$TMPDIR` is unset and
`tempfile.gettempdir()` is set to `/tmp`.
My guess is that the second case is an unintended or uncorrected omision
by Apple as they have introduced the longer, randomized temp path in a
recent version of MacOS.
* Issue #28706: Fix state user.present behavior. (#32448)
- As mentionned in issue #28706, state user.present no longer remove
user from groups if the keyword 'groups' with empty value '[]' is not
explicitly set, salt will assume current groups are still wanted.
* tests.integration: fix 4230c8a
* Move the tables of virtual modules to individual documentation pages
* Add new doc pages to toctree
* Add external ref to windows package manager docs
* Improve docstrings
* Add documentation on virtual module provider overrides to the module docs
* Clarify the scope of the provider param in states.
* Add link to provider override docs to all package providers
* Add link to provider override docs to all service providers
* Add link to provider override docs to all user providers
* dd link to provider override docs to all shadow providers
* Add link to provider override docs to all group providers
* Backport 31164 and 31364 (#32474)
* Don't send REQ while another one is waiting for response.
The message has to be removed from the queue the only *after* it's
already processed to don't confuse send() functionality that expects
empty queue means: there's no active sendings.
* Fixed zeromq ReqMessageClient destroy
* Add link to provider override docs to opkg.py
This is a companion to https://github.com/saltstack/salt/pull/32458, but
this module was not added until the 2016.3 branch, so the documentation
is being updated there for this module.
* Add documentation for some master/minion configs (#32454)
Refs #32400
Adds docs for:
- cli_summary
- event_return_queue
- event_return_whitelist
- event_return_blacklist
- file_recv_max_size
- fileserver_followsymlinks
- fileserver_ignoresymlinks
- fileserver_limit_traversal
* Automatically detect correct MySQL password column for 5.7 and fix setting passwords (#32440)
* Automatically detect MySQL password column
* Fix changing password in MySQL 5.7
* Fix lint test
* Fix unit tests (?)
They will still fail if "authentication_string" is legitimately the right column name, but I don't know what to do about that.
* Additional unit test fix
* Only unsub if we have a jid
Closes #32479
2016-04-11 23:07:15 +00:00
|
|
|
'UPDATE mysql.user SET Password=PASSWORD(%(password)s) WHERE User=%(user)s AND Host = %(host)s;',
|
2013-12-03 09:42:29 +00:00
|
|
|
{'password': 'BLUECOW',
|
|
|
|
'user': 'testuser',
|
|
|
|
'host': 'localhost',
|
|
|
|
}
|
|
|
|
),
|
2013-11-26 22:16:42 +00:00
|
|
|
call().cursor().execute('FLUSH PRIVILEGES;'),
|
|
|
|
)
|
|
|
|
connect_mock.assert_has_calls(calls, any_order=True)
|
|
|
|
|
|
|
|
def test_user_remove(self):
|
|
|
|
'''
|
|
|
|
Test the removal of a MySQL user in mysql exec module
|
|
|
|
'''
|
2013-12-03 09:42:29 +00:00
|
|
|
self._test_call(mysql.user_remove,
|
|
|
|
{'sql': 'DROP USER %(user)s@%(host)s',
|
|
|
|
'sql_args': {'user': 'testuser',
|
|
|
|
'host': 'localhost',
|
|
|
|
}
|
|
|
|
},
|
|
|
|
'testuser'
|
|
|
|
)
|
2013-11-26 22:16:42 +00:00
|
|
|
|
|
|
|
def test_db_check(self):
|
|
|
|
'''
|
|
|
|
Test MySQL db check function in mysql exec module
|
|
|
|
'''
|
2013-12-01 09:43:13 +00:00
|
|
|
self._test_call(mysql.db_check, 'CHECK TABLE `test``\'" db`.`my``\'" table`', 'test`\'" db', 'my`\'" table')
|
2013-11-26 22:16:42 +00:00
|
|
|
|
|
|
|
def test_db_repair(self):
|
|
|
|
'''
|
|
|
|
Test MySQL db repair function in mysql exec module
|
|
|
|
'''
|
2013-12-01 09:43:13 +00:00
|
|
|
self._test_call(mysql.db_repair, 'REPAIR TABLE `test``\'" db`.`my``\'" table`', 'test`\'" db', 'my`\'" table')
|
2013-11-26 22:16:42 +00:00
|
|
|
|
|
|
|
def test_db_optimize(self):
|
|
|
|
'''
|
|
|
|
Test MySQL db optimize function in mysql exec module
|
|
|
|
'''
|
2013-12-01 09:43:13 +00:00
|
|
|
self._test_call(mysql.db_optimize, 'OPTIMIZE TABLE `test``\'" db`.`my``\'" table`', 'test`\'" db', 'my`\'" table')
|
2013-11-26 22:16:42 +00:00
|
|
|
|
|
|
|
def test_db_remove(self):
|
|
|
|
'''
|
|
|
|
Test MySQL db remove function in mysql exec module
|
|
|
|
'''
|
|
|
|
mysql.db_exists = MagicMock(return_value=True)
|
2013-12-01 09:43:13 +00:00
|
|
|
self._test_call(mysql.db_remove, 'DROP DATABASE `test``\'" db`;', 'test`\'" db')
|
2013-11-26 22:16:42 +00:00
|
|
|
|
|
|
|
def test_db_tables(self):
|
|
|
|
'''
|
|
|
|
Test MySQL db_tables function in mysql exec module
|
|
|
|
'''
|
2013-12-01 09:43:13 +00:00
|
|
|
self._test_call(mysql.db_tables, 'SHOW TABLES IN `test``\'" db`', 'test`\'" db')
|
2013-11-26 22:16:42 +00:00
|
|
|
|
|
|
|
def test_db_exists(self):
|
|
|
|
'''
|
|
|
|
Test MySQL db_exists function in mysql exec module
|
|
|
|
'''
|
2013-12-01 01:34:05 +00:00
|
|
|
self._test_call(
|
|
|
|
mysql.db_exists,
|
|
|
|
{'sql': 'SHOW DATABASES LIKE %(dbname)s;',
|
2016-01-13 09:12:32 +00:00
|
|
|
'sql_args': {'dbname': r'''test%_`" db'''}
|
2013-12-01 01:34:05 +00:00
|
|
|
},
|
2016-01-13 09:12:32 +00:00
|
|
|
'test%_`" db'
|
2013-11-29 16:52:07 +00:00
|
|
|
)
|
2013-11-26 22:16:42 +00:00
|
|
|
|
|
|
|
def test_db_create(self):
|
|
|
|
'''
|
|
|
|
Test MySQL db_create function in mysql exec module
|
|
|
|
'''
|
2013-12-01 01:34:05 +00:00
|
|
|
self._test_call(
|
|
|
|
mysql.db_create,
|
2016-03-31 14:45:17 +00:00
|
|
|
'CREATE DATABASE IF NOT EXISTS `test``\'" db`;',
|
2013-12-01 09:43:13 +00:00
|
|
|
'test`\'" db'
|
2013-11-29 16:52:07 +00:00
|
|
|
)
|
2013-11-26 22:16:42 +00:00
|
|
|
|
|
|
|
def test_user_list(self):
|
|
|
|
'''
|
|
|
|
Test MySQL user_list function in mysql exec module
|
|
|
|
'''
|
|
|
|
self._test_call(mysql.user_list, 'SELECT User,Host FROM mysql.user')
|
|
|
|
|
|
|
|
def test_user_info(self):
|
|
|
|
'''
|
|
|
|
Test to see if the mysql execution module correctly forms the SQL for information on a MySQL user.
|
|
|
|
'''
|
|
|
|
self._test_call(mysql.user_info,
|
2013-12-03 09:42:29 +00:00
|
|
|
{'sql': 'SELECT * FROM mysql.user WHERE User = %(user)s AND Host = %(host)s',
|
|
|
|
'sql_args': {'host': 'localhost',
|
|
|
|
'user': 'mytestuser',
|
|
|
|
}
|
|
|
|
},
|
2013-11-29 16:52:07 +00:00
|
|
|
'mytestuser'
|
|
|
|
)
|
2013-11-26 22:16:42 +00:00
|
|
|
|
|
|
|
def test_user_grants(self):
|
|
|
|
'''
|
|
|
|
Test to ensure the mysql user_grants function returns properly formed SQL for a basic query
|
|
|
|
'''
|
2013-12-11 13:19:29 +00:00
|
|
|
mock = MagicMock(return_value=True)
|
|
|
|
mysql.user_exists = mock
|
2013-12-03 09:42:29 +00:00
|
|
|
self._test_call(mysql.user_grants,
|
|
|
|
{'sql': 'SHOW GRANTS FOR %(user)s@%(host)s',
|
|
|
|
'sql_args': {'host': 'localhost',
|
|
|
|
'user': 'testuser',
|
|
|
|
}
|
|
|
|
},
|
|
|
|
'testuser'
|
|
|
|
)
|
2013-11-26 22:16:42 +00:00
|
|
|
|
2015-06-18 13:00:23 +00:00
|
|
|
def test_grant_exists_true(self):
|
|
|
|
'''
|
|
|
|
Test to ensure that we can find a grant that exists
|
|
|
|
'''
|
|
|
|
mock_grants = [
|
|
|
|
"GRANT USAGE ON *.* TO 'testuser'@'%'",
|
|
|
|
"GRANT SELECT, INSERT, UPDATE ON `testdb`.`testtableone` TO 'testuser'@'%'",
|
|
|
|
"GRANT SELECT ON `testdb`.`testtabletwo` TO 'testuer'@'%'",
|
|
|
|
"GRANT SELECT ON `testdb`.`testtablethree` TO 'testuser'@'%'",
|
|
|
|
]
|
|
|
|
mock = MagicMock(return_value=mock_grants)
|
|
|
|
with patch.object(mysql, 'user_grants', return_value=mock_grants) as mock_user_grants:
|
|
|
|
ret = mysql.grant_exists(
|
|
|
|
'SELECT, INSERT, UPDATE',
|
|
|
|
'testdb.testtableone',
|
|
|
|
'testuser',
|
|
|
|
'%'
|
|
|
|
)
|
|
|
|
self.assertEqual(ret, True)
|
|
|
|
|
|
|
|
def test_grant_exists_false(self):
|
|
|
|
'''
|
|
|
|
Test to ensure that we don't find a grant that doesn't exist
|
|
|
|
'''
|
|
|
|
mock_grants = [
|
|
|
|
"GRANT USAGE ON *.* TO 'testuser'@'%'",
|
|
|
|
"GRANT SELECT, INSERT, UPDATE ON `testdb`.`testtableone` TO 'testuser'@'%'",
|
|
|
|
"GRANT SELECT ON `testdb`.`testtablethree` TO 'testuser'@'%'",
|
|
|
|
]
|
|
|
|
mock = MagicMock(return_value=mock_grants)
|
|
|
|
with patch.object(mysql, 'user_grants', return_value=mock_grants) as mock_user_grants:
|
|
|
|
ret = mysql.grant_exists(
|
|
|
|
'SELECT',
|
|
|
|
'testdb.testtabletwo',
|
|
|
|
'testuser',
|
|
|
|
'%'
|
|
|
|
)
|
|
|
|
self.assertEqual(ret, False)
|
2013-11-26 22:16:42 +00:00
|
|
|
|
|
|
|
@skipIf(True, 'TODO: Mock up user_grants()')
|
|
|
|
def test_grant_add(self):
|
|
|
|
'''
|
|
|
|
Test grant_add function in mysql exec module
|
|
|
|
'''
|
|
|
|
self._test_call(mysql.grant_add, '', 'SELECT,INSERT,UPDATE', 'database.*', 'frank', 'localhost')
|
|
|
|
|
|
|
|
@skipIf(True, 'TODO: Mock up user_grants()')
|
|
|
|
def test_grant_revoke(self):
|
|
|
|
'''
|
|
|
|
Test grant revoke in mysql exec module
|
|
|
|
'''
|
2013-11-28 14:41:00 +00:00
|
|
|
self._test_call(mysql.grant_revoke, '', 'SELECT,INSERT,UPDATE', 'database.*', 'frank', 'localhost')
|
2013-11-26 22:16:42 +00:00
|
|
|
|
|
|
|
def test_processlist(self):
|
|
|
|
'''
|
|
|
|
Test processlist function in mysql exec module
|
|
|
|
'''
|
|
|
|
self._test_call(mysql.processlist, 'SHOW FULL PROCESSLIST')
|
|
|
|
|
|
|
|
def test_get_master_status(self):
|
|
|
|
'''
|
|
|
|
Test get_master_status in the mysql execution module
|
|
|
|
'''
|
|
|
|
self._test_call(mysql.get_master_status, 'SHOW MASTER STATUS')
|
|
|
|
|
|
|
|
def test_get_slave_status(self):
|
|
|
|
'''
|
|
|
|
Test get_slave_status in the mysql execution module
|
|
|
|
'''
|
|
|
|
self._test_call(mysql.get_slave_status, 'SHOW SLAVE STATUS')
|
|
|
|
|
|
|
|
@skipIf(True, 'MySQL module claims this function is not ready for production')
|
|
|
|
def test_free_slave(self):
|
2015-02-05 04:11:17 +00:00
|
|
|
pass
|
2013-11-26 22:16:42 +00:00
|
|
|
|
|
|
|
def test_query(self):
|
|
|
|
self._test_call(mysql.query, 'SELECT * FROM testdb', 'testdb', 'SELECT * FROM testdb')
|
|
|
|
|
|
|
|
def _test_call(self, function, expected_sql, *args, **kwargs):
|
|
|
|
connect_mock = MagicMock()
|
|
|
|
mysql._connect = connect_mock
|
|
|
|
with patch.dict(mysql.__salt__, {'config.option': MagicMock()}):
|
|
|
|
function(*args, **kwargs)
|
2013-11-29 16:52:07 +00:00
|
|
|
if isinstance(expected_sql, dict):
|
2015-11-25 12:31:20 +00:00
|
|
|
calls = call().cursor().execute('{0}'.format(expected_sql['sql']), expected_sql['sql_args'])
|
2013-11-29 16:52:07 +00:00
|
|
|
else:
|
2015-11-25 12:31:20 +00:00
|
|
|
calls = call().cursor().execute('{0}'.format(expected_sql))
|
|
|
|
connect_mock.assert_has_calls((calls,), True)
|
2014-03-31 01:56:33 +00:00
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
from integration import run_tests
|
|
|
|
run_tests(MySQLTestCase, needs_daemon=False)
|