Merge branch '2017.7' into 47696-do-not-enumerate-none

This commit is contained in:
Aaron C. de Bruyn 2018-05-17 21:34:53 -07:00 committed by GitHub
commit 54e9bf9ec9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
45 changed files with 507 additions and 194 deletions

2
.gitignore vendored
View File

@ -11,6 +11,8 @@ MANIFEST
*.wpr
*.wpu
*.DS_Store
.pytest_cache
Pipfile.lock
# virtualenv
# - ignores directories of a virtualenv when you create it right on

View File

@ -2,7 +2,7 @@
source 'https://rubygems.org'
gem 'test-kitchen', :git => 'https://github.com/gtmanfred/test-kitchen.git'
gem 'test-kitchen', '>=1.21.0'
gem 'kitchen-salt', :git => 'https://github.com/saltstack/kitchen-salt.git'
gem 'kitchen-sync'
gem 'git'

40
Pipfile Normal file
View File

@ -0,0 +1,40 @@
[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"
[packages]
Jinja2 = "*"
msgpack-python = ">0.3,!=0.5.5"
PyYAML = "*"
MarkupSafe = "*"
requests = ">=1.0.0"
tornado = ">=4.2.1,<5.0"
pycrypto = ">=2.6.1"
pyzmq = ">=2.2.0"
[dev-packages]
mock = ">=2.0.0"
apache-libcloud = ">=0.14.0"
boto = ">=2.32.1"
boto3 = ">=1.2.1"
moto = ">=0.3.6"
SaltPyLint = ">=v2017.3.6"
pytest = ">=3.5.0"
[packages.futures]
# Required by Tornado to handle threads stuff.
version = ">=2.0"
markers = "python_version < '3.0'"
[dev-packages.pytest-salt]
git = "git://github.com/saltstack/pytest-salt.git"
ref = "master"
[dev-packages.httpretty]
# httpretty Needs to be here for now even though it's a dependency of boto.
# A pip install on a fresh system will decide to target httpretty 0.8.10 to
# satisfy other requirements, and httpretty 0.8.10 has bugs in setup.py that
# prevent it from being successfully installed (at least on Python 3.4).
version = "*"
markers = "python_version >= '3.4'"

View File

@ -6,7 +6,7 @@ Debian GNU/Linux / Raspbian
Debian GNU/Linux distribution and some derivatives such as Raspbian already
have included Salt packages to their repositories. However, current stable
release codenamed "Jessie" contains old outdated Salt release. It is
Debian release contains old outdated Salt releases. It is
recommended to use SaltStack repository for Debian as described
:ref:`below <installation-debian-repo>`.
@ -33,11 +33,13 @@ Instructions are at https://repo.saltstack.com/#debian.
Installation from the Debian / Raspbian Official Repository
===========================================================
Stretch (Testing) and Sid (Unstable) distributions are already contain mostly
up-to-date Salt packages built by Debian Salt Team. You can install Salt
components directly from Debian.
The Debian distributions contain mostly old Salt packages
built by the Debian Salt Team. You can install Salt
components directly from Debian but it is recommended to
use the instructions above for the packages from the official
Salt repository.
On Jessie (Stable) there is an option to install Salt minion from Stretch with
On Jessie there is an option to install Salt minion from Stretch with
`python-tornado` dependency from `jessie-backports` repositories.
To install fresh release of Salt minion on Jessie:
@ -79,7 +81,7 @@ To install fresh release of Salt minion on Jessie:
apt-get update
apt-get install python-zmq python-tornado/stretch salt-common/stretch
#. Install Salt minion package from Stretch:
#. Install Salt minion package from Latest Debian Release:
.. code-block:: bash

View File

@ -600,15 +600,24 @@ repository to be served up from the Salt fileserver path
Mountpoints can also be configured on a :ref:`per-remote basis
<gitfs-per-remote-config>`.
Using gitfs in Masterless Mode
==============================
Since 2014.7.0, gitfs can be used in masterless mode. To do so, simply add the
gitfs configuration parameters (and set :conf_master:`fileserver_backend`) in
the _minion_ config file instead of the master config file.
Using gitfs Alongside Other Backends
====================================
Sometimes it may make sense to use multiple backends; for instance, if ``sls``
files are stored in git but larger files are stored directly on the master.
The cascading lookup logic used for multiple remotes is also used with
multiple backends. If the ``fileserver_backend`` option contains
multiple backends:
The cascading lookup logic used for multiple remotes is also used with multiple
backends. If the :conf_master:`fileserver_backend` option contains multiple
backends:
.. code-block:: yaml
@ -620,7 +629,6 @@ Then the ``roots`` backend (the default backend of files in ``/srv/salt``) will
be searched first for the requested file; then, if it is not found on the
master, each configured git remote will be searched.
Branches, Environments, and Top Files
=====================================

4
pytest.ini Normal file
View File

@ -0,0 +1,4 @@
[pytest]
addopts = --ssh-tests -ra -sv
testpaths = tests
norecursedirs = tests/kitchen

View File

@ -1,4 +0,0 @@
-r base.txt
# Required by Tornado to handle threads stuff.
futures>=2.0

View File

@ -1 +0,0 @@
-r base.txt

View File

@ -7,4 +7,4 @@ MarkupSafe
requests>=1.0.0
tornado>=4.2.1,<5.0
# Required by Tornado to handle threads stuff.
futures>=2.0
futures>=2.0; python_version < '3.0'

16
requirements/dev.txt Normal file
View File

@ -0,0 +1,16 @@
-r base.txt
mock>=2.0.0
apache-libcloud>=0.14.0
boto>=2.32.1
boto3>=1.2.1
moto>=0.3.6
SaltPyLint>=v2017.3.6
pytest>=3.5.0
git+https://github.com/saltstack/pytest-salt.git@master#egg=pytest-salt
# httpretty Needs to be here for now even though it's a dependency of boto.
# A pip install on a fresh system will decide to target httpretty 0.8.10 to
# satisfy other requirements, and httpretty 0.8.10 has bugs in setup.py that
# prevent it from being successfully installed (at least on Python 3.4).
httpretty; python_version >= '3.4'

View File

@ -1,11 +1,2 @@
-r base-py2.txt
mock>=2.0.0
apache-libcloud>=0.14.0
boto>=2.32.1
boto3>=1.2.1
moto>=0.3.6
SaltPyLint>=v2017.3.6
pytest>=3.5.0
git+https://github.com/eisensheng/pytest-catchlog.git@develop#egg=Pytest-catchlog
git+https://github.com/saltstack/pytest-salt.git@master#egg=pytest-salt
# This is a legacy file, use dev.txt
-r dev.txt

View File

@ -1,15 +1,2 @@
-r base-py3.txt
mock>=2.0.0
apache-libcloud>=0.14.0
boto>=2.32.1
boto3>=1.2.1
moto>=0.3.6
# httpretty Needs to be here for now even though it's a dependency of boto.
# A pip install on a fresh system will decide to target httpretty 0.8.10 to
# satisfy other requirements, and httpretty 0.8.10 has bugs in setup.py that
# prevent it from being successfully installed (at least on Python 3.4).
httpretty
SaltPyLint>=v2017.2.29
pytest>=3.5.0
git+https://github.com/saltstack/pytest-salt.git@master#egg=pytest-salt
# This is a legacy file, use dev.txt
-r dev.txt

View File

@ -1910,7 +1910,7 @@ class Map(Cloud):
pmap = self.map_providers_parallel(cached=cached)
exist = set()
defined = set()
for profile_name, nodes in six.iteritems(self.rendered_map):
for profile_name, nodes in six.iteritems(copy.deepcopy(self.rendered_map)):
if profile_name not in self.opts['profiles']:
msg = (
'The required profile, \'{0}\', defined in the map '
@ -1931,13 +1931,13 @@ class Map(Cloud):
# Get associated provider data, in case something like size
# or image is specified in the provider file. See issue #32510.
alias, driver = profile_data.get('provider').split(':')
provider_details = self.opts['providers'][alias][driver].copy()
provider_details = copy.deepcopy(self.opts['providers'][alias][driver])
del provider_details['profiles']
# Update the provider details information with profile data
# Profile data should override provider data, if defined.
# This keeps map file data definitions consistent with -p usage.
provider_details.update(profile_data)
salt.utils.dictupdate.update(provider_details, profile_data)
profile_data = provider_details
for nodename, overrides in six.iteritems(nodes):

View File

@ -41,6 +41,7 @@ from salt.ext.six.moves.urllib.parse import urlparse, urlunparse
# pylint: enable=no-name-in-module,import-error
log = logging.getLogger(__name__)
MAX_FILENAME_LENGTH = 255
def get_file_client(opts, pillar=False):
@ -799,6 +800,9 @@ class Client(object):
else:
file_name = url_data.path
if len(file_name) > MAX_FILENAME_LENGTH:
file_name = salt.utils.hashutils.sha256_digest(file_name)
return salt.utils.path_join(
cachedir,
'extrn_files',

View File

@ -1106,6 +1106,7 @@ _OS_NAME_MAP = {
'synology': 'Synology',
'nilrt': 'NILinuxRT',
'nilrt-xfce': 'NILinuxRT-XFCE',
'poky': 'Poky',
'manjaro': 'Manjaro',
'manjarolin': 'Manjaro',
'antergos': 'Antergos',
@ -1647,7 +1648,7 @@ def os_data():
osarch = __salt__['cmd.run']('dpkg --print-architecture').strip()
elif grains.get('os_family') == 'RedHat':
osarch = __salt__['cmd.run']('rpm --eval %{_host_cpu}').strip()
elif grains.get('os_family') == 'NILinuxRT':
elif grains.get('os_family') in ('NILinuxRT', 'Poky'):
archinfo = {}
for line in __salt__['cmd.run']('opkg print-architecture').splitlines():
if line.startswith('arch'):

View File

@ -91,7 +91,7 @@ def _retrieve_device_cache(proxy=None):
DEVICE_CACHE = proxy['napalm.get_device']()
elif not proxy and salt.utils.napalm.is_minion(__opts__):
# if proxy var not passed and is running in a straight minion
DEVICE_CACHE = salt.utils.napalm.get_device_opts(__opts__)
DEVICE_CACHE = salt.utils.napalm.get_device(__opts__)
return DEVICE_CACHE

View File

@ -103,6 +103,20 @@ class SysLogHandler(ExcInfoOnLogLevelFormatMixIn, logging.handlers.SysLogHandler
'''
Syslog handler which properly handles exc_info on a per handler basis
'''
def handleError(self, record):
'''
Override the default error handling mechanism for py3
Deal with syslog os errors when the log file does not exist
'''
handled = False
if sys.stderr and sys.version_info >= (3, 5, 4):
t, v, tb = sys.exc_info()
if t.__name__ in 'FileNotFoundError':
sys.stderr.write('[WARNING ] The log_file does not exist. Logging not setup correctly or syslog service not started.\n')
handled = True
if not handled:
super(SysLogHandler, self).handleError(record)
class RotatingFileHandler(ExcInfoOnLogLevelFormatMixIn, logging.handlers.RotatingFileHandler, NewStyleClassMixIn):

View File

@ -544,6 +544,10 @@ def associate_vpc_with_hosted_zone(HostedZoneId=None, Name=None, VPCId=None,
r = conn.associate_vpc_with_hosted_zone(**args)
return _wait_for_sync(r['ChangeInfo']['Id'], conn)
except ClientError as e:
if e.response.get('Error', {}).get('Code') == 'ConflictingDomainExists':
log.debug('VPC Association already exists.')
# return True since the current state is the desired one
return True
if tries and e.response.get('Error', {}).get('Code') == 'Throttling':
log.debug('Throttled by AWS API.')
time.sleep(3)

View File

@ -478,10 +478,18 @@ def _run(cmd,
env_runas = dict((sdecode(k), sdecode(v)) for k, v in six.iteritems(env_runas))
env_runas.update(env)
# Fix platforms like Solaris that don't set a USER env var in the
# user's default environment as obtained above.
if env_runas.get('USER') != runas:
env_runas['USER'] = runas
# Fix some corner cases where shelling out to get the user's
# environment returns the wrong home directory.
runas_home = os.path.expanduser('~{0}'.format(runas))
if env_runas.get('HOME') != runas_home:
env_runas['HOME'] = runas_home
env = env_runas
# Encode unicode kwargs to filesystem encoding to avoid a
# UnicodeEncodeError when the subprocess is invoked.

View File

@ -496,7 +496,7 @@ def enabled(name, runas=None):
return False
def disabled(name, runas=None):
def disabled(name, runas=None, domain='system'):
'''
Check if the specified service is not enabled. This is the opposite of
``service.enabled``
@ -505,6 +505,8 @@ def disabled(name, runas=None):
:param str runas: User to run launchctl commands
:param str domain: domain to check for disabled services. Default is system.
:return: True if the specified service is NOT enabled, otherwise False
:rtype: bool
@ -514,8 +516,22 @@ def disabled(name, runas=None):
salt '*' service.disabled org.cups.cupsd
'''
# A service is disabled if it is not enabled
return not enabled(name, runas=runas)
ret = False
disabled = launchctl('print-disabled',
domain,
return_stdout=True,
output_loglevel='trace',
runas=runas)
for service in disabled.split("\n"):
if name in service:
srv_name = service.split("=>")[0].split("\"")[1]
status = service.split("=>")[1]
if name != srv_name:
pass
else:
return True if 'true' in status.lower() else False
return False
def get_all(runas=None):

View File

@ -56,9 +56,9 @@ def __virtual__():
'''
Confirm this module is on a nilrt based system
'''
if __grains__.get('os_family', False) == 'NILinuxRT':
if os.path.isdir(OPKG_CONFDIR):
return __virtualname__
return (False, "Module opkg only works on nilrt based systems")
return False, "Module opkg only works on OpenEmbedded based systems"
def latest_version(*names, **kwargs):

View File

@ -452,13 +452,10 @@ def install(pkgs=None, # pylint: disable=R0912,R0913,R0914
Path to requirements
bin_env
Path to pip bin or path to virtualenv. If doing a system install,
and want to use a specific pip bin (pip-2.7, pip-2.6, etc..) just
specify the pip bin you want.
.. note::
If installing into a virtualenv, just use the path to the
virtualenv (e.g. ``/home/code/path/to/virtualenv/``)
Path to pip (or to a virtualenv). This can be used to specify the path
to the pip to use when more than one Python release is installed (e.g.
``/usr/bin/pip-2.7`` or ``/usr/bin/pip-2.6``. If a directory path is
specified, it is assumed to be a virtualenv.
use_wheel
Prefer wheel archives (requires pip>=1.4)
@ -561,7 +558,7 @@ def install(pkgs=None, # pylint: disable=R0912,R0913,R0914
The user under which to run pip
cwd
Current working directory to run pip from
Directory from which to run pip
pre_releases
Include pre-releases in the available versions
@ -931,36 +928,38 @@ def uninstall(pkgs=None,
saltenv='base',
use_vt=False):
'''
Uninstall packages with pip
Uninstall packages individually or from a pip requirements file. Uninstall
packages globally or from a virtualenv.
Uninstall packages individually or from a pip requirements file
pkgs
comma separated list of packages to install
requirements
path to requirements.
Path to requirements file
bin_env
path to pip bin or path to virtualenv. If doing an uninstall from
the system python and want to use a specific pip bin (pip-2.7,
pip-2.6, etc..) just specify the pip bin you want.
If uninstalling from a virtualenv, just use the path to the virtualenv
(/home/code/path/to/virtualenv/)
Path to pip (or to a virtualenv). This can be used to specify the path
to the pip to use when more than one Python release is installed (e.g.
``/usr/bin/pip-2.7`` or ``/usr/bin/pip-2.6``. If a directory path is
specified, it is assumed to be a virtualenv.
log
Log file where a complete (maximum verbosity) record will be kept
proxy
Specify a proxy in the form
user:passwd@proxy.server:port. Note that the
user:password@ is optional and required only if you
are behind an authenticated proxy. If you provide
user@proxy.server:port then you will be prompted for a
password.
Specify a proxy in the format ``user:passwd@proxy.server:port``. Note
that the ``user:password@`` is optional and required only if you are
behind an authenticated proxy. If you provide
``user@proxy.server:port`` then you will be prompted for a password.
timeout
Set the socket timeout (default 15 seconds)
user
The user under which to run pip
cwd
Current working directory to run pip from
Directory from which to run pip
use_vt
Use VT terminal emulation (see output while installing)
@ -972,7 +971,6 @@ def uninstall(pkgs=None,
salt '*' pip.uninstall requirements=/path/to/requirements.txt
salt '*' pip.uninstall <package name> bin_env=/path/to/virtualenv
salt '*' pip.uninstall <package name> bin_env=/path/to/pip_bin
'''
cmd = _get_pip_bin(bin_env)
cmd.extend(['uninstall', '-y'])
@ -1054,32 +1052,27 @@ def freeze(bin_env=None,
virtualenv
bin_env
path to pip bin or path to virtualenv. If doing an uninstall from
the system python and want to use a specific pip bin (pip-2.7,
pip-2.6, etc..) just specify the pip bin you want.
If uninstalling from a virtualenv, just use the path to the virtualenv
(/home/code/path/to/virtualenv/)
Path to pip (or to a virtualenv). This can be used to specify the path
to the pip to use when more than one Python release is installed (e.g.
``/usr/bin/pip-2.7`` or ``/usr/bin/pip-2.6``. If a directory path is
specified, it is assumed to be a virtualenv.
user
The user under which to run pip
cwd
Current working directory to run pip from
Directory from which to run pip
.. note::
If the version of pip available is older than 8.0.3, the list will not
include the packages pip, wheel, setuptools, or distribute even if they
are installed.
include the packages ``pip``, ``wheel``, ``setuptools``, or
``distribute`` even if they are installed.
CLI Example:
.. code-block:: bash
salt '*' pip.freeze /home/code/path/to/virtualenv/
.. versionchanged:: 2016.11.2
The packages pip, wheel, setuptools, and distribute are included if the
installed pip is new enough.
salt '*' pip.freeze bin_env=/home/code/path/to/virtualenv
'''
cmd = _get_pip_bin(bin_env)
cmd.append('freeze')
@ -1124,21 +1117,16 @@ def list_(prefix=None,
.. note::
If the version of pip available is older than 8.0.3, the packages
wheel, setuptools, and distribute will not be reported by this function
even if they are installed. Unlike
:py:func:`pip.freeze <salt.modules.pip.freeze>`, this function always
reports the version of pip which is installed.
``wheel``, ``setuptools``, and ``distribute`` will not be reported by
this function even if they are installed. Unlike :py:func:`pip.freeze
<salt.modules.pip.freeze>`, this function always reports the version of
pip which is installed.
CLI Example:
.. code-block:: bash
salt '*' pip.list salt
.. versionchanged:: 2016.11.2
The packages wheel, setuptools, and distribute are included if the
installed pip is new enough.
'''
packages = {}
@ -1392,9 +1380,10 @@ def list_all_versions(pkg,
The package to check
bin_env
Path to pip bin or path to virtualenv. If doing a system install,
and want to use a specific pip bin (pip-2.7, pip-2.6, etc..) just
specify the pip bin you want.
Path to pip (or to a virtualenv). This can be used to specify the path
to the pip to use when more than one Python release is installed (e.g.
``/usr/bin/pip-2.7`` or ``/usr/bin/pip-2.6``. If a directory path is
specified, it is assumed to be a virtualenv.
include_alpha
Include alpha versions in the list
@ -1409,7 +1398,7 @@ def list_all_versions(pkg,
The user under which to run pip
cwd
Current working directory to run pip from
Directory from which to run pip
CLI Example:

View File

@ -939,7 +939,7 @@ def diskusage(*args):
elif __grains__['kernel'] in ('FreeBSD', 'SunOS'):
ifile = __salt__['cmd.run']('mount -p').splitlines()
else:
ifile = []
raise CommandExecutionError('status.diskusage not yet supported on this platform')
for line in ifile:
comps = line.split()

View File

@ -85,15 +85,28 @@ def serialize(obj, **options):
raise SerializationError(error)
def _read_dict(configparser, dictionary):
def _is_defaultsect(section_name):
if six.PY3:
return section_name == configparser.DEFAULTSECT
else: # in py2 the check is done against lowercased section name
return section_name.upper() == configparser.DEFAULTSECT
def _read_dict(cp, dictionary):
'''
Cribbed from python3's ConfigParser.read_dict function.
'''
for section, keys in dictionary.items():
section = str(section)
configparser.add_section(section)
if _is_defaultsect(section):
if six.PY2:
section = configparser.DEFAULTSECT
else:
cp.add_section(section)
for key, value in keys.items():
key = configparser.optionxform(str(key))
key = cp.optionxform(str(key))
if value is not None:
value = str(value)
configparser.set(section, key, value)
cp.set(section, key, value)

View File

@ -11,6 +11,7 @@
from __future__ import absolute_import
import datetime
import logging
import yaml
from yaml.constructor import ConstructorError
@ -22,6 +23,8 @@ from salt.utils.odict import OrderedDict
__all__ = ['deserialize', 'serialize', 'available']
log = logging.getLogger(__name__)
available = True
# prefer C bindings over python when available
@ -46,14 +49,17 @@ def deserialize(stream_or_string, **options):
try:
return yaml.load(stream_or_string, **options)
except ScannerError as error:
log.exception('Error encountered while deserializing')
err_type = ERROR_MAP.get(error.problem, 'Unknown yaml render error')
line_num = error.problem_mark.line + 1
raise DeserializationError(err_type,
line_num,
error.problem_mark.buffer)
except ConstructorError as error:
log.exception('Error encountered while deserializing')
raise DeserializationError(error)
except Exception as error:
log.exception('Error encountered while deserializing')
raise DeserializationError(error)
@ -74,6 +80,7 @@ def serialize(obj, **options):
return response[:-1]
return response
except Exception as error:
log.exception('Error encountered while serializing')
raise SerializationError(error)
@ -93,7 +100,6 @@ Loader.add_multi_constructor('tag:yaml.org,2002:set', Loader.construct_yaml_set)
Loader.add_multi_constructor('tag:yaml.org,2002:str', Loader.construct_yaml_str)
Loader.add_multi_constructor('tag:yaml.org,2002:seq', Loader.construct_yaml_seq)
Loader.add_multi_constructor('tag:yaml.org,2002:map', Loader.construct_yaml_map)
Loader.add_multi_constructor(None, Loader.construct_undefined)
class Dumper(BaseDumper): # pylint: disable=W0232

View File

@ -150,14 +150,17 @@ def deserialize(stream_or_string, **options):
try:
return yaml.load(stream_or_string, **options)
except ScannerError as error:
log.exception('Error encountered while deserializing')
err_type = ERROR_MAP.get(error.problem, 'Unknown yaml render error')
line_num = error.problem_mark.line + 1
raise DeserializationError(err_type,
line_num,
error.problem_mark.buffer)
except ConstructorError as error:
log.exception('Error encountered while deserializing')
raise DeserializationError(error)
except Exception as error:
log.exception('Error encountered while deserializing')
raise DeserializationError(error)
@ -178,6 +181,7 @@ def serialize(obj, **options):
return response[:-1]
return response
except Exception as error:
log.exception('Error encountered while serializing')
raise SerializationError(error)
@ -322,7 +326,6 @@ Loader.add_multi_constructor('tag:yaml.org,2002:pairs', Loader.construct_yaml_pa
Loader.add_multi_constructor('tag:yaml.org,2002:set', Loader.construct_yaml_set)
Loader.add_multi_constructor('tag:yaml.org,2002:seq', Loader.construct_yaml_seq)
Loader.add_multi_constructor('tag:yaml.org,2002:map', Loader.construct_yaml_map)
Loader.add_multi_constructor(None, Loader.construct_undefined)
class SLSMap(OrderedDict):

View File

@ -3255,43 +3255,45 @@ class BaseHighState(object):
'Specified SLS {0} on local filesystem cannot '
'be found.'.format(sls)
)
state = None
if not fn_:
errors.append(
'Specified SLS {0} in saltenv {1} is not '
'available on the salt master or through a configured '
'fileserver'.format(sls, saltenv)
)
state = None
try:
state = compile_template(fn_,
self.state.rend,
self.state.opts['renderer'],
self.state.opts['renderer_blacklist'],
self.state.opts['renderer_whitelist'],
saltenv,
sls,
rendered_sls=mods
)
except SaltRenderError as exc:
msg = 'Rendering SLS \'{0}:{1}\' failed: {2}'.format(
saltenv, sls, exc
)
log.critical(msg)
errors.append(msg)
except Exception as exc:
msg = 'Rendering SLS {0} failed, render error: {1}'.format(
sls, exc
)
log.critical(
msg,
# Show the traceback if the debug logging level is enabled
exc_info_on_loglevel=logging.DEBUG
)
errors.append('{0}\n{1}'.format(msg, traceback.format_exc()))
try:
mods.add('{0}:{1}'.format(saltenv, sls))
except AttributeError:
pass
else:
try:
state = compile_template(fn_,
self.state.rend,
self.state.opts['renderer'],
self.state.opts['renderer_blacklist'],
self.state.opts['renderer_whitelist'],
saltenv,
sls,
rendered_sls=mods
)
except SaltRenderError as exc:
msg = 'Rendering SLS \'{0}:{1}\' failed: {2}'.format(
saltenv, sls, exc
)
log.critical(msg)
errors.append(msg)
except Exception as exc:
msg = 'Rendering SLS {0} failed, render error: {1}'.format(
sls, exc
)
log.critical(
msg,
# Show the traceback if the debug logging level is enabled
exc_info_on_loglevel=logging.DEBUG
)
errors.append('{0}\n{1}'.format(msg, traceback.format_exc()))
try:
mods.add('{0}:{1}'.format(saltenv, sls))
except AttributeError:
pass
if state:
if not isinstance(state, dict):
errors.append(

View File

@ -56,10 +56,13 @@ def run_file(name,
grain=None,
key=None,
overwrite=True,
saltenv=None,
**connection_args):
'''
Execute an arbitrary query on the specified database
.. versionadded:: 2017.7.0
name
Used only as an ID
@ -84,13 +87,17 @@ def run_file(name,
overwrite:
The file or grain will be overwritten if it already exists (default)
.. versionadded:: 2017.7.0
saltenv:
The saltenv to pull the query_file from
'''
ret = {'name': name,
'changes': {},
'result': True,
'comment': 'Database {0} is already present'.format(database)}
if any([query_file.startswith(proto) for proto in ['http://', 'https://', 'salt://', 's3://', 'swift://']]):
query_file = __salt__['cp.cache_file'](query_file, saltenv=saltenv or __env__)
if not os.path.exists(query_file):
ret['comment'] = 'File {0} does not exist'.format(query_file)
ret['result'] = False

View File

@ -319,7 +319,6 @@ def managed(name, ppa=None, **kwargs):
enabled = True
repo = name
os_family = __grains__['os_family'].lower()
if __grains__['os'] in ('Ubuntu', 'Mint'):
if ppa is not None:
# overload the name/repo value for PPAs cleanly
@ -333,7 +332,7 @@ def managed(name, ppa=None, **kwargs):
if enabled is not None \
else salt.utils.is_true(disabled)
elif os_family in ('redhat', 'suse'):
elif __grains__['os_family'] in ('RedHat', 'Suse'):
if 'humanname' in kwargs:
kwargs['name'] = kwargs.pop('humanname')
if 'name' not in kwargs:
@ -344,7 +343,7 @@ def managed(name, ppa=None, **kwargs):
if disabled is not None \
else salt.utils.is_true(enabled)
elif os_family == 'nilinuxrt':
elif __grains__['os_family'] in ('NILinuxRT', 'Poky'):
# opkg is the pkg virtual
kwargs['enabled'] = not salt.utils.is_true(disabled) \
if disabled is not None \
@ -373,7 +372,7 @@ def managed(name, ppa=None, **kwargs):
else:
sanitizedkwargs = kwargs
if os_family == 'debian':
if __grains__['os_family'] == 'Debian':
repo = salt.utils.pkg.deb.strip_uri(repo)
if pre:
@ -387,7 +386,7 @@ def managed(name, ppa=None, **kwargs):
# not explicitly set, so we don't need to update the repo
# if it's desired to be enabled and the 'enabled' key is
# missing from the repo definition
if os_family == 'redhat':
if __grains__['os_family'] == 'RedHat':
if not salt.utils.is_true(sanitizedkwargs[kwarg]):
break
else:
@ -397,7 +396,7 @@ def managed(name, ppa=None, **kwargs):
elif kwarg == 'comps':
if sorted(sanitizedkwargs[kwarg]) != sorted(pre[kwarg]):
break
elif kwarg == 'line' and os_family == 'debian':
elif kwarg == 'line' and __grains__['os_family'] == 'Debian':
# split the line and sort everything after the URL
sanitizedsplit = sanitizedkwargs[kwarg].split()
sanitizedsplit[3:] = sorted(sanitizedsplit[3:])
@ -412,14 +411,14 @@ def managed(name, ppa=None, **kwargs):
salt.utils.pkg.deb.combine_comments(kwargs['comments'])
if pre_comments != post_comments:
break
elif kwarg == 'comments' and os_family == 'redhat':
elif kwarg == 'comments' and __grains__['os_family'] == 'RedHat':
precomments = salt.utils.pkg.rpm.combine_comments(pre[kwarg])
kwargcomments = salt.utils.pkg.rpm.combine_comments(
sanitizedkwargs[kwarg])
if precomments != kwargcomments:
break
else:
if os_family in ('redhat', 'suse') \
if __grains__['os_family'] in ('RedHat', 'Suse') \
and any(isinstance(x, bool) for x in
(sanitizedkwargs[kwarg], pre[kwarg])):
# This check disambiguates 1/0 from True/False
@ -450,7 +449,7 @@ def managed(name, ppa=None, **kwargs):
pass
try:
if os_family == 'debian':
if __grains__['os_family'] == 'Debian':
__salt__['pkg.mod_repo'](repo, saltenv=__env__, **kwargs)
else:
__salt__['pkg.mod_repo'](repo, **kwargs)

View File

@ -941,11 +941,13 @@ class Schedule(object):
else:
# Send back to master so the job is included in the job list
mret = ret.copy()
mret['jid'] = 'req'
if data.get('return_job') == 'nocache':
# overwrite 'req' to signal to master that
# this job shouldn't be stored
mret['jid'] = 'nocache'
# No returners defined, so we're only sending back to the master
if not data_returner and not self.schedule_returner:
mret['jid'] = 'req'
if data.get('return_job') == 'nocache':
# overwrite 'req' to signal to master that
# this job shouldn't be stored
mret['jid'] = 'nocache'
load = {'cmd': '_return', 'id': self.opts['id']}
for key, value in six.iteritems(mret):
load[key] = value

View File

@ -102,6 +102,14 @@ def pytest_addoption(parser):
'SSH server on your machine. In certain environments, this '
'may be insecure! Default: False'
)
test_selection_group.addoption(
'--proxy',
'--proxy-tests',
dest='proxy',
action='store_true',
default=False,
help='Run proxy tests'
)
test_selection_group.addoption(
'--run-destructive',
action='store_true',
@ -640,7 +648,8 @@ def test_daemon(request):
('sysinfo', request.config.getoption('--sysinfo')),
('no_colors', request.config.getoption('--no-colors')),
('output_columns', request.config.getoption('--output-columns')),
('ssh', request.config.getoption('--ssh')))
('ssh', request.config.getoption('--ssh')),
('proxy', request.config.getoption('--proxy')))
options = namedtuple('options', [n for n, v in values])(*[v for n, v in values])
fake_parser = namedtuple('parser', 'options')(options)

View File

@ -6,7 +6,7 @@ ec2-test:
script_args: '-P -Z'
ec2-win2012r2-test:
provider: ec2-config
size: t2.micro
size: m1.small
image: ami-eb1ecd96
smb_port: 445
win_installer: ''
@ -20,7 +20,7 @@ ec2-win2012r2-test:
deploy: True
ec2-win2016-test:
provider: ec2-config
size: t2.micro
size: m1.small
image: ami-ed14c790
smb_port: 445
win_installer: ''

View File

@ -10,6 +10,7 @@ from tests.support.unit import skipIf
# Import Salt libs
import salt.utils
import salt.utils.systemd
@destructiveTest
@ -101,7 +102,38 @@ class ServiceModuleTest(ModuleCase):
self.assertTrue(self.run_function('service.enable', [self.service_name]))
self.assertTrue(self.run_function('service.disable', [self.service_name]))
self.assertIn(self.service_name, self.run_function('service.get_disabled'))
if salt.utils.is_darwin():
self.assertTrue(self.run_function('service.disabled', [self.service_name]))
else:
self.assertIn(self.service_name, self.run_function('service.get_disabled'))
def test_service_disable_doesnot_exist(self):
'''
test service.get_disabled and service.disable module
when service name does not exist
'''
# enable service before test
srv_name = 'doesnotexist'
enable = self.run_function('service.enable', [srv_name])
systemd = salt.utils.systemd.booted()
# check service was not enabled
if systemd:
self.assertIn('ERROR', enable)
else:
self.assertFalse(enable)
# check service was not disabled
if tuple(self.run_function('grains.item', ['osrelease_info'])['osrelease_info']) == (14, 0o4) and not systemd:
# currently upstart does not have a mechanism to report if disabling a service fails if does not exist
self.assertTrue(self.run_function('service.disable', [srv_name]))
else:
self.assertFalse(self.run_function('service.disable', [srv_name]))
if salt.utils.is_darwin():
self.assertFalse(self.run_function('service.disabled', [srv_name]))
else:
self.assertNotIn(srv_name, self.run_function('service.get_disabled'))
@skipIf(not salt.utils.is_windows(), 'Windows Only Test')
def test_service_get_service_name(self):

View File

@ -49,7 +49,9 @@ class StatusModuleTest(ModuleCase):
status.diskusage
'''
ret = self.run_function('status.diskusage')
if salt.utils.is_windows():
if salt.utils.is_darwin():
self.assertIn('not yet supported on this platform', ret)
elif salt.utils.is_windows():
self.assertTrue(isinstance(ret['percent'], float))
else:
self.assertIn('total', str(ret))

View File

@ -340,6 +340,48 @@ class CallTest(ShellCase, testprogram.TestProgramCase, ShellCaseCommonTestsMixin
if os.path.isdir(config_dir):
shutil.rmtree(config_dir)
def test_syslog_file_not_found(self):
'''
test when log_file is set to a syslog file that does not exist
'''
old_cwd = os.getcwd()
config_dir = os.path.join(TMP, 'log_file_incorrect')
if not os.path.isdir(config_dir):
os.makedirs(config_dir)
os.chdir(config_dir)
with salt.utils.fopen(self.get_config_file_path('minion'), 'r') as fh_:
minion_config = yaml.load(fh_.read())
minion_config['log_file'] = 'file:///dev/doesnotexist'
with salt.utils.fopen(os.path.join(config_dir, 'minion'), 'w') as fh_:
fh_.write(
yaml.dump(minion_config, default_flow_style=False)
)
ret = self.run_script(
'salt-call',
'--config-dir {0} cmd.run "echo foo"'.format(
config_dir
),
timeout=60,
catch_stderr=True,
with_retcode=True
)
try:
if sys.version_info >= (3, 5, 4):
self.assertIn('local:', ret[0])
self.assertIn('[WARNING ] The log_file does not exist. Logging not setup correctly or syslog service not started.', ret[1])
self.assertEqual(ret[2], 0)
else:
self.assertIn(
'Failed to setup the Syslog logging handler', '\n'.join(ret[1])
)
self.assertEqual(ret[2], 2)
finally:
self.chdir(old_cwd)
if os.path.isdir(config_dir):
shutil.rmtree(config_dir)
def test_issue_15074_output_file_append(self):
output_file_append = os.path.join(TMP, 'issue-15074')
try:

View File

@ -3,7 +3,7 @@
:codeauthor: :email:`Pedro Algarvio (pedro@algarvio.me)`
tests.integration.states.pip
tests.integration.states.pip_state
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
'''
@ -317,7 +317,7 @@ class PipStateTest(ModuleCase, SaltReturnAssertsMixin):
# pip install passing the package name in `name`
ret = self.run_state(
'pip.installed', name='pep8', user=username, bin_env=venv_dir,
no_cache_dir=True, password='PassWord1!')
password='PassWord1!')
self.assertSaltTrueReturn(ret)
if HAS_PWD:
@ -362,12 +362,12 @@ class PipStateTest(ModuleCase, SaltReturnAssertsMixin):
req_filename = os.path.join(
RUNTIME_VARS.TMP_STATE_TREE, 'issue-6912-requirements.txt')
with salt.utils.fopen(req_filename, 'wb') as reqf:
reqf.write(six.b('pep8'))
reqf.write(b'pep8')
ret = self.run_state(
'pip.installed', name='', user=username, bin_env=venv_dir,
requirements='salt://issue-6912-requirements.txt',
no_cache_dir=True, password='PassWord1!')
password='PassWord1!')
self.assertSaltTrueReturn(ret)
if HAS_PWD:
@ -452,7 +452,7 @@ class PipStateTest(ModuleCase, SaltReturnAssertsMixin):
RUNTIME_VARS.TMP_PRODENV_STATE_TREE, 'prod-env-requirements.txt'
)
with salt.utils.fopen(requirements_file, 'wb') as reqf:
reqf.write(six.b('pep8\n'))
reqf.write(b'pep8\n')
try:
self.run_function('virtualenv.create', [venv_dir])

View File

@ -44,12 +44,6 @@ class UserTest(ModuleCase, SaltReturnAssertsMixin):
user_name = 'salt_test'
user_home = '/var/lib/salt_test'
def setUp(self):
if salt.utils.is_darwin():
#on mac we need to add user, because there is
#no creationtime for nobody user.
add_user = self.run_function('user.add', [USER], gid=GID)
def test_user_absent(self):
ret = self.run_state('user.absent', name='unpossible')
self.assertSaltTrueReturn(ret)

View File

@ -29,6 +29,7 @@ import salt.ext.six as six
from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin
from tests.support.unit import TestCase
from tests.support.helpers import win32_kill_process_tree
from tests.support.paths import CODE_DIR
from tests.support.processes import terminate_process, terminate_process_list
@ -414,9 +415,6 @@ class TestProgram(six.with_metaclass(TestProgramMeta, object)):
popen_kwargs['preexec_fn'] = detach_from_parent_group
elif sys.platform.lower().startswith('win') and timeout is not None:
raise RuntimeError('Timeout is not supported under windows')
self.argv = [self.program]
self.argv.extend(args)
log.debug('TestProgram.run: %s Environment %s', self.argv, env_delta)
@ -431,16 +429,26 @@ class TestProgram(six.with_metaclass(TestProgramMeta, object)):
if datetime.now() > stop_at:
if term_sent is False:
# Kill the process group since sending the term signal
# would only terminate the shell, not the command
# executed in the shell
os.killpg(os.getpgid(process.pid), signal.SIGINT)
term_sent = True
continue
if salt.utils.is_windows():
_, alive = win32_kill_process_tree(process.pid)
if alive:
log.error("Child processes still alive: %s", alive)
else:
# Kill the process group since sending the term signal
# would only terminate the shell, not the command
# executed in the shell
os.killpg(os.getpgid(process.pid), signal.SIGINT)
term_sent = True
continue
try:
# As a last resort, kill the process group
os.killpg(os.getpgid(process.pid), signal.SIGKILL)
if salt.utils.is_windows():
_, alive = win32_kill_process_tree(process.pid)
if alive:
log.error("Child processes still alive: %s", alive)
else:
# As a last resort, kill the process group
os.killpg(os.getpgid(process.pid), signal.SIGKILL)
process.wait()
except OSError as exc:
if exc.errno != errno.ESRCH:

View File

@ -698,6 +698,9 @@ class SaltTestsuiteParser(SaltCoverageTestingParser):
with TestDaemon(self):
if self.options.name:
for name in self.options.name:
name = name.strip()
if not name:
continue
if os.path.isfile(name):
if not name.endswith('.py'):
continue

View File

@ -30,12 +30,15 @@ from datetime import datetime, timedelta
# Import salt testing libs
from tests.support.unit import TestCase
from tests.support.helpers import RedirectStdStreams, requires_sshd_server
from tests.support.helpers import (
RedirectStdStreams, requires_sshd_server, win32_kill_process_tree
)
from tests.support.runtests import RUNTIME_VARS
from tests.support.mixins import AdaptedConfigurationTestCaseMixin, SaltClientTestCaseMixin
from tests.support.paths import ScriptPathMixin, INTEGRATION_TEST_DIR, CODE_DIR, PYEXEC, SCRIPT_DIR
# Import 3rd-party libs
import salt.utils
import salt.ext.six as six
from salt.ext.six.moves import cStringIO # pylint: disable=import-error
@ -276,9 +279,6 @@ class ShellTestCase(TestCase, AdaptedConfigurationTestCaseMixin):
popen_kwargs['preexec_fn'] = detach_from_parent_group
elif sys.platform.lower().startswith('win') and timeout is not None:
raise RuntimeError('Timeout is not supported under windows')
process = subprocess.Popen(cmd, **popen_kwargs)
if timeout is not None:
@ -292,13 +292,23 @@ class ShellTestCase(TestCase, AdaptedConfigurationTestCaseMixin):
# Kill the process group since sending the term signal
# would only terminate the shell, not the command
# executed in the shell
os.killpg(os.getpgid(process.pid), signal.SIGINT)
if salt.utils.is_windows():
_, alive = win32_kill_process_tree(process.pid)
if alive:
log.error("Child processes still alive: %s", alive)
else:
os.killpg(os.getpgid(process.pid), signal.SIGINT)
term_sent = True
continue
try:
# As a last resort, kill the process group
os.killpg(os.getpgid(process.pid), signal.SIGKILL)
if salt.utils.is_windows():
_, alive = win32_kill_process_tree(process.pid)
if alive:
log.error("Child processes still alive: %s", alive)
else:
os.killpg(os.getpgid(process.pid), signal.SIGINT)
except OSError as exc:
if exc.errno != errno.ESRCH:
# If errno is not "no such process", raise

View File

@ -1532,3 +1532,23 @@ class Webserver(object):
'''
self.ioloop.add_callback(self.ioloop.stop)
self.server_thread.join()
def win32_kill_process_tree(pid, sig=signal.SIGTERM, include_parent=True,
timeout=None, on_terminate=None):
'''
Kill a process tree (including grandchildren) with signal "sig" and return
a (gone, still_alive) tuple. "on_terminate", if specified, is a callabck
function which is called as soon as a child terminates.
'''
if pid == os.getpid():
raise RuntimeError("I refuse to kill myself")
parent = psutil.Process(pid)
children = parent.children(recursive=True)
if include_parent:
children.append(parent)
for p in children:
p.send_signal(sig)
gone, alive = psutil.wait_procs(children, timeout=timeout,
callback=on_terminate)
return (gone, alive)

View File

@ -0,0 +1,69 @@
# -*- coding: utf-8 -*-
'''
:codeauthor: :email:`Megan Wilhite<mwilhite@saltstack.com>`
'''
# Import Python libs
from __future__ import absolute_import
# Import Salt Libs
import salt.modules.mac_service as mac_service
# Import Salt Testing Libs
from tests.support.mixins import LoaderModuleMockMixin
from tests.support.unit import skipIf, TestCase
from tests.support.mock import (
MagicMock,
patch,
NO_MOCK,
NO_MOCK_REASON
)
@skipIf(NO_MOCK, NO_MOCK_REASON)
class MacServiceTestCase(TestCase, LoaderModuleMockMixin):
'''
TestCase for salt.modules.mac_service module
'''
def setup_loader_modules(self):
return {mac_service: {}}
def test_service_disabled_when_enabled(self):
'''
test service.disabled when service is enabled
'''
srv_name = 'com.apple.atrun'
cmd = 'disabled services = {\n\t"com.saltstack.salt.minion" => false\n\t"com.apple.atrun" => false\n{'
with patch.object(mac_service, 'launchctl', MagicMock(return_value=cmd)):
self.assertFalse(mac_service.disabled(srv_name))
def test_service_disabled_when_disabled(self):
'''
test service.disabled when service is disabled
'''
srv_name = 'com.apple.atrun'
cmd = 'disabled services = {\n\t"com.saltstack.salt.minion" => false\n\t"com.apple.atrun" => true\n{'
with patch.object(mac_service, 'launchctl', MagicMock(return_value=cmd)):
self.assertTrue(mac_service.disabled(srv_name))
def test_service_disabled_srvname_wrong(self):
'''
test service.disabled when service is just slightly wrong
'''
srv_names = ['com.apple.atru', 'com', 'apple']
cmd = 'disabled services = {\n\t"com.saltstack.salt.minion" => false\n\t"com.apple.atrun" => true\n}'
for name in srv_names:
with patch.object(mac_service, 'launchctl', MagicMock(return_value=cmd)):
self.assertFalse(mac_service.disabled(name))
def test_service_disabled_status_upper_case(self):
'''
test service.disabled when disabled status is uppercase
'''
srv_name = 'com.apple.atrun'
cmd = 'disabled services = {\n\t"com.saltstack.salt.minion" => false\n\t"com.apple.atrun" => True\n{'
with patch.object(mac_service, 'launchctl', MagicMock(return_value=cmd)):
self.assertTrue(mac_service.disabled(srv_name))

View File

@ -50,3 +50,14 @@ class FileclientTestCase(TestCase):
with self.assertRaises(OSError):
with Client(self.opts)._cache_loc('testfile') as c_ref_itr:
assert c_ref_itr == '/__test__/files/base/testfile'
def test_extrn_path_with_long_filename(self):
safe_file_name = os.path.split(Client(self.opts)._extrn_path('https://test.com/' + ('A' * 254), 'base'))[-1]
assert safe_file_name == 'A' * 254
oversized_file_name = os.path.split(Client(self.opts)._extrn_path('https://test.com/' + ('A' * 255), 'base'))[-1]
assert len(oversized_file_name) < 256
assert oversized_file_name != 'A' * 255
oversized_file_with_query_params = os.path.split(Client(self.opts)._extrn_path('https://test.com/file?' + ('A' * 255), 'base'))[-1]
assert len(oversized_file_with_query_params) < 256

View File

@ -37,7 +37,7 @@ integration.runners.test_jobs
integration.runners.test_salt
integration.sdb.test_env
integration.states.test_host
integration.states.test_pip
integration.states.test_pip_state
integration.states.test_renderers
integration.utils.testprogram
integration.wheel.test_client