Merge branch '2017.7' into merge-2017.7-2018.3

Conflicts:
- salt/cloud/clouds/openstack.py
- salt/daemons/masterapi.py
- salt/master.py
- salt/modules/gentoo_service.py
- salt/pillar/__init__.py
- salt/pillar/git_pillar.py
- salt/runners/cache.py
- salt/runners/git_pillar.py
- salt/runners/winrepo.py
- salt/utils/docker/__init__.py
- salt/utils/gitfs.py
This commit is contained in:
Erik Johnson 2018-03-02 16:20:20 -06:00
commit 61ab47ee70
No known key found for this signature in database
GPG key ID: 5E5583C437808F3F
19 changed files with 245 additions and 57 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 739 KiB

After

Width:  |  Height:  |  Size: 109 KiB

View file

@ -5304,11 +5304,10 @@ branch/tag.
winrepo_branch: winrepo
ext_pillar:
- git:
- https://mygitserver/winrepo1.git
- https://mygitserver/winrepo2.git:
- foo https://mygitserver/winrepo3.git
winrepo_remotes:
- https://mygitserver/winrepo1.git
- https://mygitserver/winrepo2.git:
- foo https://mygitserver/winrepo3.git
.. conf_master:: winrepo_ssl_verify

View file

@ -76,7 +76,8 @@ def init_git_pillar(opts):
opts,
opts_dict['git'],
per_remote_overrides=git_pillar.PER_REMOTE_OVERRIDES,
per_remote_only=git_pillar.PER_REMOTE_ONLY)
per_remote_only=git_pillar.PER_REMOTE_ONLY,
global_only=git_pillar.GLOBAL_ONLY)
ret.append(pillar)
except salt.exceptions.FileserverConfigError:
if opts.get('git_pillar_verify_config', True):

View file

@ -601,7 +601,8 @@ class Master(SMaster):
new_opts,
repo['git'],
per_remote_overrides=salt.pillar.git_pillar.PER_REMOTE_OVERRIDES,
per_remote_only=salt.pillar.git_pillar.PER_REMOTE_ONLY)
per_remote_only=salt.pillar.git_pillar.PER_REMOTE_ONLY,
global_only=salt.pillar.git_pillar.GLOBAL_ONLY)
except salt.exceptions.FileserverConfigError as exc:
critical_errors.append(exc.strerror)
finally:

View file

