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:
rallytime 2018-07-31 18:18:58 -04:00
commit 15bfba7143
No known key found for this signature in database
GPG key ID: E8F1A4B90D0DEA19
11 changed files with 245 additions and 135 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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