Merge branch '2018.3' into 51069-ri-and-rdoc-removed

This commit is contained in:
Lee Webb 2019-02-01 11:25:19 +11:00 committed by GitHub
commit f339608a88
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
64 changed files with 1410 additions and 572 deletions

49
.codecov.yml Normal file
View File

@ -0,0 +1,49 @@
codecov:
ci:
- drone.saltstack.com
- jenkinsci.saltstack.com
branch: 2018.3
notify:
require_ci_to_pass: no
ignore:
- ^*.py$
- doc/.*
- tests/.*
coverage:
round: up
range: 70..100
precision: 2
status:
project: # measuring the overall project coverage
default:
enabled: yes # must be yes|true to enable this status
if_no_uploads: error # will post commit status of "error" if no coverage reports we uploaded
# options: success, error, failure
if_not_found: success # if parent is not found report status as success, error, or failure
if_ci_failed: success # if ci fails report status as success, error, or failure
patch: # pull requests only: this commit status will measure the
# entire pull requests Coverage Diff. Checking if the lines
# adjusted are covered at least X%.
default:
enabled: no # must be yes|true to enable this status
target: 80% # specify the target "X%" coverage to hit
if_no_uploads: error # will post commit status of "error" if no coverage reports we uploaded
# options: success, error, failure
if_not_found: success
if_ci_failed: success
changes: # if there are any unexpected changes in coverage
default:
enabled: no # must be yes|true to enable this status
if_no_uploads: success
if_not_found: success
if_ci_failed: success
# No commends because we're not yet running the full test suite on PRs
comment: off

34
.coveragerc Normal file
View File