@ -589,6 +589,15 @@ def _scrub_links(links, name):
return ret
def _ulimit_sort(ulimit_val):
if isinstance(ulimit_val, list):
return sorted(ulimit_val,
key=lambda x: (x.get('Name'),
x.get('Hard', 0),
x.get('Soft', 0)))
return ulimit_val
def _size_fmt(num):
'''
Format bytes as human-readable file sizes
@ -932,6 +941,9 @@ def compare_containers(first, second, ignore=None):
if item == 'Links':
val1 = sorted(_scrub_links(val1, first))
val2 = sorted(_scrub_links(val2, second))
if item == 'Ulimits':
val1 = _ulimit_sort(val1)
val2 = _ulimit_sort(val2)
if val1 != val2:
ret.setdefault(conf_dict, {})[item] = {'old': val1, 'new': val2}
# Check for optionally-present items that were in the second container
@ -955,6 +967,9 @@ def compare_containers(first, second, ignore=None):
if item == 'Links':
val1 = sorted(_scrub_links(val1, first))
val2 = sorted(_scrub_links(val2, second))
if item == 'Ulimits':
val1 = _ulimit_sort(val1)
val2 = _ulimit_sort(val2)
if val1 != val2:
ret.setdefault(conf_dict, {})[item] = {'old': val1, 'new': val2}
return ret

View file

@ -39,9 +39,9 @@ def __virtual__():
'only available on Gentoo/Open-RC systems.')
def _ret_code(cmd):
def _ret_code(cmd, ignore_retcode=False):
log.debug('executing [{0}]'.format(cmd))
sts = __salt__['cmd.retcode'](cmd, python_shell=False)
sts = __salt__['cmd.retcode'](cmd, python_shell=False, ignore_retcode=ignore_retcode)
return sts
@ -270,7 +270,7 @@ def status(name, sig=None):
results = {}
for service in services:
cmd = _service_cmd(service, 'status')
results[service] = not _ret_code(cmd)
results[service] = not _ret_code(cmd, ignore_retcode=True)
if contains_globbing:
return results
return results[name]

View file

@ -900,7 +900,8 @@ class Pillar(object):
self.opts,
self.ext['git'],
per_remote_overrides=salt.pillar.git_pillar.PER_REMOTE_OVERRIDES,
per_remote_only=salt.pillar.git_pillar.PER_REMOTE_ONLY)
per_remote_only=salt.pillar.git_pillar.PER_REMOTE_ONLY,
global_only=salt.pillar.git_pillar.GLOBAL_ONLY)
git_pillar.fetch_remotes()
except TypeError:
# Handle malformed ext_pillar

View file

@ -347,6 +347,7 @@ from salt.ext import six
PER_REMOTE_OVERRIDES = ('env', 'root', 'ssl_verify', 'refspecs')
PER_REMOTE_ONLY = ('name', 'mountpoint')
GLOBAL_ONLY = ('base', 'branch')
# Set up logging
log = logging.getLogger(__name__)
@ -385,7 +386,8 @@ def ext_pillar(minion_id, pillar, *repos): # pylint: disable=unused-argument
opts,
repos,
per_remote_overrides=PER_REMOTE_OVERRIDES,
per_remote_only=PER_REMOTE_ONLY)
per_remote_only=PER_REMOTE_ONLY,
global_only=GLOBAL_ONLY)
if __opts__.get('__role') == 'minion':
# If masterless, fetch the remotes. We'll need to remove this once
# we make the minion daemon able to run standalone.

View file

@ -355,7 +355,8 @@ def clear_git_lock(role, remote=None, **kwargs):
__opts__,
ext_pillar['git'],
per_remote_overrides=salt.pillar.git_pillar.PER_REMOTE_OVERRIDES,
per_remote_only=salt.pillar.git_pillar.PER_REMOTE_ONLY)
per_remote_only=salt.pillar.git_pillar.PER_REMOTE_ONLY,
global_only=salt.pillar.git_pillar.GLOBAL_ONLY)
git_objects.append(obj)
elif role == 'winrepo':
winrepo_dir = __opts__['winrepo_dir']
@ -371,6 +372,7 @@ def clear_git_lock(role, remote=None, **kwargs):
remotes,
per_remote_overrides=salt.runners.winrepo.PER_REMOTE_OVERRIDES,
per_remote_only=salt.runners.winrepo.PER_REMOTE_ONLY,
global_only=salt.runners.winrepo.GLOBAL_ONLY,
cache_root=base_dir)
git_objects.append(obj)
else:

View file

@ -164,6 +164,20 @@ def file_list(saltenv='base', backend=None):
.. versionadded:: 2015.5.0
.. note:
Keep in mind that executing this function spawns a new process,
separate from the master. This means that if the fileserver
configuration has been changed in some way since the master has been
restarted (e.g. if :conf_master:`fileserver_backend`,
:conf_master:`gitfs_remotes`, :conf_master:`hgfs_remotes`, etc. have
been updated), then the results of this runner will not accurately
reflect what files are available to minions.
When in doubt, use :py:func:`cp.list_master
<salt.modules.cp.list_master>` to see what files the minion can see,
and always remember to restart the salt-master daemon when updating
the fileserver configuration.
CLI Examples:
.. code-block:: bash
@ -196,6 +210,20 @@ def symlink_list(saltenv='base', backend=None):
.. versionadded:: 2015.5.0
.. note:
Keep in mind that executing this function spawns a new process,
separate from the master. This means that if the fileserver
configuration has been changed in some way since the master has been
restarted (e.g. if :conf_master:`fileserver_backend`,
:conf_master:`gitfs_remotes`, :conf_master:`hgfs_remotes`, etc. have
been updated), then the results of this runner will not accurately
reflect what symlinks are available to minions.
When in doubt, use :py:func:`cp.list_master_symlinks
<salt.modules.cp.list_master_symlinks>` to see what symlinks the minion
can see, and always remember to restart the salt-master daemon when
updating the fileserver configuration.
CLI Example:
.. code-block:: bash
@ -228,6 +256,20 @@ def dir_list(saltenv='base', backend=None):
.. versionadded:: 2015.5.0
.. note:
Keep in mind that executing this function spawns a new process,
separate from the master. This means that if the fileserver
configuration has been changed in some way since the master has been
restarted (e.g. if :conf_master:`fileserver_backend`,
:conf_master:`gitfs_remotes`, :conf_master:`hgfs_remotes`, etc. have
been updated), then the results of this runner will not accurately
reflect what dirs are available to minions.
When in doubt, use :py:func:`cp.list_master_dirs
<salt.modules.cp.list_master_dirs>` to see what dirs the minion can see,
and always remember to restart the salt-master daemon when updating
the fileserver configuration.
CLI Example:
.. code-block:: bash

View file

@ -70,7 +70,8 @@ def update(branch=None, repo=None):
__opts__,
pillar_conf,
per_remote_overrides=salt.pillar.git_pillar.PER_REMOTE_OVERRIDES,
per_remote_only=salt.pillar.git_pillar.PER_REMOTE_ONLY)
per_remote_only=salt.pillar.git_pillar.PER_REMOTE_ONLY,
global_only=salt.pillar.git_pillar.GLOBAL_ONLY)
for remote in pillar.remotes:
# Skip this remote if it doesn't match the search criteria
if branch is not None:

View file

@ -37,6 +37,7 @@ PER_REMOTE_OVERRIDES = ('ssl_verify', 'refspecs')
# salt.utils.gitfs.PER_REMOTE_ONLY for this value, so this is mainly for
# runners and other modules that import salt.runners.winrepo.
PER_REMOTE_ONLY = salt.utils.gitfs.PER_REMOTE_ONLY
GLOBAL_ONLY = ('branch',)
def genrepo(opts=None, fire_event=True):
@ -164,7 +165,8 @@ def update_git_repos(opts=None, clean=False, masterless=False):
ret = {}
for remotes, base_dir in winrepo_cfg:
if not any((salt.utils.gitfs.HAS_GITPYTHON, salt.utils.gitfs.HAS_PYGIT2)):
if not any((salt.utils.gitfs.GITPYTHON_VERSION,
salt.utils.gitfs.PYGIT2_VERSION)):
# Use legacy code
winrepo_result = {}
for remote_info in remotes:
@ -217,6 +219,7 @@ def update_git_repos(opts=None, clean=False, masterless=False):
remotes,
per_remote_overrides=PER_REMOTE_OVERRIDES,
per_remote_only=PER_REMOTE_ONLY,
global_only=GLOBAL_ONLY,
cache_root=base_dir)
winrepo.fetch_remotes()
# Since we're not running update(), we need to manually call

View file

@ -696,7 +696,7 @@ def extracted(name,
# True
# >>> os.path.isfile('/tmp/foo.txt/')
# False
name = name.rstrip('/')
name = name.rstrip(os.sep)
if os.path.isfile(name):
ret['comment'] = '{0} exists and is not a directory'.format(name)
return ret
@ -729,6 +729,11 @@ def extracted(name,
)
return ret
if if_missing is not None and os.path.exists(if_missing):
ret['result'] = True
ret['comment'] = 'Path {0} exists'.format(if_missing)
return ret
if user or group:
if salt.utils.platform.is_windows():
ret['comment'] = \
@ -1511,7 +1516,7 @@ def extracted(name,
if not if_missing:
# If is_missing was used, and both a) the archive had never been
# extracted, and b) the path referred to by if_missing exists, then
# enforce_missing would contain paths of top_levle dirs/files that
# enforce_missing would contain paths of top_level dirs/files that
# _would_ have been extracted. Since if_missing can be used as a
# semaphore to conditionally extract, we don't want to make this a
# case where the state fails, so we only fail the state if

View file

@ -1564,14 +1564,14 @@ def running(name,
.. code-block:: yaml
foo:
dockerng.running:
docker_container.running:
- image: bar/baz:latest
- ulimits: nofile=1024:1024,nproc=60
.. code-block:: yaml
foo:
dockerng.running:
docker_container.running:
- image: bar/baz:latest
- ulimits:
- nofile=1024:1024

View file

@ -8,6 +8,7 @@ input as formatted by states.
# Import Python libs
from __future__ import absolute_import, print_function, unicode_literals
import copy
import logging
import os
@ -183,7 +184,7 @@ def translate_input(translator,
of that tuple will have their translation skipped. Optionally,
skip_translate can be set to True to skip *all* translation.
'''
kwargs = salt.utils.args.clean_kwargs(**kwargs)
kwargs = copy.deepcopy(salt.utils.clean_kwargs(**kwargs))
invalid = {}
collisions = []

