Merge branch '2018.3' into issue51255

This commit is contained in:
Pedro Algarvio 2019-01-28 23:19:04 +00:00 committed by GitHub
commit 04eb66bc8e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 638 additions and 148 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

@ -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-from-filenames >= 2019.1.22

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

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

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

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

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

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

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

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

@ -2530,6 +2530,26 @@ 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

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

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

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

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

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

213
tox.ini
View File

@ -1,13 +1,219 @@
[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.
# Additional, set the COVERAGE_PROCESS_START environment variable so that the coverage library
# knows it's supposed to track subprocesses.
setenv =
PYTHONPATH={toxinidir}/tests/support/coverage
COVERAGE_PROCESS_START={toxinidir}/.coveragerc
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 +223,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 +233,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