mirror of
https://github.com/saltstack/salt.git
synced 2025-04-17 10:10:20 +00:00
Merge branch '2017.7' into fix-capirca
This commit is contained in:
commit
71913ffb12
15 changed files with 264 additions and 87 deletions
2
.ci/lint
2
.ci/lint
|
@ -98,7 +98,7 @@ pipeline {
|
|||
stage('linting all') {
|
||||
// perform a full linit if this is a merge forward and the change only lint passed.
|
||||
when {
|
||||
expression { return params.CHANGE_BRANCH =~ /(?i)^merge[._-]/ }
|
||||
expression { return env.CHANGE_BRANCH =~ /(?i)^merge[._-]/ }
|
||||
}
|
||||
parallel {
|
||||
stage('setup full') {
|
||||
|
|
|
@ -6,16 +6,16 @@ Configuring the Salt Master
|
|||
|
||||
The Salt system is amazingly simple and easy to configure, the two components
|
||||
of the Salt system each have a respective configuration file. The
|
||||
:command:`salt-master` is configured via the master configuration file, and the
|
||||
:command:`salt-minion` is configured via the minion configuration file.
|
||||
``salt-master`` is configured via the master configuration file, and the
|
||||
``salt-minion`` is configured via the minion configuration file.
|
||||
|
||||
.. seealso::
|
||||
|
||||
:ref:`Example master configuration file <configuration-examples-master>`.
|
||||
|
||||
The configuration file for the salt-master is located at
|
||||
:file:`/etc/salt/master` by default. A notable exception is FreeBSD, where the
|
||||
configuration file is located at :file:`/usr/local/etc/salt`. The available
|
||||
options are as follows:
|
||||
The configuration file for the salt-master is located at ``/etc/salt/master``
|
||||
by default. A notable exception is FreeBSD, where the configuration file is
|
||||
located at ``/usr/local/etc/salt``. The available options are as follows:
|
||||
|
||||
|
||||
.. _primary-master-configuration:
|
||||
|
@ -717,6 +717,22 @@ accessible from the minions.
|
|||
|
||||
master_job_cache: redis
|
||||
|
||||
.. conf_master:: job_cache_store_endtime
|
||||
|
||||
``job_cache_store_endtime``
|
||||
---------------------------
|
||||
|
||||
.. versionadded:: 2015.8.0
|
||||
|
||||
Default: ``False``
|
||||
|
||||
Specify whether the Salt Master should store end times for jobs as returns
|
||||
come in.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
job_cache_store_endtime: False
|
||||
|
||||
.. conf_master:: enforce_mine_cache
|
||||
|
||||
``enforce_mine_cache``
|
||||
|
@ -4151,6 +4167,55 @@ The queue size for workers in the reactor.
|
|||
reactor_worker_hwm: 10000
|
||||
|
||||
|
||||
.. _salt-api-master-settings:
|
||||
|
||||
Salt-API Master Settings
|
||||
========================
|
||||
|
||||
There are some settings for :ref:`salt-api <netapi-introduction>` that can be
|
||||
configured on the Salt Master.
|
||||
|
||||
.. conf_master:: api_logfile
|
||||
|
||||
``api_logfile``
|
||||
---------------
|
||||
|
||||
Default: ``/var/log/salt/api``
|
||||
|
||||
The logfile location for ``salt-api``.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
api_logfile: /var/log/salt/api
|
||||
|
||||
.. conf_master:: api_pidfile
|
||||
|
||||
``api_pidfile``
|
||||
---------------
|
||||
|
||||
Default: /var/run/salt-api.pid
|
||||
|
||||
If this master will be running ``salt-api``, specify the pidfile of the
|
||||
``salt-api`` daemon.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
api_pidfile: /var/run/salt-api.pid
|
||||
|
||||
.. conf_master:: rest_timeout
|
||||
|
||||
``rest_timeout``
|
||||
----------------
|
||||
|
||||
Default: ``300``
|
||||
|
||||
Used by ``salt-api`` for the master requests timeout.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
rest_timeout: 300
|
||||
|
||||
|
||||
.. _syndic-server-settings:
|
||||
|
||||
Syndic Server Settings
|
||||
|
@ -4525,6 +4590,9 @@ Node Groups
|
|||
|
||||
.. conf_master:: nodegroups
|
||||
|
||||
``nodegroups``
|
||||
--------------
|
||||
|
||||
Default: ``{}``
|
||||
|
||||
Node groups allow for logical groupings of minion nodes.
|
||||
|
|
|
@ -23,7 +23,7 @@ python-dateutil==2.6.1
|
|||
python-gnupg==0.4.1
|
||||
PyYAML==3.12
|
||||
pyzmq==16.0.2
|
||||
requests==2.18.1
|
||||
requests==2.21.0
|
||||
singledispatch==3.4.0.3
|
||||
six==1.11.0
|
||||
smmap==0.9.0
|
||||
|
|
|
@ -30,7 +30,7 @@ python-gnupg==0.4.0
|
|||
pywin32==223
|
||||
PyYAML==3.12
|
||||
pyzmq==16.0.2
|
||||
requests==2.13.0
|
||||
requests==2.21.0
|
||||
singledispatch==3.4.0.3
|
||||
smmap==0.9.0
|
||||
timelib==0.2.4
|
||||
|
|
|
@ -2655,7 +2655,8 @@ def create(vm_):
|
|||
)
|
||||
|
||||
event_kwargs = vm_.copy()
|
||||
del event_kwargs['password']
|
||||
if event_kwargs.get('password'):
|
||||
del event_kwargs['password']
|
||||
|
||||
try:
|
||||
__utils__['cloud.fire_event'](
|
||||
|
|
|
@ -982,27 +982,35 @@ def _windows_platform_data():
|
|||
except IndexError:
|
||||
log.debug('Motherboard info not available on this system')
|
||||
|
||||
os_release = platform.release()
|
||||
info = salt.utils.win_osinfo.get_os_version_info()
|
||||
server = {'Vista': '2008Server',
|
||||
'7': '2008ServerR2',
|
||||
'8': '2012Server',
|
||||
'8.1': '2012ServerR2',
|
||||
'10': '2016Server'}
|
||||
|
||||
# Starting with Python 2.7.12 and 3.5.2 the `platform.uname()` function
|
||||
# started reporting the Desktop version instead of the Server version on
|
||||
# Server versions of Windows, so we need to look those up
|
||||
# So, if you find a Server Platform that's a key in the server
|
||||
# dictionary, then lookup the actual Server Release.
|
||||
# (Product Type 1 is Desktop, Everything else is Server)
|
||||
if info['ProductType'] > 1 and os_release in server:
|
||||
os_release = server[os_release]
|
||||
|
||||
service_pack = None
|
||||
if info['ServicePackMajor'] > 0:
|
||||
service_pack = ''.join(['SP', str(info['ServicePackMajor'])])
|
||||
|
||||
# This creates the osrelease grain based on the Windows Operating
|
||||
# System Product Name. As long as Microsoft maintains a similar format
|
||||
# this should be future proof
|
||||
version = 'Unknown'
|
||||
release = ''
|
||||
if 'Server' in osinfo.Caption:
|
||||
for item in osinfo.Caption.split(' '):
|
||||
# If it's all digits, then it's version
|
||||
if re.match(r'\d+', item):
|
||||
version = item
|
||||
# If it starts with R and then numbers, it's the release
|
||||
# ie: R2
|
||||
if re.match(r'^R\d+$', item):
|
||||
release = item
|
||||
os_release = '{0}Server{1}'.format(version, release)
|
||||
else:
|
||||
for item in osinfo.Caption.split(' '):
|
||||
# If it's a number, decimal number, Thin or Vista, then it's the
|
||||
# version
|
||||
if re.match(r'^(\d+(\.\d+)?)|Thin|Vista$', item):
|
||||
version = item
|
||||
os_release = version
|
||||
|
||||
grains = {
|
||||
'kernelrelease': _clean_value('kernelrelease', osinfo.Version),
|
||||
'osversion': _clean_value('osversion', osinfo.Version),
|
||||
|
@ -1081,6 +1089,7 @@ def id_():
|
|||
'''
|
||||
return {'id': __opts__.get('id', '')}
|
||||
|
||||
|
||||
_REPLACE_LINUX_RE = re.compile(r'\W(?:gnu/)?linux', re.IGNORECASE)
|
||||
|
||||
# This maps (at most) the first ten characters (no spaces, lowercased) of
|
||||
|
|
|
@ -330,7 +330,7 @@ def _connect(**kwargs):
|
|||
try:
|
||||
dbc = MySQLdb.connect(**connargs)
|
||||
except MySQLdb.OperationalError as exc:
|
||||
err = 'MySQL Error {0}: {1}'.format(*exc)
|
||||
err = 'MySQL Error {0}: {1}'.format(*exc.args)
|
||||
__context__['mysql.error'] = err
|
||||
log.error(err)
|
||||
return None
|
||||
|
@ -648,7 +648,7 @@ def query(database, query, **connection_args):
|
|||
try:
|
||||
affected = _execute(cur, query)
|
||||
except MySQLdb.OperationalError as exc:
|
||||
err = 'MySQL Error {0}: {1}'.format(*exc)
|
||||
err = 'MySQL Error {0}: {1}'.format(*exc.args)
|
||||
__context__['mysql.error'] = err
|
||||
log.error(err)
|
||||
return False
|
||||
|
@ -773,7 +773,7 @@ def status(**connection_args):
|
|||
try:
|
||||
_execute(cur, qry)
|
||||
except MySQLdb.OperationalError as exc:
|
||||
err = 'MySQL Error {0}: {1}'.format(*exc)
|
||||
err = 'MySQL Error {0}: {1}'.format(*exc.args)
|
||||
__context__['mysql.error'] = err
|
||||
log.error(err)
|
||||
return {}
|
||||
|
@ -804,7 +804,7 @@ def version(**connection_args):
|
|||
try:
|
||||
_execute(cur, qry)
|
||||
except MySQLdb.OperationalError as exc:
|
||||
err = 'MySQL Error {0}: {1}'.format(*exc)
|
||||
err = 'MySQL Error {0}: {1}'.format(*exc.args)
|
||||
__context__['mysql.error'] = err
|
||||
log.error(err)
|
||||
return ''
|
||||
|
@ -837,7 +837,7 @@ def slave_lag(**connection_args):
|
|||
try:
|
||||
_execute(cur, qry)
|
||||
except MySQLdb.OperationalError as exc:
|
||||
err = 'MySQL Error {0}: {1}'.format(*exc)
|
||||
err = 'MySQL Error {0}: {1}'.format(*exc.args)
|
||||
__context__['mysql.error'] = err
|
||||
log.error(err)
|
||||
return -3
|
||||
|
@ -922,7 +922,7 @@ def db_list(**connection_args):
|
|||
try:
|
||||
_execute(cur, qry)
|
||||
except MySQLdb.OperationalError as exc:
|
||||
err = 'MySQL Error {0}: {1}'.format(*exc)
|
||||
err = 'MySQL Error {0}: {1}'.format(*exc.args)
|
||||
__context__['mysql.error'] = err
|
||||
log.error(err)
|
||||
return []
|
||||
|
@ -1011,7 +1011,7 @@ def db_tables(name, **connection_args):
|
|||
try:
|
||||
_execute(cur, qry)
|
||||
except MySQLdb.OperationalError as exc:
|
||||
err = 'MySQL Error {0}: {1}'.format(*exc)
|
||||
err = 'MySQL Error {0}: {1}'.format(*exc.args)
|
||||
__context__['mysql.error'] = err
|
||||
log.error(err)
|
||||
return []
|
||||
|
@ -1046,7 +1046,7 @@ def db_exists(name, **connection_args):
|
|||
try:
|
||||
_execute(cur, qry, args)
|
||||
except MySQLdb.OperationalError as exc:
|
||||
err = 'MySQL Error {0}: {1}'.format(*exc)
|
||||
err = 'MySQL Error {0}: {1}'.format(*exc.args)
|
||||
__context__['mysql.error'] = err
|
||||
log.error(err)
|
||||
return False
|
||||
|
@ -1101,7 +1101,7 @@ def db_create(name, character_set=None, collate=None, **connection_args):
|
|||
log.info('DB \'{0}\' created'.format(name))
|
||||
return True
|
||||
except MySQLdb.OperationalError as exc:
|
||||
err = 'MySQL Error {0}: {1}'.format(*exc)
|
||||
err = 'MySQL Error {0}: {1}'.format(*exc.args)
|
||||
__context__['mysql.error'] = err
|
||||
log.error(err)
|
||||
return False
|
||||
|
@ -1137,7 +1137,7 @@ def db_remove(name, **connection_args):
|
|||
try:
|
||||
_execute(cur, qry)
|
||||
except MySQLdb.OperationalError as exc:
|
||||
err = 'MySQL Error {0}: {1}'.format(*exc)
|
||||
err = 'MySQL Error {0}: {1}'.format(*exc.args)
|
||||
__context__['mysql.error'] = err
|
||||
log.error(err)
|
||||
return False
|
||||
|
@ -1169,7 +1169,7 @@ def user_list(**connection_args):
|
|||
qry = 'SELECT User,Host FROM mysql.user'
|
||||
_execute(cur, qry)
|
||||
except MySQLdb.OperationalError as exc:
|
||||
err = 'MySQL Error {0}: {1}'.format(*exc)
|
||||
err = 'MySQL Error {0}: {1}'.format(*exc.args)
|
||||
__context__['mysql.error'] = err
|
||||
log.error(err)
|
||||
return []
|
||||
|
@ -1243,7 +1243,7 @@ def user_exists(user,
|
|||
try:
|
||||
_execute(cur, qry, args)
|
||||
except MySQLdb.OperationalError as exc:
|
||||
err = 'MySQL Error {0}: {1}'.format(*exc)
|
||||
err = 'MySQL Error {0}: {1}'.format(*exc.args)
|
||||
__context__['mysql.error'] = err
|
||||
log.error(err)
|
||||
return False
|
||||
|
@ -1275,7 +1275,7 @@ def user_info(user, host='localhost', **connection_args):
|
|||
try:
|
||||
_execute(cur, qry, args)
|
||||
except MySQLdb.OperationalError as exc:
|
||||
err = 'MySQL Error {0}: {1}'.format(*exc)
|
||||
err = 'MySQL Error {0}: {1}'.format(*exc.args)
|
||||
__context__['mysql.error'] = err
|
||||
log.error(err)
|
||||
return False
|
||||
|
@ -1371,7 +1371,7 @@ def user_create(user,
|
|||
try:
|
||||
_execute(cur, qry, args)
|
||||
except MySQLdb.OperationalError as exc:
|
||||
err = 'MySQL Error {0}: {1}'.format(*exc)
|
||||
err = 'MySQL Error {0}: {1}'.format(*exc.args)
|
||||
__context__['mysql.error'] = err
|
||||
log.error(err)
|
||||
return False
|
||||
|
@ -1472,7 +1472,7 @@ def user_chpass(user,
|
|||
try:
|
||||
result = _execute(cur, qry, args)
|
||||
except MySQLdb.OperationalError as exc:
|
||||
err = 'MySQL Error {0}: {1}'.format(*exc)
|
||||
err = 'MySQL Error {0}: {1}'.format(*exc.args)
|
||||
__context__['mysql.error'] = err
|
||||
log.error(err)
|
||||
return False
|
||||
|
@ -1520,7 +1520,7 @@ def user_remove(user,
|
|||
try:
|
||||
_execute(cur, qry, args)
|
||||
except MySQLdb.OperationalError as exc:
|
||||
err = 'MySQL Error {0}: {1}'.format(*exc)
|
||||
err = 'MySQL Error {0}: {1}'.format(*exc.args)
|
||||
__context__['mysql.error'] = err
|
||||
log.error(err)
|
||||
return False
|
||||
|
@ -1746,7 +1746,7 @@ def user_grants(user,
|
|||
try:
|
||||
_execute(cur, qry, args)
|
||||
except MySQLdb.OperationalError as exc:
|
||||
err = 'MySQL Error {0}: {1}'.format(*exc)
|
||||
err = 'MySQL Error {0}: {1}'.format(*exc.args)
|
||||
__context__['mysql.error'] = err
|
||||
log.error(err)
|
||||
return False
|
||||
|
@ -1858,7 +1858,7 @@ def grant_add(grant,
|
|||
try:
|
||||
_execute(cur, qry['qry'], qry['args'])
|
||||
except (MySQLdb.OperationalError, MySQLdb.ProgrammingError) as exc:
|
||||
err = 'MySQL Error {0}: {1}'.format(*exc)
|
||||
err = 'MySQL Error {0}: {1}'.format(*exc.args)
|
||||
__context__['mysql.error'] = err
|
||||
log.error(err)
|
||||
return False
|
||||
|
@ -1934,7 +1934,7 @@ def grant_revoke(grant,
|
|||
try:
|
||||
_execute(cur, qry, args)
|
||||
except MySQLdb.OperationalError as exc:
|
||||
err = 'MySQL Error {0}: {1}'.format(*exc)
|
||||
err = 'MySQL Error {0}: {1}'.format(*exc.args)
|
||||
__context__['mysql.error'] = err
|
||||
log.error(err)
|
||||
return False
|
||||
|
|
|
@ -494,6 +494,8 @@ class BaseSaltAPIHandler(tornado.web.RequestHandler): # pylint: disable=W0223
|
|||
'''
|
||||
# timeout all the futures
|
||||
self.timeout_futures()
|
||||
# clear local_client objects to disconnect event publisher's IOStream connections
|
||||
del self.saltclients
|
||||
|
||||
def on_connection_close(self):
|
||||
'''
|
||||
|
|
|
@ -47,8 +47,8 @@ execution functions, grains, pillar, etc. They are:
|
|||
``/srv/salt/foo/bar/baz.sls``, then ``__sls__`` in that file will be
|
||||
``foo.bar.baz``.
|
||||
|
||||
The global context ``data`` (same as context ``{{ data }}`` for states written
|
||||
with Jinja + YAML). The following YAML + Jinja state declaration:
|
||||
When writing a reactor SLS file the global context ``data`` (same as context ``{{ data }}``
|
||||
for states written with Jinja + YAML) is available. The following YAML + Jinja state declaration:
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
|
|
|
@ -1995,7 +1995,7 @@ def managed(name,
|
|||
|
||||
contents_pillar
|
||||
.. versionadded:: 0.17.0
|
||||
.. versionchanged: 2016.11.0
|
||||
.. versionchanged:: 2016.11.0
|
||||
contents_pillar can also be a list, and the pillars will be
|
||||
concatinated together to form one file.
|
||||
|
||||
|
|
|
@ -661,26 +661,6 @@ class CkMinions(object):
|
|||
minions = []
|
||||
return minions
|
||||
|
||||
def _expand_matching(self, auth_entry):
|
||||
ref = {'G': 'grain',
|
||||
'P': 'grain_pcre',
|
||||
'I': 'pillar',
|
||||
'J': 'pillar_pcre',
|
||||
'L': 'list',
|
||||
'S': 'ipcidr',
|
||||
'E': 'pcre',
|
||||
'N': 'node',
|
||||
None: 'compound'}
|
||||
|
||||
target_info = parse_target(auth_entry)
|
||||
if not target_info:
|
||||
log.error('Failed to parse valid target "{0}"'.format(auth_entry))
|
||||
|
||||
v_matcher = ref.get(target_info['engine'])
|
||||
v_expr = target_info['pattern']
|
||||
|
||||
return set(self.check_minions(v_expr, v_matcher))
|
||||
|
||||
def validate_tgt(self, valid, expr, tgt_type, minions=None, expr_form=None):
|
||||
'''
|
||||
Return a Bool. This function returns if the expression sent in is
|
||||
|
@ -697,7 +677,7 @@ class CkMinions(object):
|
|||
)
|
||||
tgt_type = expr_form
|
||||
|
||||
v_minions = self._expand_matching(valid)
|
||||
v_minions = set(self.check_minions(valid, 'compound'))
|
||||
if minions is None:
|
||||
minions = set(self.check_minions(expr, tgt_type))
|
||||
else:
|
||||
|
@ -824,7 +804,7 @@ class CkMinions(object):
|
|||
continue
|
||||
allowed_minions.update(set(auth_list_entry.keys()))
|
||||
for key in auth_list_entry:
|
||||
for match in self._expand_matching(key):
|
||||
for match in set(self.check_minions(key, 'compound')):
|
||||
if match in auth_dictionary:
|
||||
auth_dictionary[match].extend(auth_list_entry[key])
|
||||
else:
|
||||
|
@ -832,7 +812,7 @@ class CkMinions(object):
|
|||
|
||||
allowed_minions_from_auth_list = set()
|
||||
for next_entry in allowed_minions:
|
||||
allowed_minions_from_auth_list.update(self._expand_matching(next_entry))
|
||||
allowed_minions_from_auth_list.update(set(self.check_minions(next_entry, 'compound')))
|
||||
# 'minions' here are all the names of minions matched by the target
|
||||
# if we take out all the allowed minions, and there are any left, then
|
||||
# the target includes minions that are not allowed by eauth
|
||||
|
|
|
@ -490,7 +490,7 @@ def valid_id(opts, id_):
|
|||
if any(x in id_ for x in ('/', '\\', '\0')):
|
||||
return False
|
||||
return bool(clean_path(opts['pki_dir'], id_))
|
||||
except (AttributeError, KeyError, TypeError):
|
||||
except (AttributeError, KeyError, TypeError, UnicodeDecodeError):
|
||||
return False
|
||||
|
||||
|
||||
|
|
|
@ -653,26 +653,47 @@ def system_information():
|
|||
else:
|
||||
return ''
|
||||
|
||||
version = system_version()
|
||||
release = platform.release()
|
||||
if platform.win32_ver()[0]:
|
||||
# Get the version and release info based on the Windows Operating
|
||||
# System Product Name. As long as Microsoft maintains a similar format
|
||||
# this should be future proof
|
||||
import win32api # pylint: disable=3rd-party-module-not-gated
|
||||
server = {'Vista': '2008Server',
|
||||
'7': '2008ServerR2',
|
||||
'8': '2012Server',
|
||||
'8.1': '2012ServerR2',
|
||||
'10': '2016Server'}
|
||||
# Starting with Python 2.7.12 and 3.5.2 the `platform.uname()` function
|
||||
# started reporting the Desktop version instead of the Server version on
|
||||
# Server versions of Windows, so we need to look those up
|
||||
# So, if you find a Server Platform that's a key in the server
|
||||
# dictionary, then lookup the actual Server Release.
|
||||
# If this is a Server Platform then `GetVersionEx` will return a number
|
||||
# greater than 1.
|
||||
if win32api.GetVersionEx(1)[8] > 1 and release in server:
|
||||
release = server[release]
|
||||
import win32con # pylint: disable=3rd-party-module-not-gated
|
||||
|
||||
# Get the product name from the registry
|
||||
hkey = win32con.HKEY_LOCAL_MACHINE
|
||||
key = 'SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion'
|
||||
value_name = 'ProductName'
|
||||
reg_handle = win32api.RegOpenKey(hkey, key)
|
||||
|
||||
# Returns a tuple of (product_name, value_type)
|
||||
product_name, _ = win32api.RegQueryValueEx(reg_handle, value_name)
|
||||
|
||||
version = 'Unknown'
|
||||
release = ''
|
||||
if 'Server' in product_name:
|
||||
for item in product_name.split(' '):
|
||||
# If it's all digits, then it's version
|
||||
if re.match(r'\d+', item):
|
||||
version = item
|
||||
# If it starts with R and then numbers, it's the release
|
||||
# ie: R2
|
||||
if re.match(r'^R\d+$', item):
|
||||
release = item
|
||||
release = '{0}Server{1}'.format(version, release)
|
||||
else:
|
||||
for item in product_name.split(' '):
|
||||
# If it's a number, decimal number, Thin or Vista, then it's the
|
||||
# version
|
||||
if re.match(r'^(\d+(\.\d+)?)|Thin|Vista$', item):
|
||||
version = item
|
||||
release = version
|
||||
|
||||
_, ver, sp, extra = platform.win32_ver()
|
||||
version = ' '.join([release, ver, sp, extra])
|
||||
else:
|
||||
version = system_version()
|
||||
release = platform.release()
|
||||
|
||||
system = [
|
||||
('system', platform.system()),
|
||||
|
@ -745,6 +766,7 @@ def msi_conformant_version():
|
|||
commi = __saltstack_version__.noc
|
||||
return '{0}.{1}.{2}.{3}'.format(year2, month, minor, commi)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if len(sys.argv) == 2 and sys.argv[1] == 'msi':
|
||||
# Building the msi requires an msi-conformant version
|
||||
|
|
|
@ -282,6 +282,17 @@ class MySQLTestCase(TestCase, LoaderModuleMockMixin):
|
|||
def test_query(self):
|
||||
self._test_call(mysql.query, 'SELECT * FROM testdb', 'testdb', 'SELECT * FROM testdb')
|
||||
|
||||
def test_query_error(self):
|
||||
connect_mock = MagicMock()
|
||||
with patch.object(mysql, '_connect', connect_mock):
|
||||
with patch.dict(mysql.__salt__, {'config.option': MagicMock()}):
|
||||
side_effect = MySQLdb.OperationalError(9999, 'Something Went Wrong')
|
||||
with patch.object(mysql, '_execute', MagicMock(side_effect=side_effect)):
|
||||
mysql.query('testdb', 'SELECT * FROM testdb')
|
||||
self.assertIn('mysql.error', mysql.__context__)
|
||||
expected = 'MySQL Error 9999: Something Went Wrong'
|
||||
self.assertEqual(mysql.__context__['mysql.error'], expected)
|
||||
|
||||
def _test_call(self, function, expected_sql, *args, **kwargs):
|
||||
connect_mock = MagicMock()
|
||||
with patch.object(mysql, '_connect', connect_mock):
|
||||
|
|
84
tests/unit/states/test_git.py
Normal file
84
tests/unit/states/test_git.py
Normal file
|
@ -0,0 +1,84 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
:codeauthor: Erik Johnson <erik@saltstack.com>
|
||||
'''
|
||||
|
||||
# Import Python libs
|
||||
from __future__ import absolute_import
|
||||
import logging
|
||||
import os
|
||||
|
||||
# Import Salt Testing Libs
|
||||
from tests.support.helpers import with_tempdir
|
||||
from tests.support.mixins import LoaderModuleMockMixin
|
||||
from tests.support.unit import TestCase, skipIf
|
||||
from tests.support.mock import (
|
||||
Mock,
|
||||
MagicMock,
|
||||
patch,
|
||||
DEFAULT,
|
||||
NO_MOCK,
|
||||
NO_MOCK_REASON,
|
||||
)
|
||||
|
||||
# Import Salt Libs
|
||||
import salt.states.git as git_state # Don't potentially shadow GitPython
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@skipIf(NO_MOCK, NO_MOCK_REASON)
|
||||
class GitTestCase(TestCase, LoaderModuleMockMixin):
|
||||
'''
|
||||
Test cases for salt.states.git
|
||||
'''
|
||||
def setup_loader_modules(self):
|
||||
return {
|
||||
git_state: {
|
||||
'__env__': 'base',
|
||||
'__opts__': {'test': False},
|
||||
'__salt__': {},
|
||||
}
|
||||
}
|
||||
|
||||
@with_tempdir()
|
||||
def test_latest_no_diff_for_bare_repo(self, target):
|
||||
'''
|
||||
This test ensures that we don't attempt to diff when cloning a repo
|
||||
using either bare=True or mirror=True.
|
||||
'''
|
||||
name = 'https://foo.com/bar/baz.git'
|
||||
gitdir = os.path.join(target, 'refs')
|
||||
isdir_mock = MagicMock(
|
||||
side_effect=lambda path: DEFAULT if path != gitdir else True)
|
||||
|
||||
branches = ['foo', 'bar', 'baz']
|
||||
tags = ['v1.1.0', 'v.1.1.1', 'v1.2.0']
|
||||
local_head = 'b9ef06ab6b7524eb7c27d740dbbd5109c6d75ee4'
|
||||
remote_head = 'eef672c1ec9b8e613905dbcd22a4612e31162807'
|
||||
|
||||
git_diff = Mock()
|
||||
dunder_salt = {
|
||||
'git.current_branch': MagicMock(return_value=branches[0]),
|
||||
'git.diff': git_diff,
|
||||
'git.fetch': MagicMock(return_value={}),
|
||||
'git.is_worktree': MagicMock(return_value=False),
|
||||
'git.list_branches': MagicMock(return_value=branches),
|
||||
'git.list_tags': MagicMock(return_value=tags),
|
||||
'git.remote_refs': MagicMock(return_value={'HEAD': remote_head}),
|
||||
'git.remotes': MagicMock(return_value={
|
||||
'origin': {'fetch': name, 'push': name},
|
||||
}),
|
||||
'git.rev_parse': MagicMock(side_effect=git_state.CommandExecutionError()),
|
||||
'git.revision': MagicMock(return_value=local_head),
|
||||
'git.version': MagicMock(return_value='1.8.3.1'),
|
||||
}
|
||||
with patch('os.path.isdir', isdir_mock), \
|
||||
patch.dict(git_state.__salt__, dunder_salt):
|
||||
result = git_state.latest(
|
||||
name=name,
|
||||
target=target,
|
||||
mirror=True, # mirror=True implies bare=True
|
||||
)
|
||||
assert result['result'] is True, result
|
||||
git_diff.assert_not_called()
|
Loading…
Add table
Reference in a new issue