mirror of
https://github.com/valitydev/salt.git
synced 2024-11-07 08:58:59 +00:00
Merge branch '2017.7' into 47696-do-not-enumerate-none
This commit is contained in:
commit
54e9bf9ec9
2
.gitignore
vendored
2
.gitignore
vendored
@ -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
|
||||
|
2
Gemfile
2
Gemfile
@ -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
40
Pipfile
Normal 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'"
|
@ -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
|
||||
|
||||
|
@ -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
4
pytest.ini
Normal file
@ -0,0 +1,4 @@
|
||||
[pytest]
|
||||
addopts = --ssh-tests -ra -sv
|
||||
testpaths = tests
|
||||
norecursedirs = tests/kitchen
|
@ -1,4 +0,0 @@
|
||||
-r base.txt
|
||||
|
||||
# Required by Tornado to handle threads stuff.
|
||||
futures>=2.0
|
@ -1 +0,0 @@
|
||||
-r base.txt
|
@ -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
16
requirements/dev.txt
Normal 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'
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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):
|
||||
|
@ -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',
|
||||
|
@ -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'):
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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):
|
||||
|
@ -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)
|
||||
|
@ -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.
|
||||
|
@ -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):
|
||||
|
@ -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):
|
||||
|
@ -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:
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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):
|
||||
|
@ -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(
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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: ''
|
||||
|
@ -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):
|
||||
|
@ -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))
|
||||
|
@ -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:
|
||||
|
@ -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])
|
@ -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)
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
69
tests/unit/modules/test_mac_service.py
Normal file
69
tests/unit/modules/test_mac_service.py
Normal 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))
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user