Merge pull request #47783 from rallytime/merge-2018.3

[2018.3] Merge forward from 2018.3.1 to 2018.3
This commit is contained in:
Nicole Thomas 2018-05-23 10:51:55 -04:00 committed by GitHub
commit 4a6ca67883
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 201 additions and 144 deletions

View File

@ -538,6 +538,10 @@
# targeted with the normal -N argument to salt-ssh.
#ssh_list_nodegroups: {}
# salt-ssh has the ability to update the flat roster file if a minion is not
# found in the roster. Set this to True to enable it.
#ssh_update_roster: False
##### Master Module Management #####
##########################################
# Manage how master side modules are loaded.

View File

@ -13,3 +13,13 @@ used as part of a salt-minion process running on the master. This will allow
the minion to have pillars assigned to it, and will still allow the engine to
create a LocalClient connection to the master ipc sockets to control
environments.
Changes to Automatically Updating the Roster File
-------------------------------------------------
In ``2018.3.0`` salt-ssh was configured to automatically update the flat roster
file if a minion was not found for salt-ssh. This was decided to be
undesireable as a default. The ``--skip-roster`` flag has been removed and
replaced with ``--update-roster``, which will enable salt-ssh to add minions
to the flat roster file. This behavior can also be enabled by setting
``ssh_update_roster: True`` in the master config file.

View File

@ -5,6 +5,6 @@ msgpack>=0.5,!=0.5.5
PyYAML
MarkupSafe
requests>=1.0.0
tornado>=4.2.1,<5.0
tornado>=4.2.1,<6.0
# Required by Tornado to handle threads stuff.
futures>=2.0

View File

@ -410,7 +410,7 @@ class SSH(object):
'host': hostname,
'user': user,
}
if not self.opts.get('ssh_skip_roster'):
if self.opts.get('ssh_update_roster'):
self._update_roster()
def get_pubkey(self):

View File

