mirror of
https://github.com/saltstack/salt.git
synced 2025-04-17 10:10:20 +00:00
Merge branch '2017.7' into '2018.3'
Conflicts: - salt/modules/slsutil.py - salt/modules/win_timezone.py - salt/states/pkg.py - tests/integration/states/test_pkg.py
This commit is contained in:
commit
15bfba7143
11 changed files with 245 additions and 135 deletions
|
@ -393,20 +393,14 @@ def __within(within=None, errmsg=None, dtype=None):
|
|||
|
||||
def __space_delimited_list(value):
|
||||
'''validate that a value contains one or more space-delimited values'''
|
||||
valid, _value, errmsg = False, value, 'space-delimited string'
|
||||
try:
|
||||
if hasattr(value, '__iter__'):
|
||||
valid = True # TODO:
|
||||
else:
|
||||
_value = value.split()
|
||||
if _value == []:
|
||||
raise ValueError
|
||||
valid = True
|
||||
except AttributeError:
|
||||
pass
|
||||
except ValueError:
|
||||
pass
|
||||
return (valid, _value, errmsg)
|
||||
if isinstance(value, str):
|
||||
value = value.strip().split()
|
||||
|
||||
if hasattr(value, '__iter__') and value != []:
|
||||
return (True, value, 'space-delimited string')
|
||||
else:
|
||||
return (False, value, '{0} is not a valid space-delimited value.\n'.format(value))
|
||||
|
||||
|
||||
SALT_ATTR_TO_DEBIAN_ATTR_MAP = {
|
||||
'dns': 'dns-nameservers',
|
||||
|
|
|
@ -98,20 +98,16 @@ def _space_delimited_list(value):
|
|||
'''
|
||||
validate that a value contains one or more space-delimited values
|
||||
'''
|
||||
valid, _value, errmsg = False, value, 'space-delimited string'
|
||||
try:
|
||||
if hasattr(value, '__iter__'):
|
||||
valid = True
|
||||
else:
|
||||
_value = value.split()
|
||||
if _value == []:
|
||||
raise ValueError
|
||||
valid = True
|
||||
except AttributeError:
|
||||
errmsg = '{0} is not a valid list.\n'.format(value)
|
||||
except ValueError:
|
||||
errmsg = '{0} is not a valid list.\n'.format(value)
|
||||
return (valid, errmsg)
|
||||
if isinstance(value, str):
|
||||
items = value.split(' ')
|
||||
valid = items and all(items)
|
||||
else:
|
||||
valid = hasattr(value, '__iter__') and (value != [])
|
||||
|
||||
if valid:
|
||||
return (True, 'space-delimited string')
|
||||
else:
|
||||
return (False, '{0} is not a valid list.\n'.format(value))
|
||||
|
||||
|
||||
def _validate_ipv4(value):
|
||||
|
|
|
@ -137,13 +137,15 @@ def renderer(path=None, string=None, default_renderer='jinja|yaml', **kwargs):
|
|||
path_or_string = ':string:'
|
||||
kwargs['input_data'] = string
|
||||
|
||||
return salt.template.compile_template(
|
||||
path_or_string,
|
||||
renderers,
|
||||
default_renderer,
|
||||
__opts__['renderer_blacklist'],
|
||||
__opts__['renderer_whitelist'],
|
||||
**kwargs)
|
||||
ret = salt.template.compile_template(
|
||||
path_or_string,
|
||||
renderers,
|
||||
default_renderer,
|
||||
__opts__['renderer_blacklist'],
|
||||
__opts__['renderer_whitelist'],
|
||||
**kwargs
|
||||
)
|
||||
return ret.read() if __utils__['stringio.is_readable'](ret) else ret
|
||||
|
||||
|
||||
def _get_serialize_fn(serializer, fn_name):
|
||||
|
|
|
@ -3852,6 +3852,8 @@ def retention_schedule(name, retain, strptime_format=None, timezone=None):
|
|||
return (None, None)
|
||||
|
||||
def get_file_time_from_mtime(f):
|
||||
if f == '.' or f == '..':
|
||||
return (None, None)
|
||||
lstat = __salt__['file.lstat'](os.path.join(name, f))
|
||||
if lstat:
|
||||
mtime = lstat['st_mtime']
|
||||
|
|
|
@ -184,16 +184,31 @@ def _check_pkg_version_format(pkg):
|
|||
|
||||
def _check_if_installed(prefix, state_pkg_name, version_spec, ignore_installed,
|
||||
force_reinstall, upgrade, user, cwd, bin_env, env_vars,
|
||||
**kwargs):
|
||||
# result: None means the command failed to run
|
||||
# result: True means the package is installed
|
||||
# result: False means the package is not installed
|
||||
pip_list=False, **kwargs):
|
||||
'''
|
||||
Takes a package name and version specification (if any) and checks it is
|
||||
installed
|
||||
|
||||
Keyword arguments include:
|
||||
pip_list: optional dict of installed pip packages, and their versions,
|
||||
to search through to check if the package is installed. If not
|
||||
provided, one will be generated in this function by querying the
|
||||
system.
|
||||
|
||||
Returns:
|
||||
result: None means the command failed to run
|
||||
result: True means the package is installed
|
||||
result: False means the package is not 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.
|
||||
pip_list = __salt__['pip.list'](prefix, bin_env=bin_env,
|
||||
user=user, cwd=cwd,
|
||||
env_vars=env_vars, **kwargs)
|
||||
prefix_realname = _find_key(prefix, pip_list)
|
||||
|
||||
# If the package was already installed, check
|
||||
|
@ -715,6 +730,14 @@ def installed(name,
|
|||
# No requirements case.
|
||||
# Check pre-existence of the requested packages.
|
||||
else:
|
||||
# Attempt to pre-cache a the current pip list
|
||||
try:
|
||||
pip_list = __salt__['pip.list'](bin_env=bin_env, user=user, cwd=cwd)
|
||||
# If we fail, then just send False, and we'll try again in the next function call
|
||||
except Exception as exc:
|
||||
logger.exception(exc)
|
||||
pip_list = False
|
||||
|
||||
for prefix, state_pkg_name, version_spec in pkgs_details:
|
||||
|
||||
if prefix:
|
||||
|
@ -723,7 +746,7 @@ def installed(name,
|
|||
out = _check_if_installed(prefix, state_pkg_name, version_spec,
|
||||
ignore_installed, force_reinstall,
|
||||
upgrade, user, cwd, bin_env, env_vars,
|
||||
**kwargs)
|
||||
pip_list, **kwargs)
|
||||
# If _check_if_installed result is None, something went wrong with
|
||||
# the command running. This way we keep stateful output.
|
||||
if out['result'] is None:
|
||||
|
|
|
@ -995,8 +995,8 @@ def installed(
|
|||
|
||||
In version 2015.8.9, an **ignore_epoch** argument has been added to
|
||||
:py:mod:`pkg.installed <salt.states.pkg.installed>`,
|
||||
:py:mod:`pkg.removed <salt.states.pkg.installed>`, and
|
||||
:py:mod:`pkg.purged <salt.states.pkg.installed>` states, which
|
||||
:py:mod:`pkg.removed <salt.states.pkg.removed>`, and
|
||||
:py:mod:`pkg.purged <salt.states.pkg.purged>` states, which
|
||||
causes the epoch to be disregarded when the state checks to see if
|
||||
the desired version was installed.
|
||||
|
||||
|
@ -1556,54 +1556,46 @@ def installed(
|
|||
# _find_install_targets() found no targets or encountered an error
|
||||
|
||||
# check that the hold function is available
|
||||
if 'pkg.hold' in __salt__:
|
||||
if 'hold' in kwargs:
|
||||
try:
|
||||
if kwargs['hold']:
|
||||
hold_ret = __salt__['pkg.hold'](
|
||||
name=name, pkgs=pkgs, sources=sources
|
||||
)
|
||||
else:
|
||||
hold_ret = __salt__['pkg.unhold'](
|
||||
name=name, pkgs=pkgs, sources=sources
|
||||
)
|
||||
except (CommandExecutionError, SaltInvocationError) as exc:
|
||||
return {'name': name,
|
||||
'changes': {},
|
||||
'result': False,
|
||||
'comment': six.text_type(exc)}
|
||||
if 'pkg.hold' in __salt__ and 'hold' in kwargs:
|
||||
try:
|
||||
action = 'pkg.hold' if kwargs['hold'] else 'pkg.unhold'
|
||||
hold_ret = __salt__[action](
|
||||
name=name, pkgs=pkgs, sources=sources
|
||||
)
|
||||
except (CommandExecutionError, SaltInvocationError) as exc:
|
||||
return {'name': name,
|
||||
'changes': {},
|
||||
'result': False,
|
||||
'comment': six.text_type(exc)}
|
||||
|
||||
if 'result' in hold_ret and not hold_ret['result']:
|
||||
return {'name': name,
|
||||
'changes': {},
|
||||
'result': False,
|
||||
'comment': 'An error was encountered while '
|
||||
'holding/unholding package(s): {0}'
|
||||
.format(hold_ret['comment'])}
|
||||
else:
|
||||
modified_hold = [hold_ret[x] for x in hold_ret
|
||||
if hold_ret[x]['changes']]
|
||||
not_modified_hold = [hold_ret[x] for x in hold_ret
|
||||
if not hold_ret[x]['changes']
|
||||
and hold_ret[x]['result']]
|
||||
failed_hold = [hold_ret[x] for x in hold_ret
|
||||
if not hold_ret[x]['result']]
|
||||
if 'result' in hold_ret and not hold_ret['result']:
|
||||
return {'name': name,
|
||||
'changes': {},
|
||||
'result': False,
|
||||
'comment': 'An error was encountered while '
|
||||
'holding/unholding package(s): {0}'
|
||||
.format(hold_ret['comment'])}
|
||||
else:
|
||||
modified_hold = [hold_ret[x] for x in hold_ret
|
||||
if hold_ret[x]['changes']]
|
||||
not_modified_hold = [hold_ret[x] for x in hold_ret
|
||||
if not hold_ret[x]['changes']
|
||||
and hold_ret[x]['result']]
|
||||
failed_hold = [hold_ret[x] for x in hold_ret
|
||||
if not hold_ret[x]['result']]
|
||||
|
||||
if modified_hold:
|
||||
for i in modified_hold:
|
||||
result['comment'] += '.\n{0}'.format(i['comment'])
|
||||
result['result'] = i['result']
|
||||
result['changes'][i['name']] = i['changes']
|
||||
for i in modified_hold:
|
||||
result['comment'] += '.\n{0}'.format(i['comment'])
|
||||
result['result'] = i['result']
|
||||
result['changes'][i['name']] = i['changes']
|
||||
|
||||
if not_modified_hold:
|
||||
for i in not_modified_hold:
|
||||
result['comment'] += '.\n{0}'.format(i['comment'])
|
||||
result['result'] = i['result']
|
||||
for i in not_modified_hold:
|
||||
result['comment'] += '.\n{0}'.format(i['comment'])
|
||||
result['result'] = i['result']
|
||||
|
||||
if failed_hold:
|
||||
for i in failed_hold:
|
||||
result['comment'] += '.\n{0}'.format(i['comment'])
|
||||
result['result'] = i['result']
|
||||
for i in failed_hold:
|
||||
result['comment'] += '.\n{0}'.format(i['comment'])
|
||||
result['result'] = i['result']
|
||||
return result
|
||||
|
||||
if to_unpurge and 'lowpkg.unpurge' not in __salt__:
|
||||
|
@ -1724,45 +1716,40 @@ def installed(
|
|||
# checks reinstall targets works.
|
||||
pkg_ret = {}
|
||||
|
||||
if 'pkg.hold' in __salt__:
|
||||
if 'hold' in kwargs:
|
||||
try:
|
||||
if kwargs['hold']:
|
||||
hold_ret = __salt__['pkg.hold'](
|
||||
name=name, pkgs=pkgs, sources=sources
|
||||
)
|
||||
else:
|
||||
hold_ret = __salt__['pkg.unhold'](
|
||||
name=name, pkgs=pkgs, sources=sources
|
||||
)
|
||||
except (CommandExecutionError, SaltInvocationError) as exc:
|
||||
comment.append(six.text_type(exc))
|
||||
ret = {'name': name,
|
||||
'changes': changes,
|
||||
'result': False,
|
||||
'comment': '\n'.join(comment)}
|
||||
if warnings:
|
||||
ret.setdefault('warnings', []).extend(warnings)
|
||||
return ret
|
||||
else:
|
||||
if 'result' in hold_ret and not hold_ret['result']:
|
||||
ret = {'name': name,
|
||||
'changes': {},
|
||||
'result': False,
|
||||
'comment': 'An error was encountered while '
|
||||
'holding/unholding package(s): {0}'
|
||||
.format(hold_ret['comment'])}
|
||||
if warnings:
|
||||
ret.setdefault('warnings', []).extend(warnings)
|
||||
return ret
|
||||
else:
|
||||
modified_hold = [hold_ret[x] for x in hold_ret
|
||||
if hold_ret[x]['changes']]
|
||||
not_modified_hold = [hold_ret[x] for x in hold_ret
|
||||
if not hold_ret[x]['changes']
|
||||
and hold_ret[x]['result']]
|
||||
failed_hold = [hold_ret[x] for x in hold_ret
|
||||
if not hold_ret[x]['result']]
|
||||
if 'pkg.hold' in __salt__ and 'hold' in kwargs:
|
||||
try:
|
||||
action = 'pkg.hold' if kwargs['hold'] else 'pkg.unhold'
|
||||
hold_ret = __salt__[action](
|
||||
name=name, pkgs=desired, sources=sources
|
||||
)
|
||||
except (CommandExecutionError, SaltInvocationError) as exc:
|
||||
comment.append(six.text_type(exc))
|
||||
ret = {'name': name,
|
||||
'changes': changes,
|
||||
'result': False,
|
||||
'comment': '\n'.join(comment)}
|
||||
if warnings:
|
||||
ret.setdefault('warnings', []).extend(warnings)
|
||||
return ret
|
||||
else:
|
||||
if 'result' in hold_ret and not hold_ret['result']:
|
||||
ret = {'name': name,
|
||||
'changes': {},
|
||||
'result': False,
|
||||
'comment': 'An error was encountered while '
|
||||
'holding/unholding package(s): {0}'
|
||||
.format(hold_ret['comment'])}
|
||||
if warnings:
|
||||
ret.setdefault('warnings', []).extend(warnings)
|
||||
return ret
|
||||
else:
|
||||
modified_hold = [hold_ret[x] for x in hold_ret
|
||||
if hold_ret[x]['changes']]
|
||||
not_modified_hold = [hold_ret[x] for x in hold_ret
|
||||
if not hold_ret[x]['changes']
|
||||
and hold_ret[x]['result']]
|
||||
failed_hold = [hold_ret[x] for x in hold_ret
|
||||
if not hold_ret[x]['result']]
|
||||
|
||||
if to_unpurge:
|
||||
changes['purge_desired'] = __salt__['lowpkg.unpurge'](*to_unpurge)
|
||||
|
@ -1844,6 +1831,10 @@ def installed(
|
|||
if len(changes[change_name]['old']) > 0:
|
||||
changes[change_name]['old'] += '\n'
|
||||
changes[change_name]['old'] += '{0}'.format(i['changes']['old'])
|
||||
else:
|
||||
comment.append(i['comment'])
|
||||
changes[change_name] = {}
|
||||
changes[change_name]['new'] = '{0}'.format(i['changes']['new'])
|
||||
|
||||
# Any requested packages that were not targeted for install or reinstall
|
||||
if not_modified:
|
||||
|
@ -2737,8 +2728,8 @@ def removed(name,
|
|||
|
||||
In version 2015.8.9, an **ignore_epoch** argument has been added to
|
||||
:py:mod:`pkg.installed <salt.states.pkg.installed>`,
|
||||
:py:mod:`pkg.removed <salt.states.pkg.installed>`, and
|
||||
:py:mod:`pkg.purged <salt.states.pkg.installed>` states, which
|
||||
:py:mod:`pkg.removed <salt.states.pkg.removed>`, and
|
||||
:py:mod:`pkg.purged <salt.states.pkg.purged>` states, which
|
||||
causes the epoch to be disregarded when the state checks to see if
|
||||
the desired version was installed. If **ignore_epoch** was not set
|
||||
to ``True``, and instead of ``2:7.4.160-1.el7`` a version of
|
||||
|
@ -2843,8 +2834,8 @@ def purged(name,
|
|||
|
||||
In version 2015.8.9, an **ignore_epoch** argument has been added to
|
||||
:py:mod:`pkg.installed <salt.states.pkg.installed>`,
|
||||
:py:mod:`pkg.removed <salt.states.pkg.installed>`, and
|
||||
:py:mod:`pkg.purged <salt.states.pkg.installed>` states, which
|
||||
:py:mod:`pkg.removed <salt.states.pkg.removed>`, and
|
||||
:py:mod:`pkg.purged <salt.states.pkg.purged>` states, which
|
||||
causes the epoch to be disregarded when the state checks to see if
|
||||
the desired version was installed. If **ignore_epoch** was not set
|
||||
to ``True``, and instead of ``2:7.4.160-1.el7`` a version of
|
||||
|
|
|
@ -8,8 +8,19 @@ Linux and Solaris are supported
|
|||
# Import python libs
|
||||
from __future__ import absolute_import, print_function, unicode_literals
|
||||
|
||||
try:
|
||||
import tzlocal # pylint: disable=unused-import
|
||||
HAS_TZLOCAL = True
|
||||
except ImportError:
|
||||
HAS_TZLOCAL = False
|
||||
|
||||
# Import Salt Testing libs
|
||||
from tests.support.case import ModuleCase
|
||||
from tests.support.helpers import destructiveTest
|
||||
from tests.support.unit import skipIf
|
||||
|
||||
# Import salt libs
|
||||
import salt.utils
|
||||
|
||||
|
||||
class TimezoneLinuxModuleTest(ModuleCase):
|
||||
|
@ -42,3 +53,44 @@ class TimezoneSolarisModuleTest(ModuleCase):
|
|||
timescale = ['UTC', 'localtime']
|
||||
ret = self.run_function('timezone.get_hwclock')
|
||||
self.assertIn(ret, timescale)
|
||||
|
||||
|
||||
@skipIf(not salt.utils.is_windows(), 'windows test only')
|
||||
class TimezoneWindowsModuleTest(ModuleCase):
|
||||
def setUp(self):
|
||||
self.pre = self.run_function('timezone.get_zone')
|
||||
|
||||
def tearDown(self):
|
||||
post = self.run_function('timezone.get_zone')
|
||||
if self.pre != post:
|
||||
self.run_function('timezone.set_zone', [self.pre])
|
||||
|
||||
def test_get_hwclock(self):
|
||||
timescale = ['UTC', 'localtime']
|
||||
ret = self.run_function('timezone.get_hwclock')
|
||||
self.assertIn(ret, timescale)
|
||||
|
||||
@destructiveTest
|
||||
def test_get_zone(self):
|
||||
'''
|
||||
test timezone.set_zone, get_zone and zone_compare
|
||||
'''
|
||||
|
||||
zone = 'America/Inuvik' if not HAS_TZLOCAL else 'America/Denver'
|
||||
|
||||
# first set the zone
|
||||
assert self.run_function('timezone.set_zone', [zone])
|
||||
|
||||
# check it set the correct zone
|
||||
ret = self.run_function('timezone.get_zone')
|
||||
assert zone in ret
|
||||
|
||||
# compare zones
|
||||
assert self.run_function('timezone.zone_compare', [zone])
|
||||
|
||||
def test_get_offset(self):
|
||||
'''
|
||||
test timezone.get_offset
|
||||
'''
|
||||
ret = self.run_function('timezone.get_offset')
|
||||
self.assertIn('-', ret)
|
||||
|
|
|
@ -22,9 +22,11 @@ import tests.integration.utils
|
|||
from tests.support.case import ShellCase
|
||||
from tests.support.paths import TMP
|
||||
from tests.support.mixins import ShellCaseCommonTestsMixin
|
||||
from tests.support.unit import skipIf
|
||||
from tests.integration.utils import testprogram
|
||||
|
||||
|
||||
@skipIf(True, 'This test file should be in an isolated test space.')
|
||||
class MasterTest(ShellCase, testprogram.TestProgramCase, ShellCaseCommonTestsMixin):
|
||||
|
||||
_call_binary_ = 'salt-master'
|
||||
|
|
|
@ -591,8 +591,7 @@ class PipStateTest(ModuleCase, SaltReturnAssertsMixin):
|
|||
continue
|
||||
self.assertEqual(
|
||||
ret[key]['comment'],
|
||||
('Python package carbon < 1.3 was already installed\n'
|
||||
'All specified packages are already installed'))
|
||||
('All packages were successfully installed'))
|
||||
break
|
||||
else:
|
||||
raise Exception('Expected state did not run')
|
||||
|
|
|
@ -1051,3 +1051,52 @@ class PkgTest(ModuleCase, SaltReturnAssertsMixin):
|
|||
refresh=False,
|
||||
test=True)
|
||||
self.assertInSaltComment("System update will be performed", ret)
|
||||
|
||||
@requires_salt_modules('pkg.hold', 'pkg.unhold')
|
||||
@requires_system_grains
|
||||
def test_pkg_015_installed_held(self, grains=None): # pylint: disable=unused-argument
|
||||
'''
|
||||
Tests that a package can be held even when the package is already installed.
|
||||
'''
|
||||
os_family = grains.get('os_family', '')
|
||||
|
||||
if os_family.lower() != 'redhat' and os_family.lower() != 'debian':
|
||||
self.skipTest('Test only runs on RedHat or Debian family')
|
||||
|
||||
pkg_targets = _PKG_TARGETS.get(os_family, [])
|
||||
|
||||
# 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(pkg_targets)
|
||||
|
||||
target = pkg_targets[0]
|
||||
|
||||
# First we ensure that the package is installed
|
||||
ret = self.run_state(
|
||||
'pkg.installed',
|
||||
name=target,
|
||||
refresh=False,
|
||||
)
|
||||
self.assertSaltTrueReturn(ret)
|
||||
|
||||
# Then we check that the package is now held
|
||||
ret = self.run_state(
|
||||
'pkg.installed',
|
||||
name=target,
|
||||
hold=True,
|
||||
refresh=False,
|
||||
)
|
||||
|
||||
try:
|
||||
tag = 'pkg_|-{0}_|-{0}_|-installed'.format(target)
|
||||
self.assertSaltTrueReturn(ret)
|
||||
self.assertIn(tag, ret)
|
||||
self.assertIn('changes', ret[tag])
|
||||
self.assertIn(target, ret[tag]['changes'])
|
||||
self.assertEqual(ret[tag]['changes'][target], {'new': 'hold', 'old': 'install'})
|
||||
finally:
|
||||
# Clean up, unhold package and remove
|
||||
self.run_function('pkg.unhold', name=target)
|
||||
ret = self.run_state('pkg.removed', name=target)
|
||||
self.assertSaltTrueReturn(ret)
|
||||
|
|
|
@ -1113,7 +1113,7 @@ def requires_salt_modules(*names):
|
|||
)
|
||||
|
||||
for name in names:
|
||||
if name not in cls.run_function('sys.doc'):
|
||||
if name not in cls.run_function('sys.doc', [name]):
|
||||
cls.skipTest(
|
||||
'Salt module {0!r} is not available'.format(name)
|
||||
)
|
||||
|
|
Loading…
Add table
Reference in a new issue