View file

@ -55,6 +55,9 @@ VALID_REF_TYPES = _DEFAULT_MASTER_OPTS['gitfs_ref_types']
# Optional per-remote params that can only be used on a per-remote basis, and
# thus do not have defaults in salt/config.py.
PER_REMOTE_ONLY = ('name',)
# Params which are global only and cannot be overridden for a single remote.
GLOBAL_ONLY = ()
SYMLINK_RECURSE_DEPTH = 100
# Auth support (auth params can be global or per-remote, too)
@ -357,7 +360,7 @@ class GitProvider(object):
salt.utils.url.strip_proto(saltenv_ptr['mountpoint'])
for key, val in six.iteritems(self.conf):
if key not in PER_SALTENV_PARAMS:
if key not in PER_SALTENV_PARAMS and not hasattr(self, key):
setattr(self, key, val)
for key in PER_SALTENV_PARAMS:
@ -973,13 +976,13 @@ class GitProvider(object):
'''
Resolve dynamically-set branch
'''
if self.branch == '__env__':
if self.role == 'git_pillar' and self.branch == '__env__':
target = self.opts.get('pillarenv') \
or self.opts.get('saltenv') \
or 'base'
return self.opts['{0}_base'.format(self.role)] \
return self.base \
if target == 'base' \
else target
else six.text_type(target)
return self.branch
def get_tree(self, tgt_env):
@ -1021,7 +1024,7 @@ class GitProvider(object):
try:
self.branch, self.url = self.id.split(None, 1)
except ValueError:
self.branch = self.opts['{0}_branch'.format(self.role)]
self.branch = self.conf['branch']
self.url = self.id
else:
self.url = self.id
@ -2026,8 +2029,8 @@ class GitBase(object):
Base class for gitfs/git_pillar
'''
def __init__(self, opts, remotes=None, per_remote_overrides=(),
per_remote_only=PER_REMOTE_ONLY, git_providers=None,
cache_root=None, init_remotes=True):
per_remote_only=PER_REMOTE_ONLY, global_only=GLOBAL_ONLY,
git_providers=None, cache_root=None, init_remotes=True):
'''
IMPORTANT: If specifying a cache_root, understand that this is also
where the remotes will be cloned. A non-default cache_root is only
@ -2085,10 +2088,12 @@ class GitBase(object):
self.init_remotes(
remotes if remotes is not None else [],
per_remote_overrides,
per_remote_only)
per_remote_only,
global_only)
def init_remotes(self, remotes, per_remote_overrides=(),
per_remote_only=PER_REMOTE_ONLY):
per_remote_only=PER_REMOTE_ONLY,
global_only=GLOBAL_ONLY):
'''
Initialize remotes
'''
@ -2121,7 +2126,9 @@ class GitBase(object):
failhard(self.role)
per_remote_defaults = {}
for param in override_params:
global_values = set(override_params)
global_values.update(set(global_only))
for param in global_values:
key = '{0}_{1}'.format(self.role, param)
if key not in self.opts:
log.critical(
@ -2967,8 +2974,7 @@ class GitPillar(GitBase):
if repo.env:
env = repo.env
else:
base_branch = self.opts['{0}_base'.format(self.role)]
env = 'base' if repo.branch == base_branch else repo.branch
env = 'base' if repo.branch == repo.base else repo.branch
if repo._mountpoint:
if self.link_mountpoint(repo):
self.pillar_dirs[repo.linkdir] = env
@ -3095,6 +3101,9 @@ class WinRepo(GitBase):
Functionality specific to the winrepo runner
'''
role = 'winrepo'
# Need to define this in case we try to reference it before checking
# out the repos.
winrepo_dirs = {}
def checkout(self):
'''

View file

@ -754,6 +754,35 @@ class DockerTestCase(TestCase, LoaderModuleMockMixin):
ret = docker_mod.compare_containers('container1', 'container2')
self.assertEqual(ret, {})
def test_compare_container_ulimits_order(self):
'''
Test comparing two containers when the order of the Ulimits HostConfig
values are different, but the values are the same.
'''
def _inspect_container_effect(id_):
return {
'container1': {'Config': {},
'HostConfig': {
'Ulimits': [
{u'Hard': -1, u'Soft': -1, u'Name': u'core'},
{u'Hard': 65536, u'Soft': 65536, u'Name': u'nofile'}
]
}},
'container2': {'Config': {},
'HostConfig': {
'Ulimits': [
{u'Hard': 65536, u'Soft': 65536, u'Name': u'nofile'},
{u'Hard': -1, u'Soft': -1, u'Name': u'core'}
]
}},
}[id_]
inspect_container_mock = MagicMock(side_effect=_inspect_container_effect)
with patch.object(docker_mod, 'inspect_container', inspect_container_mock):
ret = docker_mod.compare_container('container1', 'container2')
self.assertEqual(ret, {})
def test_resolve_tag(self):
'''
Test the resolve_tag function. It runs docker.insect_image on the image

View file

@ -151,7 +151,9 @@ class GentooServicesTestCase(TestCase, LoaderModuleMockMixin):
mock = MagicMock(return_value=True)
with patch.dict(gentoo_service.__salt__, {'cmd.retcode': mock}):
self.assertFalse(gentoo_service.start('name'))
mock.assert_called_once_with('/etc/init.d/name start', python_shell=False)
mock.assert_called_once_with('/etc/init.d/name start',
ignore_retcode=False,
python_shell=False)
def test_stop(self):
'''
@ -160,7 +162,9 @@ class GentooServicesTestCase(TestCase, LoaderModuleMockMixin):
mock = MagicMock(return_value=True)
with patch.dict(gentoo_service.__salt__, {'cmd.retcode': mock}):
self.assertFalse(gentoo_service.stop('name'))
mock.assert_called_once_with('/etc/init.d/name stop', python_shell=False)
mock.assert_called_once_with('/etc/init.d/name stop',
ignore_retcode=False,
python_shell=False)
def test_restart(self):
'''
@ -169,7 +173,9 @@ class GentooServicesTestCase(TestCase, LoaderModuleMockMixin):
mock = MagicMock(return_value=True)
with patch.dict(gentoo_service.__salt__, {'cmd.retcode': mock}):
self.assertFalse(gentoo_service.restart('name'))
mock.assert_called_once_with('/etc/init.d/name restart', python_shell=False)
mock.assert_called_once_with('/etc/init.d/name restart',
ignore_retcode=False,
python_shell=False)
def test_reload_(self):
'''
@ -178,7 +184,9 @@ class GentooServicesTestCase(TestCase, LoaderModuleMockMixin):
mock = MagicMock(return_value=True)
with patch.dict(gentoo_service.__salt__, {'cmd.retcode': mock}):
self.assertFalse(gentoo_service.reload_('name'))
mock.assert_called_once_with('/etc/init.d/name reload', python_shell=False)
mock.assert_called_once_with('/etc/init.d/name reload',
ignore_retcode=False,
python_shell=False)
def test_zap(self):
'''
@ -187,7 +195,9 @@ class GentooServicesTestCase(TestCase, LoaderModuleMockMixin):
mock = MagicMock(return_value=True)
with patch.dict(gentoo_service.__salt__, {'cmd.retcode': mock}):
self.assertFalse(gentoo_service.zap('name'))
mock.assert_called_once_with('/etc/init.d/name zap', python_shell=False)
mock.assert_called_once_with('/etc/init.d/name zap',
ignore_retcode=False,
python_shell=False)
def test_status(self):
'''
@ -201,25 +211,33 @@ class GentooServicesTestCase(TestCase, LoaderModuleMockMixin):
mock = MagicMock(return_value=0)
with patch.dict(gentoo_service.__salt__, {'cmd.retcode': mock}):
self.assertTrue(gentoo_service.status('name'))
mock.assert_called_once_with('/etc/init.d/name status', python_shell=False)
mock.assert_called_once_with('/etc/init.d/name status',
ignore_retcode=True,
python_shell=False)
# service is not running
mock = MagicMock(return_value=1)
with patch.dict(gentoo_service.__salt__, {'cmd.retcode': mock}):
self.assertFalse(gentoo_service.status('name'))
mock.assert_called_once_with('/etc/init.d/name status', python_shell=False)
mock.assert_called_once_with('/etc/init.d/name status',
ignore_retcode=True,
python_shell=False)
# service is stopped
mock = MagicMock(return_value=3)
with patch.dict(gentoo_service.__salt__, {'cmd.retcode': mock}):
self.assertFalse(gentoo_service.status('name'))
mock.assert_called_once_with('/etc/init.d/name status', python_shell=False)
mock.assert_called_once_with('/etc/init.d/name status',
ignore_retcode=True,
python_shell=False)
# service has crashed
mock = MagicMock(return_value=32)
with patch.dict(gentoo_service.__salt__, {'cmd.retcode': mock}):
self.assertFalse(gentoo_service.status('name'))
mock.assert_called_once_with('/etc/init.d/name status', python_shell=False)
mock.assert_called_once_with('/etc/init.d/name status',
ignore_retcode=True,
python_shell=False)
def test_enable(self):
'''
@ -228,7 +246,9 @@ class GentooServicesTestCase(TestCase, LoaderModuleMockMixin):
rc_update_mock = MagicMock(return_value=0)
with patch.dict(gentoo_service.__salt__, {'cmd.retcode': rc_update_mock}):
self.assertTrue(gentoo_service.enable('name'))
rc_update_mock.assert_called_once_with('rc-update add name', python_shell=False)
rc_update_mock.assert_called_once_with('rc-update add name',
ignore_retcode=False,
python_shell=False)
rc_update_mock.reset_mock()
# move service from 'l1' to 'l2' runlevel
@ -238,8 +258,12 @@ class GentooServicesTestCase(TestCase, LoaderModuleMockMixin):
with patch.dict(gentoo_service.__salt__, {'cmd.run': level_list_mock}):
with patch.dict(gentoo_service.__salt__, {'cmd.retcode': rc_update_mock}):
self.assertTrue(gentoo_service.enable('name', runlevels='l2'))
rc_update_mock.assert_has_calls([call('rc-update delete name l1', python_shell=False),
call('rc-update add name l2', python_shell=False)])
rc_update_mock.assert_has_calls([call('rc-update delete name l1',
ignore_retcode=False,
python_shell=False),
call('rc-update add name l2',
ignore_retcode=False,
python_shell=False)])
rc_update_mock.reset_mock()
# requested levels are the same as the current ones
@ -260,7 +284,9 @@ class GentooServicesTestCase(TestCase, LoaderModuleMockMixin):
with patch.dict(gentoo_service.__salt__, {'cmd.run': level_list_mock}):
with patch.dict(gentoo_service.__salt__, {'cmd.retcode': rc_update_mock}):
self.assertTrue(gentoo_service.enable('name', runlevels=['l2', 'l1']))
rc_update_mock.assert_called_once_with('rc-update add name l2', python_shell=False)
rc_update_mock.assert_called_once_with('rc-update add name l2',
ignore_retcode=False,
python_shell=False)
rc_update_mock.reset_mock()
# remove service from 'l1' runlevel
@ -269,15 +295,21 @@ class GentooServicesTestCase(TestCase, LoaderModuleMockMixin):
with patch.dict(gentoo_service.__salt__, {'cmd.run': level_list_mock}):
with patch.dict(gentoo_service.__salt__, {'cmd.retcode': rc_update_mock}):
self.assertTrue(gentoo_service.enable('name', runlevels=['l2']))
rc_update_mock.assert_called_once_with('rc-update delete name l1', python_shell=False)
rc_update_mock.assert_called_once_with('rc-update delete name l1',
ignore_retcode=False,
python_shell=False)
rc_update_mock.reset_mock()
# move service from 'l2' add to 'l3', leaving at l1
with patch.dict(gentoo_service.__salt__, {'cmd.run': level_list_mock}):
with patch.dict(gentoo_service.__salt__, {'cmd.retcode': rc_update_mock}):
self.assertTrue(gentoo_service.enable('name', runlevels=['l1', 'l3']))
rc_update_mock.assert_has_calls([call('rc-update delete name l2', python_shell=False),
call('rc-update add name l3', python_shell=False)])
rc_update_mock.assert_has_calls([call('rc-update delete name l2',
ignore_retcode=False,
python_shell=False),
call('rc-update add name l3',
ignore_retcode=False,
python_shell=False)])
rc_update_mock.reset_mock()
# remove from l1, l3, and add to l2, l4, and leave at l5
@ -286,15 +318,21 @@ class GentooServicesTestCase(TestCase, LoaderModuleMockMixin):
with patch.dict(gentoo_service.__salt__, {'cmd.run': level_list_mock}):
with patch.dict(gentoo_service.__salt__, {'cmd.retcode': rc_update_mock}):
self.assertTrue(gentoo_service.enable('name', runlevels=['l2', 'l4', 'l5']))
rc_update_mock.assert_has_calls([call('rc-update delete name l1 l3', python_shell=False),
call('rc-update add name l2 l4', python_shell=False)])
rc_update_mock.assert_has_calls([call('rc-update delete name l1 l3',
ignore_retcode=False,
python_shell=False),
call('rc-update add name l2 l4',
ignore_retcode=False,
python_shell=False)])
rc_update_mock.reset_mock()
# rc-update failed
rc_update_mock = MagicMock(return_value=1)
with patch.dict(gentoo_service.__salt__, {'cmd.retcode': rc_update_mock}):
self.assertFalse(gentoo_service.enable('name'))
rc_update_mock.assert_called_once_with('rc-update add name', python_shell=False)
rc_update_mock.assert_called_once_with('rc-update add name',
ignore_retcode=False,
python_shell=False)
rc_update_mock.reset_mock()
# move service delete failed
@ -303,7 +341,9 @@ class GentooServicesTestCase(TestCase, LoaderModuleMockMixin):
with patch.dict(gentoo_service.__salt__, {'cmd.run': level_list_mock}):
with patch.dict(gentoo_service.__salt__, {'cmd.retcode': rc_update_mock}):
self.assertFalse(gentoo_service.enable('name', runlevels='l2'))
rc_update_mock.assert_called_once_with('rc-update delete name l1', python_shell=False)
rc_update_mock.assert_called_once_with('rc-update delete name l1',
ignore_retcode=False,
python_shell=False)
rc_update_mock.reset_mock()
# move service delete succeeds. add fails
@ -312,8 +352,12 @@ class GentooServicesTestCase(TestCase, LoaderModuleMockMixin):
with patch.dict(gentoo_service.__salt__, {'cmd.run': level_list_mock}):
with patch.dict(gentoo_service.__salt__, {'cmd.retcode': rc_update_mock}):
self.assertFalse(gentoo_service.enable('name', runlevels='l2'))
rc_update_mock.assert_has_calls([call('rc-update delete name l1', python_shell=False),
call('rc-update add name l2', python_shell=False)])
rc_update_mock.assert_has_calls([call('rc-update delete name l1',
ignore_retcode=False,
python_shell=False),
call('rc-update add name l2',
ignore_retcode=False,
python_shell=False)])
rc_update_mock.reset_mock()
def test_disable(self):
@ -323,7 +367,9 @@ class GentooServicesTestCase(TestCase, LoaderModuleMockMixin):
rc_update_mock = MagicMock(return_value=0)
with patch.dict(gentoo_service.__salt__, {'cmd.retcode': rc_update_mock}):
self.assertTrue(gentoo_service.disable('name'))
rc_update_mock.assert_called_once_with('rc-update delete name', python_shell=False)
rc_update_mock.assert_called_once_with('rc-update delete name',
ignore_retcode=False,
python_shell=False)
rc_update_mock.reset_mock()
# disable service
@ -334,6 +380,7 @@ class GentooServicesTestCase(TestCase, LoaderModuleMockMixin):
with patch.dict(gentoo_service.__salt__, {'cmd.retcode': rc_update_mock}):
self.assertTrue(gentoo_service.disable('name', runlevels='l1'))
rc_update_mock.assert_called_once_with('rc-update delete name l1',
ignore_retcode=False,
python_shell=False)
rc_update_mock.reset_mock()
@ -344,6 +391,7 @@ class GentooServicesTestCase(TestCase, LoaderModuleMockMixin):
with patch.dict(gentoo_service.__salt__, {'cmd.retcode': rc_update_mock}):
self.assertTrue(gentoo_service.disable('name', runlevels=['l1']))
rc_update_mock.assert_called_once_with('rc-update delete name l1',
ignore_retcode=False,
python_shell=False)
rc_update_mock.reset_mock()
@ -354,6 +402,7 @@ class GentooServicesTestCase(TestCase, LoaderModuleMockMixin):
with patch.dict(gentoo_service.__salt__, {'cmd.retcode': rc_update_mock}):
self.assertTrue(gentoo_service.disable('name', runlevels=['l1']))
rc_update_mock.assert_called_once_with('rc-update delete name l1',
ignore_retcode=False,
python_shell=False)
rc_update_mock.reset_mock()
@ -373,6 +422,7 @@ class GentooServicesTestCase(TestCase, LoaderModuleMockMixin):
with patch.dict(gentoo_service.__salt__, {'cmd.retcode': rc_update_mock}):
self.assertTrue(gentoo_service.disable('name', runlevels=['l1', 'l3']))
rc_update_mock.assert_called_once_with('rc-update delete name l1 l3',
ignore_retcode=False,
python_shell=False)
rc_update_mock.reset_mock()
@ -380,7 +430,9 @@ class GentooServicesTestCase(TestCase, LoaderModuleMockMixin):
rc_update_mock = MagicMock(return_value=1)
with patch.dict(gentoo_service.__salt__, {'cmd.retcode': rc_update_mock}):
self.assertFalse(gentoo_service.disable('name'))
rc_update_mock.assert_called_once_with('rc-update delete name', python_shell=False)
rc_update_mock.assert_called_once_with('rc-update delete name',
ignore_retcode=False,
python_shell=False)
rc_update_mock.reset_mock()
# move service delete failed
@ -389,7 +441,9 @@ class GentooServicesTestCase(TestCase, LoaderModuleMockMixin):
with patch.dict(gentoo_service.__salt__, {'cmd.run': level_list_mock}):
with patch.dict(gentoo_service.__salt__, {'cmd.retcode': rc_update_mock}):
self.assertFalse(gentoo_service.disable('name', runlevels='l1'))
rc_update_mock.assert_called_once_with('rc-update delete name l1', python_shell=False)
rc_update_mock.assert_called_once_with('rc-update delete name l1',
ignore_retcode=False,
python_shell=False)
rc_update_mock.reset_mock()
# move service delete succeeds. add fails
@ -399,6 +453,7 @@ class GentooServicesTestCase(TestCase, LoaderModuleMockMixin):
with patch.dict(gentoo_service.__salt__, {'cmd.retcode': rc_update_mock}):
self.assertFalse(gentoo_service.disable('name', runlevels=['l1', 'l3']))
rc_update_mock.assert_called_once_with('rc-update delete name l1 l3',
ignore_retcode=False,
python_shell=False)
rc_update_mock.reset_mock()

View file

@ -203,3 +203,25 @@ class ArchiveTestCase(TestCase, LoaderModuleMockMixin):
enforce_toplevel=False,
keep=True)
self.assertEqual(ret['changes']['extracted_files'], 'stderr')
def test_extracted_when_if_missing_path_exists(self):
'''
When if_missing exists, we should exit without making any changes.
NOTE: We're not mocking the __salt__ dunder because if we actually run
any functions from that dunder, we're doing something wrong. So, in
those cases we'll just let it raise a KeyError and cause the test to
fail.
'''
name = if_missing = '/tmp/foo'
source = 'salt://foo.bar.tar'
with patch.object(os.path, 'exists', MagicMock(return_value=True)):
ret = archive.extracted(
name,
source=source,
if_missing=if_missing)
self.assertTrue(ret['result'], ret)
self.assertEqual(
ret['comment'],
'Path {0} exists'.format(if_missing)
)