@ -0,0 +1,34 @@
[run]
branch = True
cover_pylib = False
source =
salt
parallel = True
concurrency = multiprocessing
omit =
tests/*.py
setup.py
[report]
# Regexes for lines to exclude from consideration
exclude_lines =
# Have to re-enable the standard pragma
pragma: no cover
# Don't complain about missing debug-only code:
def __repr__
# Don't complain if tests don't hit defensive assertion code:
raise AssertionError
raise NotImplementedError
# Don't complain if non-runnable code isn't run:
if 0:
if __name__ == .__main__.:
ignore_errors = True
[paths]
source =
salt

View File

@ -2,9 +2,8 @@
source 'https://rubygems.org'
# Point this back at the test-kitchen package after 1.23.3 is relased
gem 'test-kitchen', :git => 'https://github.com/dwoz/test-kitchen.git', :branch => 'winrm_opts'
gem 'kitchen-salt', '~>0.2'
gem 'test-kitchen', '~>1.23.3'
gem 'kitchen-salt', '~>0.4.1'
gem 'kitchen-sync'
gem 'git'
@ -14,7 +13,7 @@ end
group :windows do
gem 'winrm', '~>2.0'
gem 'winrm-fs', '~>1.3.1'
gem 'winrm-fs', '~>1.3.1'
end
group :ec2 do

View File

@ -71,6 +71,14 @@
{%- endmacro %}
<html>
<head>
<!-- Google Tag Manager -->
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-MCK7VL3');</script>
<!-- End Google Tag Manager -->
<meta charset="{{ encoding }}">
{{ metatags }}
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
@ -120,6 +128,11 @@
</head>
<body class="index">
<!-- Google Tag Manager (noscript) -->
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-MCK7VL3"
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<!-- End Google Tag Manager (noscript) -->
<!--[if lt IE 8]>
<p>You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade your browser.</a></p>
<![endif]-->

View File

@ -130,7 +130,7 @@ Cloud ``salt.cloud.clouds`` (:ref:`index <all-salt.clouds>`) ``
Engine ``salt.engines`` (:ref:`index <engines>`) ``engines`` ``engines_dirs``
Execution ``salt.modules`` (:ref:`index <all-salt.modules>`) ``modules`` ``module_dirs``
Executor ``salt.executors`` (:ref:`index <all-salt.executors>`) ``executors`` [#no-fs]_ ``executor_dirs``
File Server ``salt.fileserver`` (:ref:`index <file-server>`) ``fileserver`` [#no-fs]_ ``fileserver_dirs``
File Server ``salt.fileserver`` (:ref:`index <file-server>`) ``fileserver`` ``fileserver_dirs``
Grain ``salt.grains`` (:ref:`index <all-salt.grains>`) ``grains`` ``grains_dirs``
Log Handler ``salt.log.handlers`` (:ref:`index <external-logging-handlers>`) ``log_handlers`` ``log_handlers_dirs``
Net API ``salt.netapi`` (:ref:`index <all-netapi-modules>`) ``netapi`` [#no-fs]_ ``netapi_dirs``
@ -143,13 +143,13 @@ Returner ``salt.returners`` (:ref:`index <all-salt.returners>`) ``
Roster ``salt.roster`` (:ref:`index <all-salt.roster>`) ``roster`` ``roster_dirs``
Runner ``salt.runners`` (:ref:`index <all-salt.runners>`) ``runners`` ``runner_dirs``
SDB ``salt.sdb`` (:ref:`index <all-salt.sdb>`) ``sdb`` ``sdb_dirs``
Search ``salt.search`` ``search`` [#no-fs]_ ``search_dirs``
Serializer ``salt.serializers`` (:ref:`index <all-salt.serializers>`) ``serializers`` [#no-fs]_ ``serializers_dirs``
SPM pkgdb ``salt.spm.pkgdb`` ``pkgdb`` [#no-fs]_ ``pkgdb_dirs``
SPM pkgfiles ``salt.spm.pkgfiles`` ``pkgfiles`` [#no-fs]_ ``pkgfiles_dirs``
SSH Wrapper ``salt.client.ssh.wrapper`` ``wrapper`` [#no-fs]_ ``wrapper_dirs``
State ``salt.states`` (:ref:`index <all-salt.states>`) ``states`` ``states_dirs``
Thorium ``salt.thorium`` (:ref:`index <all-salt.thorium>`) ``thorium`` [#no-fs]_ ``thorium_dirs``
Thorium ``salt.thorium`` (:ref:`index <all-salt.thorium>`) ``thorium`` ``thorium_dirs``
Tokens ``salt.tokens`` ``tokens`` ``tokens_dirs``
Top ``salt.tops`` (:ref:`index <all-salt.tops>`) ``tops`` ``top_dirs``
Util ``salt.utils`` ``utils`` ``utils_dirs``
Wheel ``salt.wheels`` (:ref:`index <all-salt.wheel>`) ``wheel`` ``wheel_dirs``
@ -223,6 +223,12 @@ object.
Executor
--------
.. toctree::
:maxdepth: 1
:glob:
/ref/executors/index
Executors control how execution modules get called. The default is to just call
them, but this can be customized.
@ -322,11 +328,6 @@ SDB
SDB is a way to store data that's not associated with a minion. See
:ref:`Storing Data in Other Databases <sdb>`.
Search
------
A system for indexing the file server and pillars. Removed in 2018.3.
Serializer
----------
@ -375,6 +376,16 @@ Thorium
Modules for use in the :ref:`Thorium <thorium-reactor>` event reactor.
Tokens
------
Token stores for :ref:`External Authentication <acl-eauth>`. See the
:py:mod:`salt.tokens` docstring for details.
.. note:
The runner to load tokens modules is
:py:func:`saltutil.sync_eauth_tokens <salt.runners.saltutil.sync_eauth_tokens>`.
Tops
----

View File

@ -531,7 +531,13 @@ Global Remotes
The ``all_saltenvs`` per-remote configuration parameter overrides the logic
Salt uses to map branches/tags to fileserver environments (i.e. saltenvs). This
allows a single branch/tag to appear in *all* saltenvs.
allows a single branch/tag to appear in *all* GitFS saltenvs.
.. note::
``all_saltenvs`` only works *within* GitFS. That is, files in a branch
configured using ``all_saltenvs`` will *not* show up in a fileserver
environment defined via some other fileserver backend (e.g.
:conf_master:`file_roots`).
This is very useful in particular when working with :ref:`salt formulas
<conventions-formula>`. Prior to the addition of this feature, it was necessary

View File

@ -5,3 +5,4 @@ pytest-salt == 2018.12.8
pytest-timeout >= 1.3.3
pytest-tempdir >= 2018.8.11
pytest-helpers-namespace >= 2017.11.11
pytest-salt-runtests-bridge >= 2019.1.30

View File

@ -29,7 +29,6 @@ PyMySQL; sys.platform != 'win32' and sys.platform != 'darwin'
jsonschema
strict_rfc3339
rfc3987
jinja2
pyOpenSSL
ioflo
dnspython

View File

@ -52,8 +52,8 @@ def beacon(config):
beacons:
service:
- services:
salt-master:
mysql:
salt-master: {}
mysql: {}
The config above sets up beacons to check for
the salt-master and mysql services.

View File

@ -1344,6 +1344,24 @@ class Cloud(object):
output['ret'] = action_out
return output
@staticmethod
def vm_config(name, main, provider, profile, overrides):
'''
Create vm config.
:param str name: The name of the vm
:param dict main: The main cloud config
:param dict provider: The provider config
:param dict profile: The profile config
:param dict overrides: The vm's config overrides
'''
vm = main.copy()
vm = salt.utils.dictupdate.update(vm, provider)
vm = salt.utils.dictupdate.update(vm, profile)
vm.update(overrides)
vm['name'] = name
return vm
def extras(self, extra_):
'''
Extra actions
@ -1430,12 +1448,13 @@ class Cloud(object):
ret[name] = {'Error': msg}
continue
vm_ = main_cloud_config.copy()
vm_.update(provider_details)
vm_.update(profile_details)
vm_.update(vm_overrides)
vm_['name'] = name
vm_ = self.vm_config(
name,
main_cloud_config,
provider_details,
profile_details,
vm_overrides,
)
if self.opts['parallel']:
process = multiprocessing.Process(
target=self.create,

View File

@ -388,6 +388,9 @@ def _get_snapshot_version_metadata(artifactory_url, repository, group_id, artifa
extension = snapshot_version.find('extension').text
value = snapshot_version.find('value').text
extension_version_dict[extension] = value
if snapshot_version.find('classifier') is not None:
classifier = snapshot_version.find('classifier').text
extension_version_dict[classifier] = value
return {
'snapshot_versions': extension_version_dict

View File

@ -22,6 +22,7 @@ import re
# Import salt libs
import salt.utils.args
import salt.utils.compat
import salt.utils.data
import salt.utils.functools
import salt.utils.path
@ -31,9 +32,6 @@ import salt.utils.versions
from salt.exceptions import CommandExecutionError, MinionError
from salt.ext import six
# Workaround for 'reload' builtin of py2.7
if six.PY3:
from importlib import reload # pylint: disable=no-name-in-module
# Import third party libs
HAS_PORTAGE = False
@ -69,13 +67,13 @@ def __virtual__():
def _vartree():
import portage # pylint: disable=3rd-party-module-not-gated
portage = reload(portage)
portage = salt.utils.compat.reload(portage)
return portage.db[portage.root]['vartree']
def _porttree():
import portage # pylint: disable=3rd-party-module-not-gated
portage = reload(portage)
portage = salt.utils.compat.reload(portage)
return portage.db[portage.root]['porttree']

View File

@ -979,7 +979,7 @@ def clone(cwd,
information on securing the keypair from the remote side in the
``authorized_keys`` file.
.. _`sshd(8)`: http://www.man7.org/linux/man-pages/man8/sshd.8.html#AUTHORIZED_KEYS_FILE%20FORMAT
.. _`sshd(8)`: http://www.man7.org/linux/man-pages/man8/sshd.8.html#AUTHORIZED_KEYS_FILE_FORMAT
.. versionchanged:: 2015.8.7
@ -2035,7 +2035,7 @@ def fetch(cwd,
information on securing the keypair from the remote side in the
``authorized_keys`` file.
.. _`sshd(8)`: http://www.man7.org/linux/man-pages/man8/sshd.8.html#AUTHORIZED_KEYS_FILE%20FORMAT
.. _`sshd(8)`: http://www.man7.org/linux/man-pages/man8/sshd.8.html#AUTHORIZED_KEYS_FILE_FORMAT
.. versionchanged:: 2015.8.7
@ -2847,7 +2847,7 @@ def ls_remote(cwd=None,
information on securing the keypair from the remote side in the
``authorized_keys`` file.
.. _`sshd(8)`: http://www.man7.org/linux/man-pages/man8/sshd.8.html#AUTHORIZED_KEYS_FILE%20FORMAT
.. _`sshd(8)`: http://www.man7.org/linux/man-pages/man8/sshd.8.html#AUTHORIZED_KEYS_FILE_FORMAT
.. versionchanged:: 2015.8.7
@ -2940,6 +2940,7 @@ def merge(cwd,
git_opts='',
user=None,
password=None,
identity=None,
ignore_retcode=False,
output_encoding=None,
**kwargs):
@ -2983,6 +2984,22 @@ def merge(cwd,
.. versionadded:: 2016.3.4
identity
Path to a private key to use for ssh URLs. Salt will not attempt to use
passphrase-protected keys unless invoked from the minion using
``salt-call``, to prevent blocking waiting for user input. Key can also
be specified as a SaltStack file server URL, eg.
``salt://location/identity_file``.
.. note::
For greater security with passphraseless private keys, see the
`sshd(8)`_ manpage for information on securing the keypair from the
remote side in the ``authorized_keys`` file.
.. _`sshd(8)`: http://www.man7.org/linux/man-pages/man8/sshd.8.html#AUTHORIZED_KEYS_FILE_FORMAT
.. versionadded:: 2018.3.5,2019.2.1,Neon
ignore_retcode : False
If ``True``, do not log an error to the minion log if the git command
returns a nonzero exit status.
@ -3024,10 +3041,12 @@ def merge(cwd,
command.extend(_format_opts(opts))
if rev:
command.append(rev)
return _git_run(command,
cwd=cwd,
user=user,
password=password,
identity=identity,
ignore_retcode=ignore_retcode,
output_encoding=output_encoding)['stdout']
@ -3382,7 +3401,7 @@ def pull(cwd,
information on securing the keypair from the remote side in the
``authorized_keys`` file.
.. _`sshd(8)`: http://www.man7.org/linux/man-pages/man8/sshd.8.html#AUTHORIZED_KEYS_FILE%20FORMAT
.. _`sshd(8)`: http://www.man7.org/linux/man-pages/man8/sshd.8.html#AUTHORIZED_KEYS_FILE_FORMAT
.. versionchanged:: 2015.8.7
@ -3508,7 +3527,7 @@ def push(cwd,
information on securing the keypair from the remote side in the
``authorized_keys`` file.
.. _`sshd(8)`: http://www.man7.org/linux/man-pages/man8/sshd.8.html#AUTHORIZED_KEYS_FILE%20FORMAT
.. _`sshd(8)`: http://www.man7.org/linux/man-pages/man8/sshd.8.html#AUTHORIZED_KEYS_FILE_FORMAT
.. versionchanged:: 2015.8.7
@ -3792,7 +3811,7 @@ def remote_refs(url,
information on securing the keypair from the remote side in the
``authorized_keys`` file.
.. _`sshd(8)`: http://www.man7.org/linux/man-pages/man8/sshd.8.html#AUTHORIZED_KEYS_FILE%20FORMAT
.. _`sshd(8)`: http://www.man7.org/linux/man-pages/man8/sshd.8.html#AUTHORIZED_KEYS_FILE_FORMAT
.. versionchanged:: 2015.8.7
@ -4101,6 +4120,7 @@ def reset(cwd,
git_opts='',
user=None,
password=None,
identity=None,
ignore_retcode=False,
output_encoding=None):
'''
@ -4137,6 +4157,22 @@ def reset(cwd,
.. versionadded:: 2016.3.4
identity
Path to a private key to use for ssh URLs. Salt will not attempt to use
passphrase-protected keys unless invoked from the minion using
``salt-call``, to prevent blocking waiting for user input. Key can also
be specified as a SaltStack file server URL, eg.
``salt://location/identity_file``.
.. note::
For greater security with passphraseless private keys, see the
`sshd(8)`_ manpage for information on securing the keypair from the
remote side in the ``authorized_keys`` file.
.. _`sshd(8)`: http://www.man7.org/linux/man-pages/man8/sshd.8.html#AUTHORIZED_KEYS_FILE_FORMAT
.. versionadded:: 2018.3.5,2019.2.1,Neon
ignore_retcode : False
If ``True``, do not log an error to the minion log if the git command
returns a nonzero exit status.
@ -4174,6 +4210,7 @@ def reset(cwd,
cwd=cwd,
user=user,
password=password,
identity=identity,
ignore_retcode=ignore_retcode,
output_encoding=output_encoding)['stdout']
@ -4662,7 +4699,7 @@ def submodule(cwd,
information on securing the keypair from the remote side in the
``authorized_keys`` file.
.. _`sshd(8)`: http://www.man7.org/linux/man-pages/man8/sshd.8.html#AUTHORIZED_KEYS_FILE%20FORMAT
.. _`sshd(8)`: http://www.man7.org/linux/man-pages/man8/sshd.8.html#AUTHORIZED_KEYS_FILE_FORMAT
.. versionchanged:: 2015.8.7

View File

@ -10,6 +10,7 @@ import os
import shutil
# Import salt libs
import salt.utils.compat
import salt.utils.data
import salt.utils.files
import salt.utils.path
@ -57,7 +58,7 @@ def _get_portage():
portage module must be reloaded or it can't catch the changes
in portage.* which had been added after when the module was loaded
'''
return reload(portage)
return salt.utils.compat.reload(portage)
def _porttree():

File diff suppressed because it is too large Load Diff

View File

@ -390,7 +390,7 @@ def _passphrase_callback(passphrase):
Returns a callback function used to supply a passphrase for private keys
'''
def f(*args):
return salt.utils.stringutils.to_str(passphrase)
return salt.utils.stringutils.to_bytes(passphrase)
return f
@ -961,7 +961,7 @@ def create_crl( # pylint: disable=too-many-arguments,too-many-locals
serial_number = rev_item['serial_number'].replace(':', '')
# OpenSSL bindings requires this to be a non-unicode string
serial_number = salt.utils.stringutils.to_str(serial_number)
serial_number = salt.utils.stringutils.to_bytes(serial_number)
if 'not_after' in rev_item and not include_expired:
not_after = datetime.datetime.strptime(
@ -976,6 +976,7 @@ def create_crl( # pylint: disable=too-many-arguments,too-many-locals
rev_date = datetime.datetime.strptime(
rev_item['revocation_date'], '%Y-%m-%d %H:%M:%S')
rev_date = rev_date.strftime('%Y%m%d%H%M%SZ')
rev_date = salt.utils.stringutils.to_bytes(rev_date)
rev = OpenSSL.crypto.Revoked()
rev.set_serial(serial_number)
@ -1005,7 +1006,7 @@ def create_crl( # pylint: disable=too-many-arguments,too-many-locals
'days': days_valid
}
if digest:
export_kwargs['digest'] = bytes(digest)
export_kwargs['digest'] = salt.utils.stringutils.to_bytes(digest)
else:
log.warning('No digest specified. The default md5 digest will be used.')
@ -1573,7 +1574,7 @@ def create_certificate(
pem_type='CERTIFICATE'
)
else:
return cert.as_pem()
return salt.utils.stringutils.to_str(cert.as_pem())
# pylint: enable=too-many-locals

View File

@ -33,6 +33,7 @@ import salt.utils.color
import salt.utils.odict
import salt.utils.stringutils
from salt.ext import six
from collections import Mapping
class NestDisplay(object):
@ -142,7 +143,7 @@ class NestDisplay(object):
if self.retcode != 0:
color = self.RED
for ind in ret:
if isinstance(ind, (list, tuple, dict)):
if isinstance(ind, (list, tuple, Mapping)):
out.append(
self.ustring(
indent,
@ -150,11 +151,11 @@ class NestDisplay(object):
'|_'
)
)
prefix = '' if isinstance(ind, dict) else '- '
prefix = '' if isinstance(ind, Mapping) else '- '
self.display(ind, indent + 2, prefix, out)
else:
self.display(ind, indent, '- ', out)
elif isinstance(ret, dict):
elif isinstance(ret, Mapping):
if indent:
color = self.CYAN
if self.retcode != 0:

View File

@ -19,6 +19,7 @@ import salt.transport.frame
import salt.utils.immutabletypes as immutabletypes
import salt.utils.stringutils
from salt.exceptions import SaltReqTimeoutError
from salt.utils.data import CaseInsensitiveDict
# Import third party libs
from salt.ext import six
@ -205,6 +206,8 @@ class Serial(object):
elif isinstance(obj, (set, immutabletypes.ImmutableSet)):
# msgpack can't handle set so translate it to tuple
return tuple(obj)
elif isinstance(obj, CaseInsensitiveDict):
return dict(obj)
# Nothing known exceptions found. Let msgpack raise it's own.
return obj

View File

@ -7,10 +7,8 @@ The following Type: "Zabbix trapper" with "Type of information" Text items are r
.. code-block:: cfg
Key: salt.trap.info
Key: salt.trap.average
Key: salt.trap.warning
Key: salt.trap.high
Key: salt.trap.disaster
To use the Zabbix returner, append '--return zabbix' to the salt command. ex:
@ -21,15 +19,10 @@ To use the Zabbix returner, append '--return zabbix' to the salt command. ex:
# Import Python libs
from __future__ import absolute_import, print_function, unicode_literals
import logging
import os
# Import Salt libs
from salt.ext import six
import salt.utils.files
# Get logging started
log = logging.getLogger(__name__)
# Define the module's virtual name
@ -55,37 +48,24 @@ def zbx():
return False
def zabbix_send(key, host, output):
with salt.utils.files.fopen(zbx()['zabbix_config'], 'r') as file_handle:
for line in file_handle:
if "ServerActive" in line:
flag = "true"
server = line.rsplit('=')
server = server[1].rsplit(',')
for s in server:
cmd = zbx()['sender'] + " -z " + s.replace('\n', '') + " -s " + host + " -k " + key + " -o \"" + output +"\""
__salt__['cmd.shell'](cmd)
break
else:
flag = "false"
if flag == 'false':
cmd = zbx()['sender'] + " -c " + zbx()['config'] + " -s " + host + " -k " + key + " -o \"" + output +"\""
def zabbix_send(key, output):
cmd = zbx()['sender'] + " -c " + zbx()['config'] + " -k " + key + " -o \"" + output +"\""
__salt__['cmd.shell'](cmd)
def returner(ret):
changes = False
errors = False
job_minion_id = ret['id']
host = job_minion_id
if type(ret['return']) is dict:
for state, item in six.iteritems(ret['return']):
if 'comment' in item and 'name' in item and not item['result']:
if 'comment' in item and 'name' in item and item['result'] is False:
errors = True
zabbix_send("salt.trap.high", host, 'SALT:\nname: {0}\ncomment: {1}'.format(item['name'], item['comment']))
if 'comment' in item and 'name' in item and item['changes']:
zabbix_send("salt.trap.high", 'SALT:\nname: {0}\ncomment: {1}'.format(item['name'], item['comment']))
elif 'comment' in item and 'name' in item and item['changes']:
changes = True
zabbix_send("salt.trap.warning", host, 'SALT:\nname: {0}\ncomment: {1}'.format(item['name'], item['comment']))
zabbix_send("salt.trap.warning", 'SALT:\nname: {0}\ncomment: {1}'.format(item['name'], item['comment']))
if not changes and not errors:
zabbix_send("salt.trap.info", host, 'SALT {0} OK'.format(job_minion_id))
zabbix_send("salt.trap.info", 'SALT {0} OK'.format(job_minion_id))

View File

@ -2421,7 +2421,6 @@ def managed(name,
'to True to allow the managed file to be empty.'
.format(contents_id)
)
if isinstance(use_contents, six.binary_type) and b'\0' in use_contents:
contents = use_contents
elif isinstance(use_contents, six.text_type) and str('\0') in use_contents:
@ -2435,9 +2434,10 @@ def managed(name,
'contents_grains is not a string or list of strings, and '
'is not binary data. SLS is likely malformed.'
)
contents = os.linesep.join(
[line.rstrip('\n').rstrip('\r') for line in validated_contents]
)
contents = ''
for part in validated_contents:
for line in part.splitlines():
contents += line.rstrip('\n').rstrip('\r') + os.linesep
if contents_newline and not contents.endswith(os.linesep):
contents += os.linesep
if template:
@ -3276,7 +3276,7 @@ def directory(name,
ret, _ = __salt__['file.check_perms'](
full, ret, user, group, file_mode, None, follow_symlinks)
except CommandExecutionError as exc:
if not exc.strerror.endswith('does not exist'):
if not exc.strerror.startswith('Path not found'):
errors.append(exc.strerror)
if check_dirs:

View File

@ -685,6 +685,15 @@ def latest(name,
if https_pass is not None and not isinstance(https_pass, six.string_types):
https_pass = six.text_type(https_pass)
# Check for lfs filter settings, and setup lfs_opts accordingly. These opts
# will be passed where appropriate to ensure that these commands are
# authenticated and that the git LFS plugin can download files.
use_lfs = bool(
__salt__['git.config_get_regexp'](
r'filter\.lfs\.',
**{'global': True}))
lfs_opts = {'identity': identity} if use_lfs else {}
if os.path.isfile(target):
return _fail(
ret,
@ -1560,7 +1569,8 @@ def latest(name,
opts=['--hard', remote_rev],
user=user,
password=password,
output_encoding=output_encoding)
output_encoding=output_encoding,
**lfs_opts)
ret['changes']['forced update'] = True
comments.append(
'Repository was hard-reset to {0}'.format(remote_loc)
@ -1613,7 +1623,8 @@ def latest(name,
opts=merge_opts,
user=user,
password=password,
output_encoding=output_encoding)
output_encoding=output_encoding,
**lfs_opts)
comments.append(
'Repository was fast-forwarded to {0}'
.format(remote_loc)
@ -1633,7 +1644,8 @@ def latest(name,
remote_rev if rev == 'HEAD' else rev],
user=user,
password=password,
output_encoding=output_encoding)
output_encoding=output_encoding,
**lfs_opts)
comments.append(
'Repository was reset to {0} (fast-forward)'
.format(rev)

View File

@ -30,6 +30,7 @@ except ImportError:
HAS_PKG_RESOURCES = False
# Import salt libs
import salt.utils.data
import salt.utils.versions
from salt.version import SaltStackVersion as _SaltStackVersion
from salt.exceptions import CommandExecutionError, CommandNotFoundError
@ -87,20 +88,6 @@ def __virtual__():
return False
def _find_key(prefix, pip_list):
'''
Does a case-insensitive match in the pip_list for the desired package.
'''
try:
match = next(
iter(x for x in pip_list if x.lower() == prefix.lower())
)
except StopIteration:
return None
else:
return match
def _fulfills_version_spec(version, version_spec):
'''
Check version number against version specification info and return a
@ -214,23 +201,20 @@ def _check_if_installed(prefix, state_pkg_name, version_spec, ignore_installed,
ret = {'result': False, 'comment': None}
# If we are not passed a pip list, get one:
if not pip_list:
pip_list = __salt__['pip.list'](prefix, bin_env=bin_env,
user=user, cwd=cwd,
env_vars=env_vars, **kwargs)
# Check if the requested package is already installed.
prefix_realname = _find_key(prefix, pip_list)
pip_list = salt.utils.data.CaseInsensitiveDict(
pip_list or __salt__['pip.list'](prefix, bin_env=bin_env,
user=user, cwd=cwd,
env_vars=env_vars, **kwargs)
)
# If the package was already installed, check
# the ignore_installed and force_reinstall flags
if ignore_installed is False and prefix_realname is not None:
if ignore_installed is False and prefix in pip_list:
if force_reinstall is False and not upgrade:
# Check desired version (if any) against currently-installed
if (
any(version_spec) and
_fulfills_version_spec(pip_list[prefix_realname],
version_spec)
_fulfills_version_spec(pip_list[prefix], version_spec)
) or (not any(version_spec)):
ret['result'] = True
ret['comment'] = ('Python package {0} was already '
@ -250,7 +234,7 @@ def _check_if_installed(prefix, state_pkg_name, version_spec, ignore_installed,
if 'rc' in spec[1]:
include_rc = True
available_versions = __salt__['pip.list_all_versions'](
prefix_realname, bin_env=bin_env, include_alpha=include_alpha,
prefix, bin_env=bin_env, include_alpha=include_alpha,
include_beta=include_beta, include_rc=include_rc, user=user,
cwd=cwd)
desired_version = ''
@ -266,9 +250,9 @@ def _check_if_installed(prefix, state_pkg_name, version_spec, ignore_installed,
ret['comment'] = ('Python package {0} was already '
'installed and\nthe available upgrade '
'doesn\'t fulfills the version '
'requirements'.format(prefix_realname))
'requirements'.format(prefix))
return ret
if _pep440_version_cmp(pip_list[prefix_realname], desired_version) == 0:
if _pep440_version_cmp(pip_list[prefix], desired_version) == 0:
ret['result'] = True
ret['comment'] = ('Python package {0} was already '
'installed'.format(state_pkg_name))
@ -903,10 +887,12 @@ def installed(name,
# Case for packages that are not an URL
if prefix:
pipsearch = __salt__['pip.list'](prefix, bin_env,
user=user, cwd=cwd,
env_vars=env_vars,
**kwargs)
pipsearch = salt.utils.data.CaseInsensitiveDict(
__salt__['pip.list'](prefix, bin_env,
user=user, cwd=cwd,
env_vars=env_vars,
**kwargs)
)
# If we didn't find the package in the system after
# installing it report it
@ -917,12 +903,10 @@ def installed(name,
'\'pip.freeze\'.'.format(pkg)
)
else:
pkg_name = _find_key(prefix, pipsearch)
if pkg_name.lower() in already_installed_packages:
continue
ver = pipsearch[pkg_name]
ret['changes']['{0}=={1}'.format(pkg_name,
ver)] = 'Installed'
if prefix in pipsearch \
and prefix.lower() not in already_installed_packages:
ver = pipsearch[prefix]
ret['changes']['{0}=={1}'.format(prefix, ver)] = 'Installed'
# Case for packages that are an URL
else:
ret['changes']['{0}==???'.format(state_name)] = 'Installed'

View File

@ -261,7 +261,7 @@ def set_(name,
for p_name in current_policy[policy_data['output_section']]:
if policy_name.lower() == p_name.lower():
currently_set = True
pol_id = policy_name
pol_id = p_name
break
# Check aliases
else:

View File

@ -109,7 +109,15 @@ def creds(provider):
__Expiration__ = data['Expiration']
return __AccessKeyId__, __SecretAccessKey__, __Token__
else:
return provider['id'], provider['key'], ''
ret_credentials = provider['id'], provider['key'], ''
if provider.get('role_arn') is not None:
provider_shadow = provider.copy()
provider_shadow.pop("role_arn", None)
log.info("Assuming the role: %s", provider.get('role_arn'))
ret_credentials = assumed_creds(provider_shadow, role_arn=provider.get('role_arn'), location='us-east-1')
return ret_credentials
def sig2(method, endpoint, params, provider, aws_api_version):

View File

@ -8,6 +8,7 @@ from __future__ import absolute_import, print_function, unicode_literals
import sys
import copy
import types
import importlib
# Import salt libs
import salt.loader
@ -58,3 +59,13 @@ def cmp(x, y):
Return negative if x<y, zero if x==y, positive if x>y.
'''
return (x > y) - (x < y)
def reload(mod):
'''
Compatibility helper function to replace the ``reload`` builtin from Python 2.
'''
try:
return importlib.reload(mod)
except AttributeError:
return reload(mod)

View File

@ -13,9 +13,9 @@ import logging
import re
try:
from collections.abc import Mapping
from collections.abc import Mapping, MutableMapping, Sequence
except ImportError:
from collections import Mapping
from collections import Mapping, MutableMapping, Sequence
# Import Salt libs
import salt.utils.dictupdate
@ -24,6 +24,7 @@ import salt.utils.yaml
from salt.defaults import DEFAULT_TARGET_DELIM
from salt.exceptions import SaltException
from salt.utils.decorators.jinja import jinja_filter
from salt.utils.odict import OrderedDict
# Import 3rd-party libs
from salt.ext import six
@ -32,6 +33,87 @@ from salt.ext.six.moves import range # pylint: disable=redefined-builtin
log = logging.getLogger(__name__)
class CaseInsensitiveDict(MutableMapping):
'''
Inspired by requests' case-insensitive dict implementation, but works with
non-string keys as well.
'''
def __init__(self, init=None, **kwargs):
'''
Force internal dict to be ordered to ensure a consistent iteration
order, irrespective of case.
'''
self._data = OrderedDict()
self.update(init or {}, **kwargs)
def __len__(self):
return len(self._data)
def __setitem__(self, key, value):
# Store the case-sensitive key so it is available for dict iteration
self._data[to_lowercase(key)] = (key, value)
def __delitem__(self, key):
del self._data[to_lowercase(key)]
def __getitem__(self, key):
return self._data[to_lowercase(key)][1]
def __iter__(self):
return (item[0] for item in six.itervalues(self._data))
def __eq__(self, rval):
if not isinstance(rval, Mapping):
# Comparing to non-mapping type (e.g. int) is always False
return False
return dict(self.items_lower()) == dict(CaseInsensitiveDict(rval).items_lower())
def __repr__(self):
return repr(dict(six.iteritems(self)))
def items_lower(self):
'''
Returns a generator iterating over keys and values, with the keys all
being lowercase.
'''
return ((key, val[1]) for key, val in six.iteritems(self._data))
def copy(self):
'''
Returns a copy of the object
'''
return CaseInsensitiveDict(six.iteritems(self._data))
def __change_case(data, attr, preserve_dict_class=False):
try:
return getattr(data, attr)()
except AttributeError:
pass
data_type = data.__class__
if isinstance(data, Mapping):
return (data_type if preserve_dict_class else dict)(
(__change_case(key, attr, preserve_dict_class),
__change_case(val, attr, preserve_dict_class))
for key, val in six.iteritems(data)
)
elif isinstance(data, Sequence):
return data_type(
__change_case(item, attr, preserve_dict_class) for item in data)
else:
return data
def to_lowercase(data, preserve_dict_class=False):
return __change_case(data, 'lower', preserve_dict_class)
def to_uppercase(data, preserve_dict_class=False):
return __change_case(data, 'upper', preserve_dict_class)
@jinja_filter('compare_dicts')
def compare_dicts(old=None, new=None):
'''

View File

@ -23,6 +23,7 @@ import shlex
import socket
import ssl
import string
import functools
# Import Salt libs
import salt.utils.files
@ -364,12 +365,13 @@ def _lookup_host(name, rdtype, timeout=None, server=None):
'''
cmd = 'host -t {0} '.format(rdtype)
if server is not None:
cmd += '@{0} '.format(server)
if timeout:
cmd += '-W {0} '.format(int(timeout))
cmd += name
if server is not None:
cmd += ' {0}'.format(server)
cmd = __salt__['cmd.run_all'](cmd + name, python_shell=False, output_loglevel='quiet')
cmd = __salt__['cmd.run_all'](cmd, python_shell=False, output_loglevel='quiet')
if 'invalid type' in cmd['stderr']:
raise ValueError('Invalid DNS type {}'.format(rdtype))
@ -380,7 +382,8 @@ def _lookup_host(name, rdtype, timeout=None, server=None):
return []
res = []
for line in cmd['stdout'].splitlines():
_stdout = cmd['stdout'] if server is None else cmd['stdout'].split('\n\n')[-1]
for line in _stdout.splitlines():
if rdtype != 'CNAME' and 'is an alias' in line:
continue
line = line.split(' ', 3)[-1]
@ -563,12 +566,15 @@ def lookup(
timeout /= len(servers)
# Inject a wrapper for multi-server behaviour
def _multi_srvr(**res_kwargs):
for server in servers:
s_res = resolver(server=server, **res_kwargs)
if s_res:
return s_res
resolver = _multi_srvr
def _multi_srvr(resolv_func):
@functools.wraps(resolv_func)
def _wrapper(**res_kwargs):
for server in servers:
s_res = resolv_func(server=server, **res_kwargs)
if s_res:
return s_res
return _wrapper
resolver = _multi_srvr(resolver)
if not walk:
name = [name]

View File

@ -416,11 +416,13 @@ def flopen(*args, **kwargs):
'''
Shortcut for fopen with lock and context manager.
'''
with fopen(*args, **kwargs) as f_handle:
filename, args = args[0], args[1:]
writing = 'wa'
with fopen(filename, *args, **kwargs) as f_handle:
try:
if is_fcntl_available(check_sunos=True):
lock_type = fcntl.LOCK_SH
if 'w' in args[1] or 'a' in args[1]:
if args and any([write in args[0] for write in writing]):
lock_type = fcntl.LOCK_EX
fcntl.flock(f_handle.fileno(), lock_type)
yield f_handle

View File

@ -1410,6 +1410,19 @@ class Pygit2(GitProvider):
override_params, cache_root, role
)
def peel(self, obj):
'''
Compatibility function for pygit2.Reference objects. Older versions of
pygit2 use .get_object() to return the object to which the reference
points, while newer versions use .peel(). In pygit2 0.27.4,
.get_object() was removed. This function will try .peel() first and
fall back to .get_object().
'''
try:
return obj.peel()
except AttributeError:
return obj.get_object()
def checkout(self):
'''
Checkout the configured branch/tag
@ -1428,7 +1441,7 @@ class Pygit2(GitProvider):
return None
try:
head_sha = local_head.get_object().hex
head_sha = self.peel(local_head).hex
except AttributeError:
# Shouldn't happen, but just in case a future pygit2 API change
# breaks things, avoid a traceback and log an error.
@ -1477,7 +1490,7 @@ class Pygit2(GitProvider):
try:
if remote_ref in refs:
# Get commit id for the remote ref
oid = self.repo.lookup_reference(remote_ref).get_object().id
oid = self.peel(self.repo.lookup_reference(remote_ref)).id
if local_ref not in refs:
# No local branch for this remote, so create one and point
# it at the commit id of the remote ref
@ -1485,7 +1498,7 @@ class Pygit2(GitProvider):
try:
target_sha = \
self.repo.lookup_reference(remote_ref).get_object().hex
self.peel(self.repo.lookup_reference(remote_ref)).hex
except KeyError:
log.error(
'pygit2 was unable to get SHA for %s in %s remote '
@ -1857,8 +1870,8 @@ class Pygit2(GitProvider):
refs/remotes/origin/
'''
try:
return self.repo.lookup_reference(
'refs/remotes/origin/{0}'.format(ref)).get_object().tree
return self.peel(self.repo.lookup_reference(
'refs/remotes/origin/{0}'.format(ref))).tree
except KeyError:
return None
@ -1867,8 +1880,8 @@ class Pygit2(GitProvider):
Return a pygit2.Tree object matching a tag ref fetched into refs/tags/
'''
try:
return self.repo.lookup_reference(
'refs/tags/{0}'.format(ref)).get_object().tree
return self.peel(self.repo.lookup_reference(
'refs/tags/{0}'.format(ref))).tree
except KeyError:
return None

View File

@ -149,7 +149,7 @@ def nodegroup_comp(nodegroup, nodegroups, skip=None, first_call=True):
# No compound operators found in nodegroup definition. Check for
# group type specifiers
group_type_re = re.compile('^[A-Z]@')
regex_chars = ['(', '[', '{', '\\', '?''}])']
regex_chars = ['(', '[', '{', '\\', '?', '}', ']', ')']
if not [x for x in ret if '*' in x or group_type_re.match(x)]:
# No group type specifiers and no wildcards.
# Treat this as an expression.

View File

@ -1935,7 +1935,7 @@ def parse_host_port(host_port):
if _s_[0] == "[":
if "]" in host_port:
host, _s_ = _s_.lstrip("[").rsplit("]", 1)
host = ipaddress.IPv6Address(host)
host = ipaddress.IPv6Address(host).compressed
if _s_[0] == ":":
port = int(_s_.lstrip(":"))
else:
@ -1953,7 +1953,7 @@ def parse_host_port(host_port):
host = _s_
try:
if not isinstance(host, ipaddress._BaseAddress):
host_ip = ipaddress.ip_address(host)
host_ip = ipaddress.ip_address(host).compressed
host = host_ip
except ValueError:
log.debug('"%s" Not an IP address? Assuming it is a hostname.', host)

View File

@ -806,7 +806,7 @@ def default_signals(*signals):
old_signals = {}
for signum in signals:
try:
old_signals[signum] = signal.getsignal(signum)
saved_signal = signal.getsignal(signum)
signal.signal(signum, signal.SIG_DFL)
except ValueError as exc:
# This happens when a netapi module attempts to run a function
@ -816,6 +816,8 @@ def default_signals(*signals):
'Failed to register signal for signum %d: %s',
signum, exc
)
else:
old_signals[signum] = saved_signal
# Do whatever is needed with the reset signals
yield

View File

@ -333,7 +333,9 @@ def build_whitespace_split_regex(text):
lexer = shlex.shlex(text)
lexer.whitespace_split = True
lexer.commenters = ''
if '\'' in text:
if r"'\"" in text:
lexer.quotes = ''
elif '\'' in text:
lexer.quotes = '"'
elif '"' in text:
lexer.quotes = '\''

View File

@ -14,13 +14,11 @@ import re
import time
# Import salt libs
import salt.utils.compat
import salt.utils.data
from salt.utils.timeout import wait_for
import salt.ext.six as six
# Workaround for 'reload' builtin of py2.7
if six.PY3:
from importlib import reload # pylint: disable=no-name-in-module
log = logging.getLogger(__name__)
@ -140,7 +138,7 @@ def vb_get_manager():
'''
global _virtualboxManager
if _virtualboxManager is None and HAS_LIBS:
reload(vboxapi)
salt.utils.compat.reload(vboxapi)
_virtualboxManager = vboxapi.VirtualBoxManager(None, None)
return _virtualboxManager

View File

@ -3,7 +3,7 @@ r'''
A salt util for modifying firewall settings.
.. versionadded:: 2018.3.4
.. versionadded:: Fluorine
.. versionadded:: 2019.2.0
This util allows you to modify firewall settings in the local group policy in
addition to the normal firewall settings. Parameters are taken from the

View File

@ -55,7 +55,7 @@ import salt.log.setup
from salt.utils.odict import OrderedDict
# Define the pytest plugins we rely on
pytest_plugins = ['tempdir', 'helpers_namespace'] # pylint: disable=invalid-name
pytest_plugins = ['tempdir', 'helpers_namespace', 'salt-from-filenames'] # pylint: disable=invalid-name
# Define where not to collect tests from
collect_ignore = ['setup.py']

View File

@ -743,6 +743,12 @@ class TestDaemon(object):
master_opts['root_dir'] = os.path.join(TMP, 'rootdir')
master_opts['pki_dir'] = os.path.join(TMP, 'rootdir', 'pki', 'master')
master_opts['syndic_master'] = 'localhost'
file_tree = {
'root_dir': os.path.join(FILES, 'pillar', 'base', 'file_tree'),
'follow_dir_links': False,
'keep_newline': True,
}
master_opts['ext_pillar'].append({'file_tree': file_tree})
# This is the syndic for master
# Let's start with a copy of the syndic master configuration

View File

@ -0,0 +1,3 @@
{{ pillar['name'] }}:
file.managed:
- contents_pillar: issue-50221

View File

@ -0,0 +1,4 @@
/tmp/vimrc:
file.append:
- sources:
- salt://test/files/vimrc.stub

View File

@ -0,0 +1,8 @@
set number
syntax on
set paste
set ruler
if has("autocmd")
au BufReadPost * if line("'\"") > 1 && line("'\"") <= line("$") | exe "normal! g'\"" | endif
endif

View File

@ -11,34 +11,28 @@ class DataModuleTest(ModuleCase):
'''
Validate the data module
'''
def _clear_db(self):
'''
Clear out the database
'''
def setUp(self):
self.run_function('data.clear')
self.addCleanup(self.run_function, 'data.clear')
def test_load_dump(self):
'''
data.load
data.dump
'''
self._clear_db()
self.assertTrue(self.run_function('data.dump', ['{"foo": "bar"}']))
self.assertEqual(self.run_function('data.load'), {'foo': 'bar'})
self._clear_db()
def test_get_update(self):
'''
data.get
data.update
'''
self._clear_db()
self.assertTrue(self.run_function('data.update', ['spam', 'eggs']))
self.assertEqual(self.run_function('data.get', ['spam']), 'eggs')
self.assertTrue(self.run_function('data.update', ['unladen', 'swallow']))
self.assertEqual(self.run_function('data.get', ['["spam", "unladen"]']), ['eggs', 'swallow'])
self._clear_db()
self.assertEqual(self.run_function('data.get', [["spam", "unladen"]]), ['eggs', 'swallow'])
def test_cas_update(self):
'''
@ -46,7 +40,6 @@ class DataModuleTest(ModuleCase):
data.cas
data.get
'''
self._clear_db()
self.assertTrue(self.run_function('data.update', ['spam', 'eggs']))
self.assertTrue(self.run_function('data.cas', ['spam', 'green', 'eggs']))
self.assertEqual(self.run_function('data.get', ['spam']), 'green')

View File

@ -222,7 +222,7 @@ class SupervisordModuleTest(ModuleCase):
ret = self.run_function(
'supervisord.status', [], conf_file=self.supervisor_conf,
bin_env=self.venv_dir)
self.assertEqual(list(ret.keys()), ['sleep_service', 'sleep_service2'])
self.assertEqual(sorted(ret), ['sleep_service', 'sleep_service2'])
def test_status_one(self):
'''

View File

@ -100,6 +100,11 @@ class TestSaltAPIHandler(_SaltnadoIntegrationTestCase):
)
response_obj = salt.utils.json.loads(response.body)
self.assertEqual(len(response_obj['return']), 1)
# If --proxy is set, it will cause an extra minion_id to be in the
# response. Since there's not a great way to know if the test
# runner's proxy minion is running, and we're not testing proxy
# minions here anyway, just remove it from the response.
response_obj['return'][0].pop('proxytest', None)
self.assertEqual(response_obj['return'][0], {'minion': True, 'sub_minion': True})
def test_simple_local_post_no_tgt(self):
@ -142,6 +147,11 @@ class TestSaltAPIHandler(_SaltnadoIntegrationTestCase):
)
response_obj = salt.utils.json.loads(response.body)
self.assertEqual(len(response_obj['return']), 1)
# If --proxy is set, it will cause an extra minion_id to be in the
# response. Since there's not a great way to know if the test
# runner's proxy minion is running, and we're not testing proxy
# minions here anyway, just remove it from the response.
response_obj['return'][0].pop('proxytest', None)
self.assertEqual(response_obj['return'][0], {'minion': True, 'sub_minion': True})
def test_simple_local_post_invalid_request(self):
@ -175,6 +185,14 @@ class TestSaltAPIHandler(_SaltnadoIntegrationTestCase):
response_obj = salt.utils.json.loads(response.body)
ret = response_obj['return']
ret[0]['minions'] = sorted(ret[0]['minions'])
try:
# If --proxy is set, it will cause an extra minion_id to be in the
# response. Since there's not a great way to know if the test
# runner's proxy minion is running, and we're not testing proxy
# minions here anyway, just remove it from the response.
ret[0]['minions'].remove('proxytest')
except ValueError:
pass
# TODO: verify pub function? Maybe look at how we test the publisher
self.assertEqual(len(ret), 1)
@ -201,6 +219,15 @@ class TestSaltAPIHandler(_SaltnadoIntegrationTestCase):
ret = response_obj['return']
ret[0]['minions'] = sorted(ret[0]['minions'])
ret[1]['minions'] = sorted(ret[1]['minions'])
try:
# If --proxy is set, it will cause an extra minion_id to be in the
# response. Since there's not a great way to know if the test
# runner's proxy minion is running, and we're not testing proxy
# minions here anyway, just remove it from the response.
ret[0]['minions'].remove('proxytest')
ret[1]['minions'].remove('proxytest')
except ValueError:
pass
self.assertEqual(len(ret), 2)
self.assertIn('jid', ret[0])
@ -235,6 +262,15 @@ class TestSaltAPIHandler(_SaltnadoIntegrationTestCase):
ret = response_obj['return']
ret[0]['minions'] = sorted(ret[0]['minions'])
ret[1]['minions'] = sorted(ret[1]['minions'])
try:
# If --proxy is set, it will cause an extra minion_id to be in the
# response. Since there's not a great way to know if the test
# runner's proxy minion is running, and we're not testing proxy
# minions here anyway, just remove it from the response.
ret[0]['minions'].remove('proxytest')
ret[1]['minions'].remove('proxytest')
except ValueError:
pass
self.assertEqual(len(ret), 3) # make sure we got 3 responses
self.assertIn('jid', ret[0]) # the first 2 are regular returns
@ -279,9 +315,13 @@ class TestSaltAPIHandler(_SaltnadoIntegrationTestCase):
request_timeout=30,
)
response_obj = salt.utils.json.loads(response.body)
self.application.opts['order_masters'] = []
self.application.opts['syndic_wait'] = 5
# If --proxy is set, it will cause an extra minion_id to be in the
# response. Since there's not a great way to know if the test runner's
# proxy minion is running, and we're not testing proxy minions here
# anyway, just remove it from the response.
response_obj[0]['return'].pop('proxytest', None)
self.assertEqual(response_obj['return'], [{'minion': True, 'sub_minion': True}])
# runner tests
@ -299,7 +339,15 @@ class TestSaltAPIHandler(_SaltnadoIntegrationTestCase):
)
response_obj = salt.utils.json.loads(response.body)
self.assertEqual(len(response_obj['return']), 1)
self.assertEqual(set(response_obj['return'][0]), set(['minion', 'sub_minion']))
try:
# If --proxy is set, it will cause an extra minion_id to be in the
# response. Since there's not a great way to know if the test
# runner's proxy minion is running, and we're not testing proxy
# minions here anyway, just remove it from the response.
response_obj['return'][0].remove('proxytest')
except ValueError:
pass
self.assertEqual(sorted(response_obj['return'][0]), sorted(['minion', 'sub_minion']))
# runner_async tests
def test_simple_local_runner_async_post(self):

View File

@ -36,6 +36,11 @@ class NetapiClientTest(TestCase):
low.update(self.eauth_creds)
ret = self.netapi.run(low)
# If --proxy is set, it will cause an extra minion_id to be in the
# response. Since there's not a great way to know if the test
# runner's proxy minion is running, and we're not testing proxy
# minions here anyway, just remove it from the response.
ret.pop('proxytest', None)
self.assertEqual(ret, {'minion': True, 'sub_minion': True})
def test_local_batch(self):
@ -59,6 +64,14 @@ class NetapiClientTest(TestCase):
self.assertIn('jid', ret)
ret.pop('jid', None)
ret['minions'] = sorted(ret['minions'])
try:
# If --proxy is set, it will cause an extra minion_id to be in the
# response. Since there's not a great way to know if the test
# runner's proxy minion is running, and we're not testing proxy
# minions here anyway, just remove it from the response.
ret['minions'].remove('proxytest')
except ValueError:
pass
self.assertEqual(ret, {'minions': sorted(['minion', 'sub_minion'])})
def test_wheel(self):

View File

@ -2530,6 +2530,42 @@ class FileTest(ModuleCase, SaltReturnAssertsMixin):
'',
]).encode('utf-8'))
@with_tempfile()
def test_issue_50221(self, name):
expected = 'abc{0}{0}{0}'.format(os.linesep)
ret = self.run_function(
'pillar.get',
['issue-50221']
)
assert ret == expected
ret = self.run_function(
'state.apply',
['issue-50221'],
pillar={
'name': name
},
)
self.assertSaltTrueReturn(ret)
with salt.utils.files.fopen(name, 'r') as fp:
contents = fp.read()
assert contents == expected
def test_managed_file_issue_51208(self):
'''
Test to ensure we can handle a file with escaped double-quotes
'''
name = os.path.join(TMP, 'issue_51208.txt')
ret = self.run_state(
'file.managed', name=name, source='salt://issue-51208/vimrc.stub'
)
src = os.path.join(BASE_FILES, 'issue-51208', 'vimrc.stub')
with salt.utils.files.fopen(src, 'r') as fp_:
master_data = fp_.read()
with salt.utils.files.fopen(name, 'r') as fp_:
minion_data = fp_.read()
self.assertEqual(master_data, minion_data)
self.assertSaltTrueReturn(ret)
class BlockreplaceTest(ModuleCase, SaltReturnAssertsMixin):
marker_start = '# start'

View File

@ -44,7 +44,7 @@ _PKG_TARGETS = {
}
_PKG_CAP_TARGETS = {
'Suse': [('w3m_ssl', 'w3m')],
'Suse': [('perl(ZNC)', 'znc-perl')],
}
_PKG_TARGETS_32 = {
@ -821,12 +821,14 @@ class PkgTest(ModuleCase, SaltReturnAssertsMixin):
self.assertFalse(version)
self.assertFalse(realver)
ret = self.run_state('pkg.installed', name=target, refresh=False, resolve_capabilities=True, test=True)
self.assertInSaltComment("The following packages would be installed/updated: {0}".format(realpkg), ret)
ret = self.run_state('pkg.installed', name=target, refresh=False, resolve_capabilities=True)
self.assertSaltTrueReturn(ret)
ret = self.run_state('pkg.removed', name=realpkg)
self.assertSaltTrueReturn(ret)
try:
ret = self.run_state('pkg.installed', name=target, refresh=False, resolve_capabilities=True, test=True)
self.assertInSaltComment("The following packages would be installed/updated: {0}".format(realpkg), ret)
ret = self.run_state('pkg.installed', name=target, refresh=False, resolve_capabilities=True)
self.assertSaltTrueReturn(ret)
finally:
ret = self.run_state('pkg.removed', name=realpkg)
self.assertSaltTrueReturn(ret)
@skipIf(salt.utils.platform.is_windows(), 'minion is windows')
@requires_system_grains
@ -853,18 +855,22 @@ class PkgTest(ModuleCase, SaltReturnAssertsMixin):
self.assertFalse(version)
self.assertFalse(realver)
# install the package already
ret = self.run_state('pkg.installed', name=realpkg, refresh=False)
try:
# install the package
ret = self.run_state('pkg.installed', name=realpkg, refresh=False)
self.assertSaltTrueReturn(ret)
ret = self.run_state('pkg.installed', name=target, refresh=False, resolve_capabilities=True, test=True)
self.assertInSaltComment("All specified packages are already installed", ret)
# Try to install again. Nothing should be installed this time.
ret = self.run_state('pkg.installed', name=target, refresh=False, resolve_capabilities=True, test=True)
self.assertInSaltComment("All specified packages are already installed", ret)
ret = self.run_state('pkg.installed', name=target, refresh=False, resolve_capabilities=True)
self.assertSaltTrueReturn(ret)
ret = self.run_state('pkg.installed', name=target, refresh=False, resolve_capabilities=True)
self.assertSaltTrueReturn(ret)
self.assertInSaltComment("packages are already installed", ret)
ret = self.run_state('pkg.removed', name=realpkg)
self.assertSaltTrueReturn(ret)
self.assertInSaltComment("packages are already installed", ret)
finally:
ret = self.run_state('pkg.removed', name=realpkg)
self.assertSaltTrueReturn(ret)
@skipIf(salt.utils.platform.is_windows(), 'minion is windows')
@requires_system_grains
@ -890,8 +896,8 @@ class PkgTest(ModuleCase, SaltReturnAssertsMixin):
# Make sure that we have targets that match the os_family. If this
# fails then the _PKG_TARGETS dict above needs to have an entry added,
# with two packages that are not installed before these tests are run
self.assertTrue(bool(pkg_cap_targets))
self.assertTrue(bool(pkg_targets))
self.assertTrue(pkg_cap_targets)
self.assertTrue(pkg_targets)
if os_family == 'Arch':
for idx in range(13):
@ -909,34 +915,36 @@ class PkgTest(ModuleCase, SaltReturnAssertsMixin):
# If this assert fails, we need to find new targets, this test needs to
# be able to test successful installation of packages, so these
# packages need to not be installed before we run the states below
self.assertTrue(bool(version))
self.assertTrue(bool(realver))
self.assertTrue(version)
self.assertTrue(realver)
pkgs = [{pkg_targets[0]: version}, pkg_targets[1], {capability: realver}]
ret = self.run_state('pkg.installed',
name='test_pkg_cap_003_installed_multipkg_with_version-install',
pkgs=pkgs,
refresh=False)
self.assertSaltFalseReturn(ret)
try:
pkgs = [{pkg_targets[0]: version}, pkg_targets[1], {capability: realver}]
ret = self.run_state('pkg.installed',
name='test_pkg_cap_003_installed_multipkg_with_version-install',
pkgs=pkgs,
refresh=False)
self.assertSaltFalseReturn(ret)
ret = self.run_state('pkg.installed',
name='test_pkg_cap_003_installed_multipkg_with_version-install-capability',
pkgs=pkgs,
refresh=False, resolve_capabilities=True, test=True)
self.assertInSaltComment("packages would be installed/updated", ret)
self.assertInSaltComment("{0}={1}".format(realpkg, realver), ret)
ret = self.run_state('pkg.installed',
name='test_pkg_cap_003_installed_multipkg_with_version-install-capability',
pkgs=pkgs,
refresh=False, resolve_capabilities=True, test=True)
self.assertInSaltComment("packages would be installed/updated", ret)
self.assertInSaltComment("{0}={1}".format(realpkg, realver), ret)
ret = self.run_state('pkg.installed',
name='test_pkg_cap_003_installed_multipkg_with_version-install-capability',
pkgs=pkgs,
refresh=False, resolve_capabilities=True)
self.assertSaltTrueReturn(ret)
cleanup_pkgs = pkg_targets
cleanup_pkgs.append(realpkg)
ret = self.run_state('pkg.removed',
name='test_pkg_cap_003_installed_multipkg_with_version-remove',
pkgs=cleanup_pkgs)
self.assertSaltTrueReturn(ret)
ret = self.run_state('pkg.installed',
name='test_pkg_cap_003_installed_multipkg_with_version-install-capability',
pkgs=pkgs,
refresh=False, resolve_capabilities=True)
self.assertSaltTrueReturn(ret)
cleanup_pkgs = pkg_targets
cleanup_pkgs.append(realpkg)
finally:
ret = self.run_state('pkg.removed',
name='test_pkg_cap_003_installed_multipkg_with_version-remove',
pkgs=cleanup_pkgs)
self.assertSaltTrueReturn(ret)
@skipIf(salt.utils.platform.is_windows(), 'minion is windows')
@requires_system_grains
@ -964,17 +972,18 @@ class PkgTest(ModuleCase, SaltReturnAssertsMixin):
self.assertFalse(version)
self.assertFalse(realver)
ret = self.run_state('pkg.latest', name=target, refresh=False, resolve_capabilities=True, test=True)
self.assertInSaltComment("The following packages would be installed/upgraded: {0}".format(realpkg), ret)
ret = self.run_state('pkg.latest', name=target, refresh=False, resolve_capabilities=True)
self.assertSaltTrueReturn(ret)
try:
ret = self.run_state('pkg.latest', name=target, refresh=False, resolve_capabilities=True, test=True)
self.assertInSaltComment("The following packages would be installed/upgraded: {0}".format(realpkg), ret)
ret = self.run_state('pkg.latest', name=target, refresh=False, resolve_capabilities=True)
self.assertSaltTrueReturn(ret)
ret = self.run_state('pkg.latest', name=target, refresh=False, resolve_capabilities=True)
self.assertSaltTrueReturn(ret)
self.assertInSaltComment("is already up-to-date", ret)
ret = self.run_state('pkg.removed', name=realpkg)
self.assertSaltTrueReturn(ret)
ret = self.run_state('pkg.latest', name=target, refresh=False, resolve_capabilities=True)
self.assertSaltTrueReturn(ret)
self.assertInSaltComment("is already up-to-date", ret)
finally:
ret = self.run_state('pkg.removed', name=realpkg)
self.assertSaltTrueReturn(ret)
@skipIf(salt.utils.platform.is_windows(), 'minion is windows')
@requires_system_grains
@ -1035,23 +1044,20 @@ class PkgTest(ModuleCase, SaltReturnAssertsMixin):
self.assertFalse(version)
self.assertFalse(realver)
ret = self.run_state('pkg.installed', name=target,
refresh=False, resolve_capabilities=True)
self.assertSaltTrueReturn(ret)
ret = self.run_state('pkg.uptodate',
name='test_pkg_cap_006_uptodate',
pkgs=[target],
refresh=False,
resolve_capabilities=True)
self.assertSaltTrueReturn(ret)
self.assertInSaltComment("System is already up-to-date", ret)
ret = self.run_state('pkg.removed', name=realpkg)
self.assertSaltTrueReturn(ret)
ret = self.run_state('pkg.uptodate',
name='test_pkg_cap_006_uptodate',
refresh=False,
test=True)
self.assertInSaltComment("System update will be performed", ret)
try:
ret = self.run_state('pkg.installed', name=target,
refresh=False, resolve_capabilities=True)
self.assertSaltTrueReturn(ret)
ret = self.run_state('pkg.uptodate',
name='test_pkg_cap_006_uptodate',
pkgs=[target],
refresh=False,
resolve_capabilities=True)
self.assertSaltTrueReturn(ret)
self.assertInSaltComment("System is already up-to-date", ret)
finally:
ret = self.run_state('pkg.removed', name=realpkg)
self.assertSaltTrueReturn(ret)
@requires_salt_modules('pkg.hold', 'pkg.unhold')
@requires_system_grains

View File

@ -267,9 +267,18 @@ class ShellTestCase(TestCase, AdaptedConfigurationTestCaseMixin):
script_path = self.get_script_path(script)
if not os.path.isfile(script_path):
return False
popen_kwargs = popen_kwargs or {}
if salt.utils.platform.is_windows():
cmd = 'python '
if 'cwd' not in popen_kwargs:
popen_kwargs['cwd'] = os.getcwd()
if 'env' not in popen_kwargs:
popen_kwargs['env'] = os.environ.copy()
if sys.version_info[0] < 3:
popen_kwargs['env'][b'PYTHONPATH'] = os.getcwd().encode()
else:
popen_kwargs['env']['PYTHONPATH'] = os.getcwd()
else:
cmd = 'PYTHONPATH='
python_path = os.environ.get('PYTHONPATH', None)
@ -286,7 +295,6 @@ class ShellTestCase(TestCase, AdaptedConfigurationTestCaseMixin):
tmp_file = tempfile.SpooledTemporaryFile()
popen_kwargs = popen_kwargs or {}
popen_kwargs = dict({
'shell': True,
'stdout': tmp_file,

View File

@ -0,0 +1,11 @@
# -*- coding: utf-8 -*-
'''
Python will always try to import sitecustomize.
We use that fact to try and support code coverage for sub-processes
'''
from __future__ import absolute_import
try:
import coverage
coverage.process_startup()
except ImportError:
pass

44
tests/tox-helper.py Normal file
View File

@ -0,0 +1,44 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# This script exists so that path handling when running tox works for both Linux and Windows
# Import Python Libs
from __future__ import absolute_import, unicode_literals
import os
import shutil
import argparse
import tempfile
def main():
parser = argparse.ArgumentParser()
parser.add_argument(
'--rootdir',
default=os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
)
subparsers = parser.add_subparsers(help='sub-command help', dest='subparser')
subparsers.add_parser('create-dirs')
subparsers.add_parser('move-artifacts')
options = parser.parse_args()
if options.subparser == 'create-dirs':
for dirname in ('logs', 'coverage', 'xml-unittests-output'):
path = os.path.join(options.rootdir, 'artifacts', dirname)
if not os.path.exists(path):
os.makedirs(path)
if options.subparser == 'move-artifacts':
tmp_artifacts_dir = os.path.join(tempfile.gettempdir(), 'artifacts')
if not os.path.exists(tmp_artifacts_dir):
os.makedirs(tmp_artifacts_dir)
for dirname in ('logs', 'coverage', 'xml-unittests-output'):
src = os.path.join(options.rootdir, 'artifacts', dirname)
dst = os.path.join(tmp_artifacts_dir, dirname)
shutil.copytree(src, dst)
if __name__ == '__main__':
main()

View File

@ -1 +1,70 @@
# -*- coding: utf-8 -*-
'''
tests.unit.cloud
~~~~~~~~~~~~~~~~
'''
from __future__ import absolute_import, print_function, unicode_literals
from tests.support.unit import TestCase
import salt.cloud
class CloudTest(TestCase):
def test_vm_config_merger(self):
'''
Validate the vm's config is generated correctly.
https://github.com/saltstack/salt/issues/49226
'''
main = {
'minion': {'master': '172.31.39.213'},
'log_file': 'var/log/salt/cloud.log',
'pool_size': 10
}
provider = {
'private_key': 'dwoz.pem',
'grains': {'foo1': 'bar', 'foo2': 'bang'},
'availability_zone': 'us-west-2b',
'driver': 'ec2',
'ssh_interface': 'private_ips',
'ssh_username': 'admin',
'location': 'us-west-2'
}
profile = {
'profile': 'default',
'grains': {'meh2': 'bar', 'meh1': 'foo'},
'provider': 'ec2-default:ec2',
'ssh_username': 'admin',
'image': 'ami-0a1fbca0e5b419fd1',
'size': 't2.micro'
}
vm = salt.cloud.Cloud.vm_config(
'test_vm',
main,
provider,
profile,
{}
)
self.assertEqual({
'minion': {'master': '172.31.39.213'},
'log_file': 'var/log/salt/cloud.log',
'pool_size': 10,
'private_key': 'dwoz.pem',
'grains': {
'foo1': 'bar',
'foo2': 'bang',
'meh2': 'bar',
'meh1': 'foo',
},
'availability_zone': 'us-west-2b',
'driver': 'ec2',
'ssh_interface': 'private_ips',
'ssh_username': 'admin',
'location': 'us-west-2',
'profile': 'default',
'provider': 'ec2-default:ec2',
'image': 'ami-0a1fbca0e5b419fd1',
'size': 't2.micro',
'name': 'test_vm',
}, vm)

View File

@ -135,26 +135,12 @@ class InspectorFSDBTestCase(TestCase):
csvdb.open()
csvdb.create_table_from_object(FoobarEntity())
sorted_writable_data = sorted(writable.data[0].strip().split(','))
if six.PY2:
assert writable.data[0].strip() == "foo:int,bar:unicode,spam:float"
sorted_expected_data = sorted("foo:int,bar:unicode,spam:float".split(','))
else:
# Order in PY3 is not the same for every run
writable_data = writable.data[0].strip()
assert_order_options = ['bar:str,foo:int,spam:float',
'bar:str,spam:float,foo:int',
'foo:int,spam:float,bar:str',
'foo:int,bar:str,spam:float',
'spam:float,foo:int,bar:str',
'spam:float,bar:str,foo:int']
while assert_order_options:
assert_option = assert_order_options.pop()
try:
assert writable_data == assert_option
break
except AssertionError:
if not assert_order_options:
raise
continue
sorted_expected_data = sorted("foo:int,bar:str,spam:float".split(','))
self.assertEqual(sorted_writable_data, sorted_expected_data)
def test_list_databases(self):
'''

View File

@ -118,9 +118,12 @@ class CassandraTestCase(TestCase, LoaderModuleMockMixin):
self.assertCountEqual(cassandra.column_families(),
{'A': ['a', 'b'], 'B': ['c', 'd']})
else:
self.assertEqual(cassandra.column_families('A'),
self.assertEqual(sorted(cassandra.column_families('A')),
['a', 'b'])
self.assertEqual(cassandra.column_families(),
column_families = cassandra.column_families()
for key in ('A', 'B'):
column_families[key] = sorted(column_families[key])
self.assertEqual(column_families,
{'A': ['a', 'b'], 'B': ['c', 'd']})
def test_column_family_definition(self):

View File

@ -54,10 +54,11 @@ class PillarModuleTestCase(TestCase, LoaderModuleMockMixin):
@skipIf(NO_MOCK, NO_MOCK_REASON)
def test_ls(self):
with patch('salt.modules.pillar.items', MagicMock(return_value=pillar_value_1)):
ls = sorted(pillarmod.ls())
if six.PY3:
self.assertCountEqual(pillarmod.ls(), ['a', 'b'])
self.assertCountEqual(ls, ['a', 'b'])
else:
self.assertEqual(pillarmod.ls(), ['a', 'b'])
self.assertEqual(ls, ['a', 'b'])
@skipIf(NO_MOCK, NO_MOCK_REASON)
def test_pillar_get_default_merge(self):

View File

@ -136,7 +136,7 @@ c9bcgp7D7xD+TxWWNj4CSXEccJgGr91StV+gFg4ARQ==
'''
ret = x509.create_private_key(text=True,
passphrase='super_secret_passphrase')
self.assertIn(b'BEGIN RSA PRIVATE KEY', ret)
self.assertIn('BEGIN RSA PRIVATE KEY', ret)
@skipIf(not HAS_M2CRYPTO, 'Skipping, M2Crypto is unavailble')
def test_create_certificate(self):
@ -176,7 +176,7 @@ c9bcgp7D7xD+TxWWNj4CSXEccJgGr91StV+gFg4ARQ==
authorityKeyIdentifier='keyid,issuer:always',
days_valid=3650,
days_remaining=0)
self.assertIn(b'BEGIN CERTIFICATE', ret)
self.assertIn('BEGIN CERTIFICATE', ret)
@skipIf(not HAS_M2CRYPTO, 'Skipping, M2Crypto is unavailble')
def test_create_crl(self):
@ -240,7 +240,7 @@ c9bcgp7D7xD+TxWWNj4CSXEccJgGr91StV+gFg4ARQ==
os.remove(ca_crl_file.name)
# Ensure that a CRL was actually created
self.assertIn(b'BEGIN X509 CRL', crl)
self.assertIn('BEGIN X509 CRL', crl)
@skipIf(not HAS_M2CRYPTO, 'Skipping, M2Crypto is unavailble')
def test_revoke_certificate_with_crl(self):

View File

@ -152,7 +152,7 @@ class TestSerializers(TestCase):
# BLAAM! yml_src is not valid !
final_obj = OrderedDict(yaml.deserialize(yml_src))
assert obj != final_obj
assert obj != final_obj, 'Objects matched! {} == {}'.format(obj, final_obj)
@skipIf(not yamlex.available, SKIP_MESSAGE % 'sls')
def test_sls_aggregate(self):

View File

@ -60,6 +60,7 @@ class GitTestCase(TestCase, LoaderModuleMockMixin):
git_diff = Mock()
dunder_salt = {
'git.current_branch': MagicMock(return_value=branches[0]),
'git.config_get_regexp': MagicMock(return_value={}),
'git.diff': git_diff,
'git.fetch': MagicMock(return_value={}),
'git.is_worktree': MagicMock(return_value=False),

View File

@ -14,9 +14,6 @@ from tests.support.mock import (
call
)
# Import 3rd-party libs
from salt.ext import six
# Import Salt Libs
import salt.states.proxy as proxy
@ -70,9 +67,7 @@ class ProxyTestCase(TestCase, LoaderModuleMockMixin):
with patch.dict(proxy.__salt__, patches):
out = proxy.managed('192.168.0.1', '3128', user='frank', password='passw0rd',
bypass_domains=['salt.com', 'test.com'])
if six.PY3:
# Sorting is different in Py3
out['changes']['new'][-1]['bypass_domains'] = sorted(out['changes']['new'][-1]['bypass_domains'])
out['changes']['new'][-1]['bypass_domains'] = sorted(out['changes']['new'][-1]['bypass_domains'])
calls = [
call('192.168.0.1', '3128', 'frank', 'passw0rd', 'Ethernet'),

View File

@ -47,6 +47,7 @@ EXCLUDED_FILES = [
os.path.join('tests', 'modparser.py'),
os.path.join('tests', 'committer_parser.py'),
os.path.join('tests', 'zypp_plugin.py'),
os.path.join('tests', 'tox-helper.py'),
os.path.join('tests', 'unit', 'transport', 'mixins.py'),
os.path.join('tests', 'integration', 'utils', 'testprogram.py'),
]

View File

@ -577,6 +577,7 @@ class PubServerChannel(TestCase, AdaptedConfigurationTestCaseMixin):
executor.submit(self._send_small, opts, 3)
executor.submit(self._send_large, opts, 4)
expect = ['{}-{}'.format(a, b) for a in range(10) for b in (1, 2, 3, 4)]
time.sleep(0.1)
server_channel.publish({'tgt_type': 'glob', 'tgt': '*', 'stop': True})
gather.join()
server_channel.pub_close()

View File

@ -14,6 +14,7 @@ from salt.utils.odict import OrderedDict
import salt.utils.dns
from salt.utils.dns import _to_port, _tree, _weighted_order, _data2rec, _data2rec_group
from salt.utils.dns import _lookup_gai, _lookup_dig, _lookup_drill, _lookup_host, _lookup_nslookup
from salt.utils.dns import lookup
# Testing
from tests.support.unit import skipIf, TestCase
@ -277,6 +278,46 @@ class DNSlookupsCase(TestCase):
msg='Error parsing DNSSEC\'d {0} returns'.format(rec_t)
)
def test_lookup_with_servers(self):
rights = {
'A': [
'Name:\tmocksrvr.example.com\nAddress: 10.1.1.1',
'Name:\tmocksrvr.example.com\nAddress: 10.1.1.1\n'
'Name:\tweb.example.com\nAddress: 10.2.2.2\n'
'Name:\tweb.example.com\nAddress: 10.3.3.3'
],
'AAAA': [
'mocksrvr.example.com\thas AAAA address 2a00:a00:b01:c02:d03:e04:f05:111',
'mocksrvr.example.com\tcanonical name = web.example.com.\n'
'web.example.com\thas AAAA address 2a00:a00:b01:c02:d03:e04:f05:111\n'
'web.example.com\thas AAAA address 2a00:a00:b01:c02:d03:e04:f05:222\n'
'web.example.com\thas AAAA address 2a00:a00:b01:c02:d03:e04:f05:333'
],
'CNAME': [
'mocksrvr.example.com\tcanonical name = web.example.com.'
],
'MX': [
'example.com\tmail exchanger = 10 mx1.example.com.',
'example.com\tmail exchanger = 10 mx1.example.com.\n'
'example.com\tmail exchanger = 20 mx2.example.eu.\n'
'example.com\tmail exchanger = 30 mx3.example.nl.'
],
'TXT': [
'example.com\ttext = "v=spf1 a include:_spf4.example.com include:mail.example.eu ip4:10.0.0.0/8 ip6:2a00:a00:b01::/48 ~all"'
]
}
for rec_t, tests in rights.items():
with self._mock_cmd_ret([dict([('stdout', dres)]) for dres in tests]):
for test_res in self.RESULTS[rec_t]:
if rec_t in ('A', 'AAAA', 'CNAME'):
rec = 'mocksrvr.example.com'
else:
rec = 'example.com'
self.assertEqual(
lookup(rec, rec_t, method='nslookup', servers='8.8.8.8'), test_res,
)
def test_dig(self):
wrong_type = {'retcode': 0, 'stderr': ';; Warning, ignoring invalid type ABC'}
@ -442,13 +483,13 @@ class DNSlookupsCase(TestCase):
empty = {'stdout': 'www.example.com has no MX record'}
# example returns for dig
# example returns for host
rights = {
'A': [
'mocksrvr.example.com has address 10.1.1.1',
'web.example.com has address 10.1.1.1\n'
'web.example.com has address 10.2.2.2\n'
'web.example.com has address 10.3.3.3'
'web.example.com has address 10.1.1.1\n'
'web.example.com has address 10.2.2.2\n'
'web.example.com has address 10.3.3.3'
],
'AAAA': [

View File

@ -206,12 +206,12 @@ class NetworkTestCase(TestCase):
def test_parse_host_port(self):
_ip = ipaddress.ip_address
good_host_ports = {
'10.10.0.3': (_ip('10.10.0.3'), None),
'10.10.0.3:1234': (_ip('10.10.0.3'), 1234),
'2001:0db8:85a3::8a2e:0370:7334': (_ip('2001:0db8:85a3::8a2e:0370:7334'), None),
'[2001:0db8:85a3::8a2e:0370:7334]:1234': (_ip('2001:0db8:85a3::8a2e:0370:7334'), 1234),
'2001:0db8:85a3::7334': (_ip('2001:0db8:85a3::7334'), None),
'[2001:0db8:85a3::7334]:1234': (_ip('2001:0db8:85a3::7334'), 1234)
'10.10.0.3': (_ip('10.10.0.3').compressed, None),
'10.10.0.3:1234': (_ip('10.10.0.3').compressed, 1234),
'2001:0db8:85a3::8a2e:0370:7334': (_ip('2001:0db8:85a3::8a2e:0370:7334').compressed, None),
'[2001:0db8:85a3::8a2e:0370:7334]:1234': (_ip('2001:0db8:85a3::8a2e:0370:7334').compressed, 1234),
'2001:0db8:85a3::7334': (_ip('2001:0db8:85a3::7334').compressed, None),
'[2001:0db8:85a3::7334]:1234': (_ip('2001:0db8:85a3::7334').compressed, 1234)
}
bad_host_ports = [
'10.10.0.3/24',
@ -232,6 +232,54 @@ class NetworkTestCase(TestCase):
log.error('bad host_port value: "%s" failed to trigger ValueError exception', host_port)
raise _e_
def test_dns_check(self):
class MockSocket(object):
def __init__(self, *args, **kwargs):
pass
def __call__(self, *args, **kwargs):
pass
def setsockopt(self, *args, **kwargs):
pass
def sendto(self, *args, **kwargs):
pass
def connect(self, *args, **kwargs):
pass
def close(self, *args, **kwargs):
pass
hosts = [
{'host': '10.10.0.3',
'port': '',
'mocked': [(2, 1, 6, '', ('10.10.0.3', 0))],
'ret': '10.10.0.3'},
{'host': '10.10.0.3',
'port': '1234',
'mocked': [(2, 1, 6, '', ('10.10.0.3', 0))],
'ret': '10.10.0.3'},
{'host': '2001:0db8:85a3::8a2e:0370:7334',
'port': '',
'mocked': [(10, 1, 6, '', ('2001:db8:85a3::8a2e:370:7334', 0, 0, 0))],
'ret': '2001:db8:85a3::8a2e:370:7334'},
{'host': '2001:0db8:85a3::8a2e:370:7334',
'port': '1234',
'mocked': [(10, 1, 6, '', ('2001:db8:85a3::8a2e:370:7334', 0, 0, 0))],
'ret': '2001:db8:85a3::8a2e:370:7334'},
{'host': 'salt-master',
'port': '1234',
'mocked': [(2, 1, 6, '', ('127.0.0.1', 0))],
'ret': '127.0.0.1'},
]
for host in hosts:
with patch.object(socket, 'getaddrinfo', MagicMock(return_value=host['mocked'])):
with patch('socket.socket', MockSocket):
ret = network.dns_check(host['host'], host['port'])
self.assertEqual(ret, host['ret'])
def test_is_subnet(self):
for subnet_data in (IPV4_SUBNETS, IPV6_SUBNETS):
for item in subnet_data[True]:

View File

@ -17,6 +17,7 @@ from tests.support.unit import TestCase, skipIf
from tests.support.mock import patch, NO_MOCK, NO_MOCK_REASON
# Import Salt libs
import salt.utils.compat
import salt.utils.path
import salt.utils.platform
from salt.exceptions import CommandNotFoundError
@ -125,7 +126,7 @@ class PathJoinTestCase(TestCase):
platform.system = lambda: "windows"
for module in (ntpath, os, os.path, tempfile):
reload(module)
salt.utils.compat.reload(module)
def __unpatch_path(self):
del sys.modules['nt']
@ -133,7 +134,7 @@ class PathJoinTestCase(TestCase):
platform.system = self.PLATFORM_FUNC
for module in (posixpath, os, os.path, tempfile, platform):
reload(module)
salt.utils.compat.reload(module)
@skipIf(NO_MOCK, NO_MOCK_REASON)

210
tox.ini
View File

@ -1,13 +1,216 @@
[tox]
envlist = py27,py34,py35,py36,pylint-salt,pylint-tests
envlist =
py{27,34,35,36},
py{27,34,35,36}-coverage,
py{27,34,35,36}-pytest,
py{27,34,35,36}-runtests,
py{27,34,35,36}-pytest-coverage,
py{27,34,35,36}-runtests-coverage,
pylint-salt,
pylint-tests
skip_missing_interpreters = True
skipsdist = True
[testenv]
deps = -Ur{toxinidir}/requirements/tests.txt
commands = pytest --rootdir {toxinidir} {posargs}
changedir = {toxinidir}
commands_pre = {envpython} tests/tox-helper.py create-dirs
passenv = LANG HOME
sitepackages = True
commands = {[testenv:runtests]commands}
[testenv:runtests]
deps =
{[testenv]deps}
unittest-xml-reporting
commands = {envpython} {toxinidir}/tests/runtests.py --tests-logfile={toxinidir}/artifacts/logs/runtests.log {posargs}
[testenv:pytest]
commands = pytest --rootdir {toxinidir} --log-file={toxinidir}/artifacts/logs/runtests.log {posargs}
[testenv:runtests-coverage]
# Add tests/support/coverage to PYTHONPATH in order to get code coverage from subprocesses.
setenv =
PYTHONPATH={toxinidir}/tests/support/coverage
commands_pre =
- coverage erase
commands =
coverage run -m tests.runtests {posargs}
commands_post =
- coverage combine
- coverage xml -o {toxinidir}/artifacts/coverage/coverage.xml
[testenv:pytest-coverage]
setenv = {[testenv:runtests-coverage]setenv}
commands_pre = {[testenv:runtests-coverage]commands_pre}
commands = coverage run -m py.test --rootdir {toxinidir} {posargs}
commands_post = {[testenv:runtests-coverage]commands_post}
[testenv:py2-pytest]
commands = {[testenv:pytest]commands}
[testenv:py2-runtests]
deps = {[testenv:runtests]deps}
commands = {[testenv:runtests]commands}
[testenv:py2-coverage]
deps = {[testenv:runtests]deps}
setenv = {[testenv:runtests-coverage]setenv}
commands = {[testenv:runtests-coverage]commands}
commands_pre = {[testenv:runtests-coverage]commands_pre}
commands_post = {[testenv:runtests-coverage]commands_post}
[testenv:py2-runtests-coverage]
deps = {[testenv:runtests]deps}
setenv = {[testenv:runtests-coverage]setenv}
commands = {[testenv:runtests-coverage]commands}
commands_pre = {[testenv:runtests-coverage]commands_pre}
commands_post = {[testenv:runtests-coverage]commands_post}
[testenv:py2-pytest-coverage]
setenv = {[testenv:runtests-coverage]setenv}
commands = {[testenv:pytest-coverage]commands}
commands_pre = {[testenv:pytest-coverage]commands_pre}
commands_post = {[testenv:pytest-coverage]commands_post}
[testenv:py27-pytest]
commands = {[testenv:pytest]commands}
[testenv:py27-runtests]
deps = {[testenv:runtests]deps}
commands = {[testenv:runtests]commands}
[testenv:py27-coverage]
deps = {[testenv:runtests]deps}
setenv = {[testenv:runtests-coverage]setenv}
commands = {[testenv:runtests-coverage]commands}
commands_pre = {[testenv:runtests-coverage]commands_pre}
commands_post = {[testenv:runtests-coverage]commands_post}
[testenv:py27-runtests-coverage]
deps = {[testenv:runtests]deps}
setenv = {[testenv:runtests-coverage]setenv}
commands = {[testenv:runtests-coverage]commands}
commands_pre = {[testenv:runtests-coverage]commands_pre}
commands_post = {[testenv:runtests-coverage]commands_post}
[testenv:py27-pytest-coverage]
setenv = {[testenv:runtests-coverage]setenv}
commands = {[testenv:pytest-coverage]commands}
commands_pre = {[testenv:pytest-coverage]commands_pre}
commands_post = {[testenv:pytest-coverage]commands_post}
[testenv:py3-pytest]
commands = {[testenv:pytest]commands}
[testenv:py3-runtests]
deps = {[testenv:runtests]deps}
commands = {[testenv:runtests]commands}
[testenv:py3-coverage]
deps = {[testenv:runtests]deps}
setenv = {[testenv:runtests-coverage]setenv}
commands = {[testenv:runtests-coverage]commands}
commands_pre = {[testenv:runtests-coverage]commands_pre}
commands_post = {[testenv:runtests-coverage]commands_post}
[testenv:py3-runtests-coverage]
deps = {[testenv:runtests]deps}
setenv = {[testenv:runtests-coverage]setenv}
commands = {[testenv:runtests-coverage]commands}
commands_pre = {[testenv:runtests-coverage]commands_pre}
commands_post = {[testenv:runtests-coverage]commands_post}
[testenv:py3-pytest-coverage]
setenv = {[testenv:runtests-coverage]setenv}
commands = {[testenv:pytest-coverage]commands}
commands_pre = {[testenv:pytest-coverage]commands_pre}
commands_post = {[testenv:pytest-coverage]commands_post}
[testenv:py34-pytest]
commands = {[testenv:pytest]commands}
[testenv:py34-runtests]
deps = {[testenv:runtests]deps}
commands = {[testenv:runtests]commands}
[testenv:py34-coverage]
deps = {[testenv:runtests]deps}
setenv = {[testenv:runtests-coverage]setenv}
commands = {[testenv:runtests-coverage]commands}
commands_pre = {[testenv:runtests-coverage]commands_pre}
commands_post = {[testenv:runtests-coverage]commands_post}
[testenv:py34-runtests-coverage]
deps = {[testenv:runtests]deps}
setenv = {[testenv:runtests-coverage]setenv}
commands = {[testenv:runtests-coverage]commands}
commands_pre = {[testenv:runtests-coverage]commands_pre}
commands_post = {[testenv:runtests-coverage]commands_post}
[testenv:py34-pytest-coverage]
setenv = {[testenv:runtests-coverage]setenv}
commands = {[testenv:pytest-coverage]commands}
commands_pre = {[testenv:pytest-coverage]commands_pre}
commands_post = {[testenv:pytest-coverage]commands_post}
[testenv:py35-pytest]
commands = {[testenv:pytest]commands}
[testenv:py35-runtests]
deps = {[testenv:runtests]deps}
commands = {[testenv:runtests]commands}
[testenv:py35-coverage]
deps = {[testenv:runtests]deps}
setenv = {[testenv:runtests-coverage]setenv}
commands = {[testenv:runtests-coverage]commands}
commands_pre = {[testenv:runtests-coverage]commands_pre}
commands_post = {[testenv:runtests-coverage]commands_post}
[testenv:py35-runtests-coverage]
deps = {[testenv:runtests]deps}
setenv = {[testenv:runtests-coverage]setenv}
commands = {[testenv:runtests-coverage]commands}
commands_pre = {[testenv:runtests-coverage]commands_pre}
commands_post = {[testenv:runtests-coverage]commands_post}
[testenv:py35-pytest-coverage]
setenv = {[testenv:runtests-coverage]setenv}
commands = {[testenv:pytest-coverage]commands}
commands_pre = {[testenv:pytest-coverage]commands_pre}
commands_post = {[testenv:pytest-coverage]commands_post}
[testenv:py36-pytest]
commands = {[testenv:pytest]commands}
[testenv:py36-runtests]
deps = {[testenv:runtests]deps}
commands = {[testenv:runtests]commands}
[testenv:py36-coverage]
deps = {[testenv:runtests]deps}
setenv = {[testenv:runtests-coverage]setenv}
commands = {[testenv:runtests-coverage]commands}
commands_pre = {[testenv:runtests-coverage]commands_pre}
commands_post = {[testenv:runtests-coverage]commands_post}
[testenv:py36-runtests-coverage]
deps = {[testenv:runtests]deps}
setenv = {[testenv:runtests-coverage]setenv}
commands = {[testenv:runtests-coverage]commands}
commands_pre = {[testenv:runtests-coverage]commands_pre}
commands_post = {[testenv:runtests-coverage]commands_post}
[testenv:py36-pytest-coverage]
setenv = {[testenv:runtests-coverage]setenv}
commands = {[testenv:pytest-coverage]commands}
commands_pre = {[testenv:pytest-coverage]commands_pre}
commands_post = {[testenv:pytest-coverage]commands_post}
[testenv:pylint-salt]
basepython = python2.7
@ -17,6 +220,7 @@ commands =
pylint --rcfile=.testing.pylintrc --disable=I,W1307,C0411,C0413,W8410,str-format-in-logging {posargs:setup.py salt/}
sitepackages = False
[testenv:pylint-tests]
basepython = python2.7
deps = -r{toxinidir}/requirements/dev.txt
@ -26,6 +230,6 @@ commands =
sitepackages = False
[pytest]
addopts = --log-file /tmp/salt-runtests.log --no-print-logs --ssh-tests -ra -sv
addopts = --no-print-logs --ssh-tests -ra -sv
testpaths = tests
norecursedirs = tests/kitchen