Merge branch '2017.7' into fix-capirca

This commit is contained in:
Mircea Ulinic 2019-01-04 21:02:20 +00:00 committed by GitHub
commit 71913ffb12
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 264 additions and 87 deletions

View file

@ -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') {

View file

@ -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.

View file

@ -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

View file

@ -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

View file

@ -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'](

View file

@ -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

View file

@ -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

View file

@ -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):
'''

View file

@ -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

View file

@ -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.

View 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

View file

@ -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

View file

@ -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

View file

@ -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):

View 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()