@ -1396,14 +1396,12 @@ class RemoteClient(Client):
'''
Return the metadata derived from the master_tops system
'''
salt.utils.versions.warn_until(
'Magnesium',
'The _ext_nodes master function has '
'been renamed to _master_tops. To ensure '
'compatibility when using older Salt masters '
'we continue to pass the function as _ext_nodes.'
log.debug(
'The _ext_nodes master function has been renamed to _master_tops. '
'To ensure compatibility when using older Salt masters we will '
'continue to invoke the function as _ext_nodes until the '
'Magnesium release.'
)
# TODO: Change back to _master_tops
# for Magnesium release
load = {'cmd': '_ext_nodes',

View File

@ -850,7 +850,8 @@ class FSChan(object):
self.opts['__fs_update'] = True
else:
self.fs.update()
self.cmd_stub = {'master_tops': {}}
self.cmd_stub = {'master_tops': {},
'ext_nodes': {}}
def send(self, load, tries=None, timeout=None, raw=False): # pylint: disable=unused-argument
'''

View File

@ -253,7 +253,7 @@ def file_hash(load, fnd):
except OSError:
pass
return file_hash(load, fnd)
if os.path.getmtime(path) == mtime:
if str(os.path.getmtime(path)) == mtime:
# check if mtime changed
ret['hsum'] = hsum
return ret

View File

@ -35,7 +35,7 @@ In case both are provided the `file` entry is preferred.
.. warning::
Configuration options will change in Flourine. All options above will be replaced by:
Configuration options will change in Fluorine. All options above will be replaced by:
- kubernetes.kubeconfig or kubernetes.kubeconfig-data
- kubernetes.context

View File

@ -14,7 +14,6 @@ import salt.utils.files
import salt.utils.path
import salt.utils.platform
import salt.utils.stringutils
import salt.utils.mac_utils
from salt.exceptions import CommandExecutionError
from salt.utils.versions import LooseVersion as _LooseVersion
@ -62,7 +61,7 @@ def _get_service(name):
:return: The service information for the service, otherwise an Error
:rtype: dict
'''
services = salt.utils.mac_utils.available_services()
services = __utils__['mac_utils.available_services']()
name = name.lower()
if name in services:
@ -127,7 +126,7 @@ def launchctl(sub_cmd, *args, **kwargs):
salt '*' service.launchctl debug org.cups.cupsd
'''
return salt.utils.mac_utils.launchctl(sub_cmd, *args, **kwargs)
return __utils__['mac_utils.launchctl'](sub_cmd, *args, **kwargs)
def list_(name=None, runas=None):
@ -158,13 +157,11 @@ def list_(name=None, runas=None):
return launchctl('list',
label,
return_stdout=True,
output_loglevel='trace',
runas=runas)
# Collect information on all services: will raise an error if it fails
return launchctl('list',
return_stdout=True,
output_loglevel='trace',
runas=runas)
@ -454,7 +451,7 @@ def get_all(runas=None):
enabled = get_enabled(runas=runas)
# Get list of all services
available = list(salt.utils.mac_utils.available_services().keys())
available = list(__utils__['mac_utils.available_services']().keys())
# Return composite list
return sorted(set(enabled + available))

View File

@ -627,7 +627,7 @@ def install(pkgs=None, # pylint: disable=R0912,R0913,R0914
'''
if 'no_chown' in kwargs:
salt.utils.versions.warn_until(
'Flourine',
'Fluorine',
'The no_chown argument has been deprecated and is no longer used. '
'Its functionality was removed in Boron.')
kwargs.pop('no_chown')

View File

@ -6,8 +6,10 @@ for managing outputters.
# Import Python libs
from __future__ import absolute_import, print_function, unicode_literals
import errno
import logging
import io
import os
import re
import sys
@ -168,7 +170,7 @@ def get_printout(out, opts=None, **kwargs):
'''
try:
fileno = sys.stdout.fileno()
except AttributeError:
except (AttributeError, io.UnsupportedOperation):
fileno = -1 # sys.stdout is StringIO or fake
return not os.isatty(fileno)

View File

@ -8,7 +8,7 @@ salt.modules.kubernetes for more information.
.. warning::
Configuration options will change in Flourine.
Configuration options will change in Fluorine.
The kubernetes module is used to manage different kubernetes resources.

View File

@ -586,7 +586,7 @@ def installed(name,
'''
if 'no_chown' in kwargs:
salt.utils.versions.warn_until(
'Flourine',
'Fluorine',
'The no_chown argument has been deprecated and is no longer used. '
'Its functionality was removed in Boron.')
kwargs.pop('no_chown')

View File

@ -2512,13 +2512,21 @@ def latest(
'result': None,
'comment': '\n'.join(comments)}
# Build updated list of pkgs to exclude non-targeted ones
targeted_pkgs = list(targets.keys()) if pkgs else None
if salt.utils.platform.is_windows():
# pkg.install execution module on windows ensures the software
# package is installed when no version is specified, it does not
# upgrade the software to the latest. This is per the design.
# Build updated list of pkgs *with verion number*, exclude
# non-targeted ones
targeted_pkgs = [{x: targets[x]} for x in targets]
else:
# Build updated list of pkgs to exclude non-targeted ones
targeted_pkgs = list(targets)
# No need to refresh, if a refresh was necessary it would have been
# performed above when pkg.latest_version was run.
try:
# No need to refresh, if a refresh was necessary it would have been
# performed above when pkg.latest_version was run.
changes = __salt__['pkg.install'](name,
changes = __salt__['pkg.install'](name=None,
refresh=False,
fromrepo=fromrepo,
skip_verify=skip_verify,

View File

@ -137,7 +137,7 @@ def managed(name,
'''
if 'no_chown' in kwargs:
salt.utils.versions.warn_until(
'Flourine',
'Fluorine',
'The no_chown argument has been deprecated and is no longer used. '
'Its functionality was removed in Boron.')
kwargs.pop('no_chown')

View File

@ -115,10 +115,10 @@ def installed(name,
'''
if 'force' in kwargs:
salt.utils.versions.warn_until(
'Flourine',
'Fluorine',
'Parameter \'force\' has been detected in the argument list. This'
'parameter is no longer used and has been replaced by \'recurse\''
'as of Salt 2018.3.0. This warning will be removed in Salt Flourine.'
'as of Salt 2018.3.0. This warning will be removed in Salt Fluorine.'
)
kwargs.pop('force')

View File

@ -603,23 +603,22 @@ class TCPReqServerChannel(salt.transport.mixins.auth.AESReqServerMixin, salt.tra
self.payload_handler = payload_handler
self.io_loop = io_loop
self.serial = salt.payload.Serial(self.opts)
if USE_LOAD_BALANCER:
self.req_server = LoadBalancerWorker(self.socket_queue,
self.handle_message,
io_loop=self.io_loop,
ssl_options=self.opts.get('ssl'))
else:
if salt.utils.platform.is_windows():
self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
_set_tcp_keepalive(self._socket, self.opts)
self._socket.setblocking(0)
self._socket.bind((self.opts['interface'], int(self.opts['ret_port'])))
self.req_server = SaltMessageServer(self.handle_message,
io_loop=self.io_loop,
ssl_options=self.opts.get('ssl'))
self.req_server.add_socket(self._socket)
self._socket.listen(self.backlog)
with salt.utils.async.current_ioloop(self.io_loop):
if USE_LOAD_BALANCER:
self.req_server = LoadBalancerWorker(self.socket_queue,
self.handle_message,
ssl_options=self.opts.get('ssl'))
else:
if salt.utils.platform.is_windows():
self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
_set_tcp_keepalive(self._socket, self.opts)
self._socket.setblocking(0)
self._socket.bind((self.opts['interface'], int(self.opts['ret_port'])))
self.req_server = SaltMessageServer(self.handle_message,
ssl_options=self.opts.get('ssl'))
self.req_server.add_socket(self._socket)
self._socket.listen(self.backlog)
salt.transport.mixins.auth.AESReqServerMixin.post_fork(self, payload_handler, io_loop)
@tornado.gen.coroutine
@ -704,6 +703,7 @@ class SaltMessageServer(tornado.tcpserver.TCPServer, object):
'''
def __init__(self, message_handler, *args, **kwargs):
super(SaltMessageServer, self).__init__(*args, **kwargs)
self.io_loop = tornado.ioloop.IOLoop.current()
self.clients = []
self.message_handler = message_handler
@ -807,7 +807,9 @@ class TCPClientKeepAlive(tornado.tcpclient.TCPClient):
stream = tornado.iostream.IOStream(
sock,
max_buffer_size=max_buffer_size)
return stream.connect(addr)
if tornado.version_info < (5,):
return stream.connect(addr)
return stream, stream.connect(addr)
class SaltMessageClientPool(salt.transport.MessageClientPool):
@ -891,33 +893,33 @@ class SaltMessageClient(object):
return
self._closing = True
if hasattr(self, '_stream') and not self._stream.closed():
self._stream.close()
if self._read_until_future is not None:
# This will prevent this message from showing up:
# '[ERROR ] Future exception was never retrieved:
# StreamClosedError'
# This happens because the logic is always waiting to read
# the next message and the associated read future is marked
# 'StreamClosedError' when the stream is closed.
self._read_until_future.exception()
if (not self._stream_return_future.done() and
self.io_loop != tornado.ioloop.IOLoop.current(
instance=False)):
# If _stream_return() hasn't completed, it means the IO
# Loop is stopped (such as when using
# 'salt.utils.async.SyncWrapper'). Ensure that
# _stream_return() completes by restarting the IO Loop.
# This will prevent potential errors on shutdown.
orig_loop = tornado.ioloop.IOLoop.current()
self.io_loop.make_current()
try:
# If _stream_return() hasn't completed, it means the IO
# Loop is stopped (such as when using
# 'salt.utils.async.SyncWrapper'). Ensure that
# _stream_return() completes by restarting the IO Loop.
# This will prevent potential errors on shutdown.
try:
orig_loop = tornado.ioloop.IOLoop.current()
self.io_loop.make_current()
self._stream.close()
if self._read_until_future is not None:
# This will prevent this message from showing up:
# '[ERROR ] Future exception was never retrieved:
# StreamClosedError'
# This happens because the logic is always waiting to read
# the next message and the associated read future is marked
# 'StreamClosedError' when the stream is closed.
self._read_until_future.exception()
if (not self._stream_return_future.done() and
self.io_loop != tornado.ioloop.IOLoop.current(
instance=False)):
self.io_loop.add_future(
self._stream_return_future,
lambda future: self.io_loop.stop()
)
self.io_loop.start()
finally:
orig_loop.make_current()
finally:
orig_loop.make_current()
self._tcp_client.close()
# Clear callback references to allow the object that they belong to
# to be deleted.
@ -970,7 +972,8 @@ class SaltMessageClient(object):
with salt.utils.async.current_ioloop(self.io_loop):
self._stream = yield self._tcp_client.connect(self.host,
self.port,
ssl_options=self.opts.get('ssl'))
ssl_options=self.opts.get('ssl'),
**kwargs)
self._connecting_future.set_result(True)
break
except Exception as e:

View File

@ -2733,9 +2733,9 @@ def cache_nodes_ip(opts, base=None):
addresses. Returns a dict.
'''
salt.utils.versions.warn_until(
'Flourine',
'Fluorine',
'This function is incomplete and non-functional '
'and will be removed in Salt Flourine.'
'and will be removed in Salt Fluorine.'
)
if base is None:
base = opts['cachedir']

View File

@ -36,6 +36,11 @@ log = logging.getLogger(__name__)
__virtualname__ = 'mac_utils'
__salt__ = {
'cmd.run_all': salt.modules.cmdmod._run_all_quiet,
'cmd.run': salt.modules.cmdmod._run_quiet,
}
def __virtual__():
'''
@ -267,7 +272,8 @@ def launchctl(sub_cmd, *args, **kwargs):
# Run command
kwargs['python_shell'] = False
ret = salt.modules.cmdmod.run_all(cmd, **kwargs)
kwargs = salt.utils.args.clean_kwargs(**kwargs)
ret = __salt__['cmd.run_all'](cmd, **kwargs)
# Raise an error or return successful result
if ret['retcode']:
@ -321,7 +327,7 @@ def _available_services():
# the system provided plutil program to do the conversion
cmd = '/usr/bin/plutil -convert xml1 -o - -- "{0}"'.format(
true_path)
plist_xml = salt.modules.cmdmod.run(cmd, output_loglevel='quiet')
plist_xml = __salt__['cmd.run'](cmd)
if six.PY2:
plist = plistlib.readPlistFromString(plist_xml)
else:

View File

@ -3078,11 +3078,11 @@ class SaltSSHOptionParser(six.with_metaclass(OptionParserMeta,
help='Run command via sudo.'
)
auth_group.add_option(
'--skip-roster',
dest='ssh_skip_roster',
'--update-roster',
dest='ssh_update_roster',
default=False,
action='store_true',
help='If hostname is not found in the roster, do not store the information'
help='If hostname is not found in the roster, store the information'
'into the default roster file (flat).'
)
self.add_option_group(auth_group)

View File

@ -6,7 +6,7 @@ ec2-test:
script_args: '-P -Z'
ec2-win2012r2-test:
provider: ec2-config
size: t2.micro
size: m1.small
image: ami-eb1ecd96
smb_port: 445
win_installer: ''
@ -20,7 +20,7 @@ ec2-win2012r2-test:
deploy: True
ec2-win2016-test:
provider: ec2-config
size: t2.micro
size: m1.small
image: ami-ed14c790
smb_port: 445
win_installer: ''

View File

@ -249,6 +249,7 @@ class CMDMODTestCase(TestCase, LoaderModuleMockMixin):
self.assertRaises(CommandExecutionError, cmdmod._run, 'foo')
@skipIf(salt.utils.platform.is_windows(), 'Do not run on Windows')
@skipIf(True, 'Test breaks unittests runs')
def test_run(self):
'''
Tests end result when a command is not found

View File

@ -175,11 +175,8 @@ class MacUtilsTestCase(TestCase):
mock_cmd = MagicMock(return_value={'retcode': 0,
'stdout': 'success',
'stderr': 'none'})
with patch('salt.modules.cmdmod.run_all', mock_cmd) as m_run_all:
with patch('salt.utils.mac_utils.__salt__', {'cmd.run_all': mock_cmd}):
ret = mac_utils.launchctl('enable', 'org.salt.minion')
m_run_all.assert_called_with(
['launchctl', 'enable', 'org.salt.minion'],
python_shell=False)
self.assertEqual(ret, True)
def test_launchctl_return_stdout(self):
@ -189,12 +186,10 @@ class MacUtilsTestCase(TestCase):
mock_cmd = MagicMock(return_value={'retcode': 0,
'stdout': 'success',
'stderr': 'none'})
with patch('salt.modules.cmdmod.run_all', mock_cmd) as m_run_all:
with patch('salt.utils.mac_utils.__salt__', {'cmd.run_all': mock_cmd}):
ret = mac_utils.launchctl('enable',
'org.salt.minion',
return_stdout=True)
m_run_all.assert_called_with(['launchctl', 'enable', 'org.salt.minion'],
python_shell=False)
self.assertEqual(ret, 'success')
def test_launchctl_error(self):
@ -208,13 +203,11 @@ class MacUtilsTestCase(TestCase):
'stdout: failure\n' \
'stderr: test failure\n' \
'retcode: 1'
with patch('salt.modules.cmdmod.run_all', mock_cmd) as m_run_all:
with patch('salt.utils.mac_utils.__salt__', {'cmd.run_all': mock_cmd}):
try:
mac_utils.launchctl('enable', 'org.salt.minion')
except CommandExecutionError as exc:
self.assertEqual(exc.message, error)
m_run_all.assert_called_with(['launchctl', 'enable', 'org.salt.minion'],
python_shell=False)
@patch('salt.utils.path.os_walk')
@patch('os.path.exists')
@ -317,7 +310,7 @@ class MacUtilsTestCase(TestCase):
@patch('salt.utils.path.os_walk')
@patch('os.path.exists')
@patch('plistlib.readPlist')
@patch('salt.modules.cmdmod.run')
@patch('salt.utils.mac_utils.__salt__')
@patch('plistlib.readPlistFromString' if six.PY2 else 'plistlib.loads')
def test_available_services_non_xml(self,
mock_read_plist_from_string,
@ -334,9 +327,15 @@ class MacUtilsTestCase(TestCase):
[('/System/Library/LaunchAgents', [], ['com.apple.slla1.plist', 'com.apple.slla2.plist'])],
[('/System/Library/LaunchDaemons', [], ['com.apple.slld1.plist', 'com.apple.slld2.plist'])],
]
attrs = {'cmd.run': MagicMock(return_value='<some xml>')}
def getitem(name):
return attrs[name]
mock_run.__getitem__.side_effect = getitem
mock_run.configure_mock(**attrs)
mock_exists.return_value = True
mock_read_plist.side_effect = Exception()
mock_run.return_value = '<some xml>'
mock_read_plist_from_string.side_effect = [
MagicMock(Label='com.apple.lla1'),
MagicMock(Label='com.apple.lla2'),
@ -352,32 +351,24 @@ class MacUtilsTestCase(TestCase):
cmd = '/usr/bin/plutil -convert xml1 -o - -- "{0}"'
calls = [
call(cmd.format(os.path.realpath(os.path.join(
'/Library/LaunchAgents', 'com.apple.lla1.plist'))),
output_loglevel='quiet'),
call(cmd.format(os.path.realpath(os.path.join(
'/Library/LaunchAgents', 'com.apple.lla2.plist'))),
output_loglevel='quiet'),
call(cmd.format(os.path.realpath(os.path.join(
'/Library/LaunchDaemons', 'com.apple.lld1.plist'))),
output_loglevel='quiet'),
call(cmd.format(os.path.realpath(os.path.join(
'/Library/LaunchDaemons', 'com.apple.lld2.plist'))),
output_loglevel='quiet'),
call(cmd.format(os.path.realpath(os.path.join(
'/System/Library/LaunchAgents', 'com.apple.slla1.plist'))),
output_loglevel='quiet'),
call(cmd.format(os.path.realpath(os.path.join(
'/System/Library/LaunchAgents', 'com.apple.slla2.plist'))),
output_loglevel='quiet'),
call(cmd.format(os.path.realpath(os.path.join(
'/System/Library/LaunchDaemons', 'com.apple.slld1.plist'))),
output_loglevel='quiet'),
call(cmd.format(os.path.realpath(os.path.join(
'/System/Library/LaunchDaemons', 'com.apple.slld2.plist'))),
output_loglevel='quiet'),
call.cmd.run(cmd.format(os.path.realpath(os.path.join(
'/Library/LaunchAgents', 'com.apple.lla1.plist'))),),
call.cmd.run(cmd.format(os.path.realpath(os.path.join(
'/Library/LaunchAgents', 'com.apple.lla2.plist'))),),
call.cmd.run(cmd.format(os.path.realpath(os.path.join(
'/Library/LaunchDaemons', 'com.apple.lld1.plist'))),),
call.cmd.run(cmd.format(os.path.realpath(os.path.join(
'/Library/LaunchDaemons', 'com.apple.lld2.plist'))),),
call.cmd.run(cmd.format(os.path.realpath(os.path.join(
'/System/Library/LaunchAgents', 'com.apple.slla1.plist'))),),
call.cmd.run(cmd.format(os.path.realpath(os.path.join(
'/System/Library/LaunchAgents', 'com.apple.slla2.plist'))),),
call.cmd.run(cmd.format(os.path.realpath(os.path.join(
'/System/Library/LaunchDaemons', 'com.apple.slld1.plist'))),),
call.cmd.run(cmd.format(os.path.realpath(os.path.join(
'/System/Library/LaunchDaemons', 'com.apple.slld2.plist'))),),
]
mock_run.assert_has_calls(calls)
mock_run.assert_has_calls(calls, any_order=True)
# Make sure it's a dict with 8 items
self.assertTrue(isinstance(ret, dict))
@ -404,7 +395,7 @@ class MacUtilsTestCase(TestCase):
@patch('salt.utils.path.os_walk')
@patch('os.path.exists')
@patch('plistlib.readPlist')
@patch('salt.modules.cmdmod.run')
@patch('salt.utils.mac_utils.__salt__')
@patch('plistlib.readPlistFromString' if six.PY2 else 'plistlib.loads')
def test_available_services_non_xml_malformed_plist(self,
mock_read_plist_from_string,
@ -421,41 +412,39 @@ class MacUtilsTestCase(TestCase):
[('/System/Library/LaunchAgents', [], ['com.apple.slla1.plist', 'com.apple.slla2.plist'])],
[('/System/Library/LaunchDaemons', [], ['com.apple.slld1.plist', 'com.apple.slld2.plist'])],
]
attrs = {'cmd.run': MagicMock(return_value='<some xml>')}
def getitem(name):
return attrs[name]
mock_run.__getitem__.side_effect = getitem
mock_run.configure_mock(**attrs)
mock_exists.return_value = True
mock_read_plist.side_effect = Exception()
mock_run.return_value = '<some xml>'
mock_read_plist_from_string.return_value = 'malformedness'
ret = mac_utils._available_services()
cmd = '/usr/bin/plutil -convert xml1 -o - -- "{0}"'
calls = [
call(cmd.format(os.path.realpath(os.path.join(
'/Library/LaunchAgents', 'com.apple.lla1.plist'))),
output_loglevel='quiet'),
call(cmd.format(os.path.realpath(os.path.join(
'/Library/LaunchAgents', 'com.apple.lla2.plist'))),
output_loglevel='quiet'),
call(cmd.format(os.path.realpath(os.path.join(
'/Library/LaunchDaemons', 'com.apple.lld1.plist'))),
output_loglevel='quiet'),
call(cmd.format(os.path.realpath(os.path.join(
'/Library/LaunchDaemons', 'com.apple.lld2.plist'))),
output_loglevel='quiet'),
call(cmd.format(os.path.realpath(os.path.join(
'/System/Library/LaunchAgents', 'com.apple.slla1.plist'))),
output_loglevel='quiet'),
call(cmd.format(os.path.realpath(os.path.join(
'/System/Library/LaunchAgents', 'com.apple.slla2.plist'))),
output_loglevel='quiet'),
call(cmd.format(os.path.realpath(os.path.join(
'/System/Library/LaunchDaemons', 'com.apple.slld1.plist'))),
output_loglevel='quiet'),
call(cmd.format(os.path.realpath(os.path.join(
'/System/Library/LaunchDaemons', 'com.apple.slld2.plist'))),
output_loglevel='quiet'),
call.cmd.run(cmd.format(os.path.realpath(os.path.join(
'/Library/LaunchAgents', 'com.apple.lla1.plist'))),),
call.cmd.run(cmd.format(os.path.realpath(os.path.join(
'/Library/LaunchAgents', 'com.apple.lla2.plist'))),),
call.cmd.run(cmd.format(os.path.realpath(os.path.join(
'/Library/LaunchDaemons', 'com.apple.lld1.plist'))),),
call.cmd.run(cmd.format(os.path.realpath(os.path.join(
'/Library/LaunchDaemons', 'com.apple.lld2.plist'))),),
call.cmd.run(cmd.format(os.path.realpath(os.path.join(
'/System/Library/LaunchAgents', 'com.apple.slla1.plist'))),),
call.cmd.run(cmd.format(os.path.realpath(os.path.join(
'/System/Library/LaunchAgents', 'com.apple.slla2.plist'))),),
call.cmd.run(cmd.format(os.path.realpath(os.path.join(
'/System/Library/LaunchDaemons', 'com.apple.slld1.plist'))),),
call.cmd.run(cmd.format(os.path.realpath(os.path.join(
'/System/Library/LaunchDaemons', 'com.apple.slld2.plist'))),),
]
mock_run.assert_has_calls(calls)
mock_run.assert_has_calls(calls, any_order=True)
# Make sure it's a dict with 8 items
self.assertTrue(isinstance(ret, dict))

View File

@ -10,15 +10,19 @@
# Import python libs
from __future__ import absolute_import, print_function, unicode_literals
import os
import sys
import warnings
# Import Salt Testing libs
import tests.integration as integration
from tests.support.unit import TestCase, skipIf
from tests.support.mock import patch, NO_MOCK, NO_MOCK_REASON
# Import Salt libs
import salt.modules.cmdmod
import salt.version
import salt.utils.platform
import salt.utils.versions
from salt.utils.versions import LooseVersion, StrictVersion
@ -95,6 +99,40 @@ class VersionTestCase(TestCase):
'cmp(%s, %s) should be %s, got %s' %
(v1, v2, wanted, res))
@skipIf(not salt.utils.platform.is_linux(), 'only need to run on linux')
def test_spelling_version_name(self):
'''
check the spelling of the version name for the release
names in the salt.utils.versions.warn_until call
'''
salt_dir = integration.CODE_DIR
query = 'salt.utils.versions.warn_until'
names = salt.version.SaltStackVersion.NAMES
salt_dir += '/salt/'
cmd = 'grep -lr {0} -A 1 '.format(query) + salt_dir
grep_call = salt.modules.cmdmod.run_stdout(cmd=cmd).split(os.linesep)
for line in grep_call:
num_cmd = salt.modules.cmdmod.run_stdout('grep -c {0} {1}'.format(query, line))
ver_cmd = salt.modules.cmdmod.run_stdout('grep {0} {1} -A 1'.format(query, line))
if 'pyc' in line:
break
match = 0
for key in names:
if key in ver_cmd:
match = match + (ver_cmd.count(key))
if 'utils/__init__.py' in line:
# work around for utils/__init__.py because
# it includes the warn_utils function
match = match + 1
self.assertEqual(match, int(num_cmd), msg='The file: {0} has an '
'incorrect spelling for the release name in the warn_utils '
'call: {1}. Expecting one of these release names: '
'{2}'.format(line, ver_cmd, names))
class VersionFuncsTestCase(TestCase):