mirror of
https://github.com/saltstack/salt.git
synced 2025-04-17 10:10:20 +00:00
Merge branch 'master' into master.v3000-conda
This commit is contained in:
commit
566c03b786
19 changed files with 396 additions and 168 deletions
|
@ -592,6 +592,19 @@ Enhancements to chroot
|
|||
:py:func:`highstate<salt.modules.chroot.highstate>` that allow executing
|
||||
states in sls files or running apply/highstate inside of a chroot.
|
||||
|
||||
Minion-side ACL
|
||||
---------------
|
||||
|
||||
Salt has had master-side ACL for the salt mine for some time, where the master
|
||||
configuration contained `mine_get` that specified which minions could request
|
||||
which functions. However, now you can specify which minions can access a function
|
||||
in the salt mine function definition itself (or when calling :py:func:`mine.send <salt.modules.mine.send>`).
|
||||
This targeting works the same as the generic minion targeting as specified
|
||||
:ref:`here <targeting>`. The parameters used are ``allow_tgt`` and ``allow_tgt_type``.
|
||||
See also :ref:`the documentation of the Salt Mine <mine_minion-side-acl>`. Please
|
||||
note that if you want to use this new feature both your minion and masters will need
|
||||
to be on atleast version 3000.
|
||||
|
||||
Deprecations
|
||||
============
|
||||
|
||||
|
|
|
@ -16,14 +16,3 @@ also support the syntax used in :py:mod:`module.run <salt.states.module.run>`.
|
|||
The old syntax for the mine_function - as a dict, or as a list with dicts that
|
||||
contain more than exactly one key - is still supported but discouraged in favor
|
||||
of the more uniform syntax of module.run.
|
||||
|
||||
Minion-side ACL
|
||||
---------------
|
||||
|
||||
Salt has had master-side ACL for the salt mine for some time, where the master
|
||||
configuration contained `mine_get` that specified which minions could request
|
||||
which functions. However, now you can specify which minions can access a function
|
||||
in the salt mine function definition itself (or when calling :py:func:`mine.send <salt.modules.mine.send>`).
|
||||
This targeting works the same as the generic minion targeting as specified
|
||||
:ref:`here <targeting>`. The parameters used are ``allow_tgt`` and ``allow_tgt_type``.
|
||||
See also :ref:`the documentation of the Salt Mine <mine_minion-side-acl>`.
|
||||
|
|
|
@ -12,7 +12,7 @@ import importlib
|
|||
|
||||
class TornadoImporter(object):
|
||||
|
||||
def find_module(self, module_name, package_path):
|
||||
def find_module(self, module_name, package_path=None):
|
||||
if module_name.startswith('tornado'):
|
||||
return self
|
||||
return None
|
||||
|
|
|
@ -617,14 +617,17 @@ class RemoteFuncs(object):
|
|||
if 'allow_tgt' in mine_entry:
|
||||
# Only determine allowed targets if any have been specified.
|
||||
# This prevents having to add a list of all minions as allowed targets.
|
||||
get_minion = checker.check_minions(
|
||||
mine_entry['allow_tgt'],
|
||||
mine_entry.get('allow_tgt_type', 'glob'))['minions']
|
||||
# the minion in allow_tgt does not exist
|
||||
if not get_minion:
|
||||
continue
|
||||
salt.utils.dictupdate.set_dict_key_value(
|
||||
minion_side_acl,
|
||||
'{}:{}'.format(minion, function),
|
||||
checker.check_minions(
|
||||
mine_entry['allow_tgt'],
|
||||
mine_entry.get('allow_tgt_type', 'glob')
|
||||
)['minions']
|
||||
)
|
||||
get_minion
|
||||
)
|
||||
if salt.utils.mine.minion_side_acl_denied(minion_side_acl, minion, function, load['id']):
|
||||
continue
|
||||
if _ret_dict:
|
||||
|
|
|
@ -27,6 +27,7 @@ from salt.ext import six
|
|||
from salt._compat import ipaddress
|
||||
from salt.utils.network import parse_host_port
|
||||
from salt.ext.six.moves import range
|
||||
from salt.template import SLS_ENCODING
|
||||
from salt.utils.zeromq import zmq, ZMQDefaultLoop, install_zmq, ZMQ_VERSION_INFO
|
||||
import salt.transport.client
|
||||
import salt.defaults.exitcodes
|
||||
|
@ -865,11 +866,11 @@ class SMinion(MinionBase):
|
|||
penv = 'base'
|
||||
cache_top = {penv: {self.opts['id']: ['cache']}}
|
||||
with salt.utils.files.fopen(ptop, 'wb') as fp_:
|
||||
salt.utils.yaml.safe_dump(cache_top, fp_)
|
||||
salt.utils.yaml.safe_dump(cache_top, fp_, encoding=SLS_ENCODING)
|
||||
os.chmod(ptop, 0o600)
|
||||
cache_sls = os.path.join(pdir, 'cache.sls')
|
||||
with salt.utils.files.fopen(cache_sls, 'wb') as fp_:
|
||||
salt.utils.yaml.safe_dump(self.opts['pillar'], fp_)
|
||||
salt.utils.yaml.safe_dump(self.opts['pillar'], fp_, encoding=SLS_ENCODING)
|
||||
os.chmod(cache_sls, 0o600)
|
||||
|
||||
|
||||
|
|
|
@ -194,10 +194,13 @@ def update(clear=False, mine_functions=None):
|
|||
log.error('Function %s in mine.update failed to execute', function_name or function_alias)
|
||||
log.debug('Error: %s', trace)
|
||||
continue
|
||||
mine_data[function_alias] = salt.utils.mine.wrap_acl_structure(
|
||||
res,
|
||||
**minion_acl
|
||||
)
|
||||
if minion_acl.get('allow_tgt'):
|
||||
mine_data[function_alias] = salt.utils.mine.wrap_acl_structure(
|
||||
res,
|
||||
**minion_acl
|
||||
)
|
||||
else:
|
||||
mine_data[function_alias] = res
|
||||
return _mine_store(mine_data, clear)
|
||||
|
||||
|
||||
|
@ -213,9 +216,13 @@ def send(name, *args, **kwargs):
|
|||
:param str mine_function: The name of the execution_module.function to run
|
||||
and whose value will be stored in the salt mine. Defaults to ``name``.
|
||||
:param str allow_tgt: Targeting specification for ACL. Specifies which minions
|
||||
are allowed to access this function.
|
||||
are allowed to access this function. Please note both your master and
|
||||
minion need to be on, at least, version 3000 for this to work properly.
|
||||
|
||||
:param str allow_tgt_type: Type of the targeting specification. This value will
|
||||
be ignored if ``allow_tgt`` is not specified.
|
||||
be ignored if ``allow_tgt`` is not specified. Please note both your
|
||||
master and minion need to be on, at least, version 3000 for this to work
|
||||
properly.
|
||||
|
||||
Remaining args and kwargs will be passed on to the function to run.
|
||||
|
||||
|
@ -252,11 +259,15 @@ def send(name, *args, **kwargs):
|
|||
log.error('Function %s in mine.send failed to execute', mine_function or name)
|
||||
log.debug('Error: %s', trace)
|
||||
return False
|
||||
mine_data[name] = salt.utils.mine.wrap_acl_structure(
|
||||
res,
|
||||
allow_tgt=allow_tgt,
|
||||
allow_tgt_type=allow_tgt_type
|
||||
)
|
||||
|
||||
if allow_tgt:
|
||||
mine_data[name] = salt.utils.mine.wrap_acl_structure(
|
||||
res,
|
||||
allow_tgt=allow_tgt,
|
||||
allow_tgt_type=allow_tgt_type
|
||||
)
|
||||
else:
|
||||
mine_data[name] = res
|
||||
return _mine_store(mine_data)
|
||||
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ log = logging.getLogger(__name__)
|
|||
HAS_DJANGO = False
|
||||
|
||||
try:
|
||||
from django import dispatch
|
||||
from django import dispatch # pylint: disable=E0611
|
||||
HAS_DJANGO = True
|
||||
except ImportError:
|
||||
HAS_DJANGO = False
|
||||
|
|
|
@ -10,7 +10,7 @@ Runner to provide F5 Load Balancer functionality
|
|||
.. code-block:: yaml
|
||||
|
||||
load_balancers:
|
||||
bigip1.example.com
|
||||
bigip1.example.com:
|
||||
username: admin
|
||||
password: secret
|
||||
bigip2.example.com:
|
||||
|
|
|
@ -319,50 +319,44 @@ class SaltStackVersion(object):
|
|||
# Higher than 0.17, lower than first date based
|
||||
return 0 < self.major < 2014
|
||||
|
||||
def min_info(self):
|
||||
info = [self.major]
|
||||
if self.new_version(self.major):
|
||||
if self.minor:
|
||||
info.append(self.minor)
|
||||
else:
|
||||
info.extend([self.minor,
|
||||
self.bugfix,
|
||||
self.mbugfix])
|
||||
return info
|
||||
|
||||
@property
|
||||
def info(self):
|
||||
return (
|
||||
self.major,
|
||||
self.minor,
|
||||
self.bugfix,
|
||||
self.mbugfix
|
||||
)
|
||||
return tuple(self.min_info())
|
||||
|
||||
@property
|
||||
def pre_info(self):
|
||||
return (
|
||||
self.major,
|
||||
self.minor,
|
||||
self.bugfix,
|
||||
self.mbugfix,
|
||||
self.pre_type,
|
||||
self.pre_num
|
||||
)
|
||||
info = self.min_info()
|
||||
info.extend([self.pre_type,
|
||||
self.pre_num])
|
||||
return tuple(info)
|
||||
|
||||
@property
|
||||
def noc_info(self):
|
||||
return (
|
||||
self.major,
|
||||
self.minor,
|
||||
self.bugfix,
|
||||
self.mbugfix,
|
||||
self.pre_type,
|
||||
self.pre_num,
|
||||
self.noc
|
||||
)
|
||||
info = self.min_info()
|
||||
info.extend([self.pre_type,
|
||||
self.pre_num,
|
||||
self.noc])
|
||||
return tuple(info)
|
||||
|
||||
@property
|
||||
def full_info(self):
|
||||
return (
|
||||
self.major,
|
||||
self.minor,
|
||||
self.bugfix,
|
||||
self.mbugfix,
|
||||
self.pre_type,
|
||||
self.pre_num,
|
||||
self.noc,
|
||||
self.sha
|
||||
)
|
||||
info = self.min_info()
|
||||
info.extend([self.pre_type,
|
||||
self.pre_num,
|
||||
self.noc,
|
||||
self.sha])
|
||||
return tuple(info)
|
||||
|
||||
@property
|
||||
def string(self):
|
||||
|
@ -402,6 +396,16 @@ class SaltStackVersion(object):
|
|||
version_string += ' ({0})'.format(self.RMATCH[(self.major, self.minor)])
|
||||
return version_string
|
||||
|
||||
@property
|
||||
def pre_index(self):
|
||||
if self.new_version(self.major):
|
||||
pre_type = 2
|
||||
if not isinstance(self.minor, int):
|
||||
pre_type = 1
|
||||
else:
|
||||
pre_type = 4
|
||||
return pre_type
|
||||
|
||||
def __str__(self):
|
||||
return self.string
|
||||
|
||||
|
@ -418,23 +422,29 @@ class SaltStackVersion(object):
|
|||
)
|
||||
)
|
||||
|
||||
pre_type = self.pre_index
|
||||
other_pre_type = other.pre_index
|
||||
other_noc_info = list(other.noc_info)
|
||||
noc_info = list(self.noc_info)
|
||||
|
||||
if self.new_version(self.major):
|
||||
if isinstance(self.minor, int) and not isinstance(other.minor, int):
|
||||
other_noc_info[1] = 0
|
||||
if self.minor and not other.minor:
|
||||
# We have minor information, the other side does not
|
||||
if self.minor > 0:
|
||||
other_noc_info[1] = 0
|
||||
|
||||
if not isinstance(self.minor, int) and isinstance(other.minor, int):
|
||||
noc_info[1] = 0
|
||||
if not self.minor and other.minor:
|
||||
# The other side has minor information, we don't
|
||||
if other.minor > 0:
|
||||
noc_info[1] = 0
|
||||
|
||||
if self.pre_type and not other.pre_type:
|
||||
# We have pre-release information, the other side doesn't
|
||||
other_noc_info[4] = 'zzzzz'
|
||||
other_noc_info[other_pre_type] = 'zzzzz'
|
||||
|
||||
if not self.pre_type and other.pre_type:
|
||||
# The other side has pre-release informatio, we don't
|
||||
noc_info[4] = 'zzzzz'
|
||||
# The other side has pre-release information, we don't
|
||||
noc_info[pre_type] = 'zzzzz'
|
||||
|
||||
return method(tuple(noc_info), tuple(other_noc_info))
|
||||
|
||||
|
|
|
@ -95,6 +95,9 @@ config_test:
|
|||
|
||||
mine_functions:
|
||||
test.ping: []
|
||||
test.arg:
|
||||
- isn't
|
||||
- allow_tgt: 'sub_minion'
|
||||
|
||||
# sdb env module
|
||||
osenv:
|
||||
|
|
|
@ -62,3 +62,8 @@ grains:
|
|||
keystone.password: demopass
|
||||
keystone.tenant: demo
|
||||
keystone.auth_url: http://127.0.0.1:5000/v3/
|
||||
|
||||
mine_functions:
|
||||
test.arg:
|
||||
- isn't
|
||||
- allow_tgt: 'sub_minion'
|
||||
|
|
53
tests/integration/minion/test_minion_cache.py
Normal file
53
tests/integration/minion/test_minion_cache.py
Normal file
|
@ -0,0 +1,53 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
import os
|
||||
|
||||
import salt.loader
|
||||
import salt.minion
|
||||
import salt.utils.yaml
|
||||
from salt.utils.files import fopen
|
||||
from tests.support.case import ModuleCase
|
||||
from tests.support.helpers import with_tempdir
|
||||
from tests.support.mock import patch
|
||||
|
||||
|
||||
class BasePillarTest(ModuleCase):
|
||||
@with_tempdir()
|
||||
def test_minion_cache_should_cache_files(self, tempdir):
|
||||
pillar = {"this": {"is": {"some": "pillar data"}}}
|
||||
opts = {
|
||||
"file_client": "remote",
|
||||
"minion_pillar_cache": "true",
|
||||
"master_type": "local",
|
||||
"discovery": False,
|
||||
"master": "local",
|
||||
"__role": "",
|
||||
"id": "test",
|
||||
"saltenv": "base",
|
||||
"pillar_cache": True,
|
||||
"pillar_cache_backend": "disk",
|
||||
"pillar_cache_ttl": 3600,
|
||||
"cachedir": tempdir,
|
||||
"state_top": "top.sls",
|
||||
"pillar_roots": {"base": tempdir},
|
||||
"extension_modules": tempdir,
|
||||
"file_ignore_regex": [],
|
||||
"file_ignore_glob": [],
|
||||
"pillar": pillar,
|
||||
}
|
||||
with patch("salt.loader.grains", return_value={}), patch(
|
||||
"salt.minion.SMinion.gen_modules"
|
||||
), patch("salt.minion.SMinion.eval_master"), patch(
|
||||
"salt.minion.install_zmq"
|
||||
), patch(
|
||||
"salt.minion.ZMQDefaultLoop.current"
|
||||
):
|
||||
minion = salt.minion.SMinion(opts)
|
||||
self.assertTrue("pillar" in os.listdir(tempdir))
|
||||
pillar_cache = os.path.join(tempdir, "pillar")
|
||||
self.assertTrue("top.sls" in os.listdir(pillar_cache))
|
||||
self.assertTrue("cache.sls" in os.listdir(pillar_cache))
|
||||
with fopen(os.path.join(pillar_cache, "cache.sls"), "rb") as f:
|
||||
cached_data = salt.utils.yaml.safe_load(f)
|
||||
assert cached_data == pillar
|
|
@ -8,29 +8,29 @@ import time
|
|||
import pprint
|
||||
|
||||
# Import Salt Testing libs
|
||||
from tests.support.case import ModuleCase
|
||||
from tests.support.case import ModuleCase, ShellCase
|
||||
from tests.support.runtests import RUNTIME_VARS
|
||||
|
||||
# Import Salt libs
|
||||
import salt.utils.platform
|
||||
|
||||
|
||||
class MineTest(ModuleCase):
|
||||
class MineTest(ModuleCase, ShellCase):
|
||||
'''
|
||||
Test the mine system
|
||||
'''
|
||||
def setUp(self):
|
||||
self.tgt = r'\*'
|
||||
if salt.utils.platform.is_windows():
|
||||
self.tgt = '*'
|
||||
self.wait_for_all_jobs()
|
||||
|
||||
def test_get(self):
|
||||
'''
|
||||
test mine.get and mine.update
|
||||
'''
|
||||
self.assertTrue(self.run_function('mine.update', minion_tgt='minion'))
|
||||
# The sub_minion does not have mine_functions defined in its configuration
|
||||
# In this case, mine.update returns None
|
||||
self.assertIsNone(
|
||||
self.run_function(
|
||||
'mine.update',
|
||||
minion_tgt='sub_minion'
|
||||
)
|
||||
)
|
||||
assert self.run_function('mine.update', minion_tgt='minion')
|
||||
assert self.run_function('mine.update', minion_tgt='sub_minion')
|
||||
# Since the minion has mine_functions defined in its configuration,
|
||||
# mine.update will return True
|
||||
self.assertTrue(
|
||||
|
@ -40,6 +40,78 @@ class MineTest(ModuleCase):
|
|||
)
|
||||
)
|
||||
|
||||
def test_get_allow_tgt(self):
|
||||
'''
|
||||
test mine.get and mine.update using allow_tgt
|
||||
'''
|
||||
assert self.run_function('mine.update', minion_tgt='minion')
|
||||
assert self.run_function('mine.update', minion_tgt='sub_minion')
|
||||
|
||||
# sub_minion should be able to view test.arg data
|
||||
sub_min_ret = self.run_call('mine.get {0} test.arg'.format(self.tgt), config_dir=RUNTIME_VARS.TMP_SUB_MINION_CONF_DIR)
|
||||
assert " - isn't" in sub_min_ret
|
||||
|
||||
# minion should not be able to view test.arg data
|
||||
min_ret = self.run_call('mine.get {0} test.arg'.format(self.tgt))
|
||||
assert " - isn't" not in min_ret
|
||||
|
||||
def test_send_allow_tgt(self):
|
||||
'''
|
||||
test mine.send with allow_tgt set
|
||||
'''
|
||||
mine_name = 'test_this'
|
||||
for minion in ['sub_minion', 'minion']:
|
||||
assert self.run_function('mine.send', [mine_name,
|
||||
'mine_function=test.arg_clean', 'one'], allow_tgt='sub_minion',
|
||||
minion_tgt=minion)
|
||||
min_ret = self.run_call('mine.get {0} {1}'.format(self.tgt, mine_name))
|
||||
sub_ret = self.run_call('mine.get {0} {1}'.format(self.tgt, mine_name),
|
||||
config_dir=RUNTIME_VARS.TMP_SUB_MINION_CONF_DIR)
|
||||
|
||||
# ensure we did get the mine_name mine function for sub_minion
|
||||
assert ' - one' in sub_ret
|
||||
# ensure we did not get the mine_name mine function for minion
|
||||
assert ' - one' not in min_ret
|
||||
|
||||
def test_send_allow_tgt_compound(self):
|
||||
'''
|
||||
test mine.send with allow_tgt set
|
||||
and using compound targeting
|
||||
'''
|
||||
mine_name = 'test_this_comp'
|
||||
for minion in ['sub_minion', 'minion']:
|
||||
assert self.run_function('mine.send', [mine_name,
|
||||
'mine_function=test.arg_clean', 'one'],
|
||||
allow_tgt='L@minion,sub_minion',
|
||||
allow_tgt_type='compound',
|
||||
minion_tgt=minion)
|
||||
min_ret = self.run_call('mine.get {0} {1}'.format(self.tgt, mine_name))
|
||||
sub_ret = self.run_call('mine.get {0} {1}'.format(self.tgt, mine_name),
|
||||
config_dir=RUNTIME_VARS.TMP_SUB_MINION_CONF_DIR)
|
||||
|
||||
# ensure we get the mine_name mine function for both minions
|
||||
for ret in [min_ret, sub_ret]:
|
||||
assert ' - one' in ret
|
||||
|
||||
def test_send_allow_tgt_doesnotexist(self):
|
||||
'''
|
||||
test mine.send with allow_tgt set when
|
||||
the minion defined in allow_tgt does
|
||||
not exist
|
||||
'''
|
||||
mine_name = 'mine_doesnotexist'
|
||||
for minion in ['sub_minion', 'minion']:
|
||||
assert self.run_function('mine.send', [mine_name,
|
||||
'mine_function=test.arg_clean', 'one'], allow_tgt='doesnotexist',
|
||||
minion_tgt=minion)
|
||||
min_ret = self.run_call('mine.get {0} {1}'.format(self.tgt, mine_name))
|
||||
sub_ret = self.run_call('mine.get {0} {1}'.format(self.tgt, mine_name),
|
||||
config_dir=RUNTIME_VARS.TMP_SUB_MINION_CONF_DIR)
|
||||
|
||||
# ensure we did not get the mine_name mine function for both minions
|
||||
for ret in [sub_ret, min_ret]:
|
||||
assert ' - one' not in ret
|
||||
|
||||
def test_send(self):
|
||||
'''
|
||||
test mine.send
|
||||
|
|
|
@ -175,9 +175,12 @@ class ShellTestCase(TestCase, AdaptedConfigurationTestCaseMixin, ScriptPathMixin
|
|||
arg_str = '--config-dir {0} {1}'.format(self.config_dir, arg_str)
|
||||
return self.run_script('salt-cp', arg_str, with_retcode=with_retcode, catch_stderr=catch_stderr)
|
||||
|
||||
def run_call(self, arg_str, with_retcode=False, catch_stderr=False, local=False, timeout=15):
|
||||
def run_call(self, arg_str, with_retcode=False, catch_stderr=False,
|
||||
local=False, timeout=15, config_dir=None):
|
||||
if not config_dir:
|
||||
config_dir = self.config_dir
|
||||
arg_str = '{0} --config-dir {1} {2}'.format('--local' if local else '',
|
||||
self.config_dir, arg_str)
|
||||
config_dir, arg_str)
|
||||
|
||||
return self.run_script('salt-call',
|
||||
arg_str,
|
||||
|
@ -582,12 +585,14 @@ class ShellCase(ShellTestCase, AdaptedConfigurationTestCaseMixin, ScriptPathMixi
|
|||
timeout=timeout)
|
||||
|
||||
def run_call(self, arg_str, with_retcode=False, catch_stderr=False, # pylint: disable=W0221
|
||||
local=False, timeout=RUN_TIMEOUT):
|
||||
local=False, timeout=RUN_TIMEOUT, config_dir=None):
|
||||
'''
|
||||
Execute salt-call.
|
||||
'''
|
||||
if not config_dir:
|
||||
config_dir = self.config_dir
|
||||
arg_str = '{0} --config-dir {1} {2}'.format('--local' if local else '',
|
||||
self.config_dir, arg_str)
|
||||
config_dir, arg_str)
|
||||
ret = self.run_script('salt-call',
|
||||
arg_str,
|
||||
with_retcode=with_retcode,
|
||||
|
@ -772,8 +777,6 @@ class ModuleCase(TestCase, SaltClientTestCaseMixin):
|
|||
'ssh.recv_known_host_entries',
|
||||
'time.sleep'
|
||||
)
|
||||
if minion_tgt == 'sub_minion':
|
||||
known_to_return_none += ('mine.update',)
|
||||
if 'f_arg' in kwargs:
|
||||
kwargs['arg'] = kwargs.pop('f_arg')
|
||||
if 'f_timeout' in kwargs:
|
||||
|
|
|
@ -1485,3 +1485,19 @@ class CoreGrainsTestCase(TestCase, LoaderModuleMockMixin):
|
|||
|
||||
self.assertIn('osfullname', os_grains)
|
||||
self.assertEqual(os_grains.get('osfullname'), 'FreeBSD')
|
||||
|
||||
def test_saltversioninfo(self):
|
||||
'''
|
||||
test saltversioninfo core grain.
|
||||
'''
|
||||
ret = core.saltversioninfo()
|
||||
info = ret['saltversioninfo']
|
||||
assert isinstance(ret, dict)
|
||||
assert isinstance(info, list)
|
||||
try:
|
||||
assert len(info) == 1
|
||||
except AssertionError:
|
||||
# We have a minor version we need to test
|
||||
assert len(info) == 2
|
||||
assert all([x is not None for x in info])
|
||||
assert all([isinstance(x, int) for x in info])
|
||||
|
|
|
@ -43,6 +43,9 @@ class MineTestCase(TestCase, LoaderModuleMockMixin):
|
|||
Test cases for salt.modules.mine
|
||||
'''
|
||||
def setUp(self):
|
||||
self.kernel_ret = 'Linux!'
|
||||
self.foo_ret = 'baz'
|
||||
self.ip_ret = '2001:db8::1:3'
|
||||
self.cache = FakeCache()
|
||||
|
||||
def setup_loader_modules(self):
|
||||
|
@ -94,15 +97,16 @@ class MineTestCase(TestCase, LoaderModuleMockMixin):
|
|||
'''
|
||||
Tests sending an item to the mine in the minion's local cache,
|
||||
and then immediately fetching it again (since tests are executed unordered).
|
||||
Also verify that the stored mine cache has the correct structure (with ACL).
|
||||
Also verify that the stored mine cache does not use ACL data structure
|
||||
without allow_tgt passed.
|
||||
'''
|
||||
with patch.dict(mine.__opts__, {
|
||||
'file_client': 'local',
|
||||
'id': 'webserver',
|
||||
}), \
|
||||
patch.dict(mine.__salt__, {
|
||||
'network.ip_addrs': MagicMock(return_value='2001:db8::1:3'),
|
||||
'foo.bar': MagicMock(return_value='baz'),
|
||||
'network.ip_addrs': MagicMock(return_value=self.ip_ret),
|
||||
'foo.bar': MagicMock(return_value=self.foo_ret),
|
||||
}):
|
||||
ret = mine.send('ip_addr', mine_function='network.ip_addrs')
|
||||
mine.send('foo.bar')
|
||||
|
@ -110,14 +114,8 @@ class MineTestCase(TestCase, LoaderModuleMockMixin):
|
|||
self.assertEqual(
|
||||
self.cache.fetch('minions/webserver', 'mine_cache'),
|
||||
{
|
||||
'ip_addr': {
|
||||
salt.utils.mine.MINE_ITEM_ACL_DATA: '2001:db8::1:3',
|
||||
salt.utils.mine.MINE_ITEM_ACL_ID: salt.utils.mine.MINE_ITEM_ACL_VERSION,
|
||||
},
|
||||
'foo.bar': {
|
||||
salt.utils.mine.MINE_ITEM_ACL_DATA: 'baz',
|
||||
salt.utils.mine.MINE_ITEM_ACL_ID: salt.utils.mine.MINE_ITEM_ACL_VERSION,
|
||||
},
|
||||
'ip_addr': self.ip_ret,
|
||||
'foo.bar': self.foo_ret,
|
||||
}
|
||||
)
|
||||
with patch.dict(mine.__opts__, {
|
||||
|
@ -128,9 +126,9 @@ class MineTestCase(TestCase, LoaderModuleMockMixin):
|
|||
ret_single_dict = mine.get('*', ['ip_addr'])
|
||||
ret_multi = mine.get('*', 'ip_addr,foo.bar')
|
||||
ret_multi2 = mine.get('*', ['ip_addr', 'foo.bar'])
|
||||
self.assertEqual(ret_single, {'webserver': '2001:db8::1:3'})
|
||||
self.assertEqual(ret_single_dict, {'ip_addr': {'webserver': '2001:db8::1:3'}})
|
||||
self.assertEqual(ret_multi, {'ip_addr': {'webserver': '2001:db8::1:3'}, 'foo.bar': {'webserver': 'baz'}})
|
||||
self.assertEqual(ret_single, {'webserver': self.ip_ret})
|
||||
self.assertEqual(ret_single_dict, {'ip_addr': {'webserver': self.ip_ret}})
|
||||
self.assertEqual(ret_multi, {'ip_addr': {'webserver': self.ip_ret}, 'foo.bar': {'webserver': self.foo_ret}})
|
||||
self.assertEqual(ret_multi, ret_multi2)
|
||||
|
||||
def test_send_get_acl_local(self):
|
||||
|
@ -138,15 +136,16 @@ class MineTestCase(TestCase, LoaderModuleMockMixin):
|
|||
Tests sending an item to the mine in the minion's local cache,
|
||||
including ACL information (useless when only working locally, but hey),
|
||||
and then immediately fetching it again (since tests are executed unordered).
|
||||
Also verify that the stored mine cache has the correct structure (with ACL).
|
||||
Also verify that the stored mine cache has the correct structure (with ACL)
|
||||
when using allow_tgt and no ACL without allow_tgt.
|
||||
'''
|
||||
with patch.dict(mine.__opts__, {
|
||||
'file_client': 'local',
|
||||
'id': 'webserver',
|
||||
}), \
|
||||
patch.dict(mine.__salt__, {
|
||||
'network.ip_addrs': MagicMock(return_value='2001:db8::1:3'),
|
||||
'foo.bar': MagicMock(return_value='baz'),
|
||||
'network.ip_addrs': MagicMock(return_value=self.ip_ret),
|
||||
'foo.bar': MagicMock(return_value=self.foo_ret),
|
||||
}):
|
||||
ret = mine.send('ip_addr', mine_function='network.ip_addrs', allow_tgt='web*', allow_tgt_type='glob')
|
||||
mine.send('foo.bar')
|
||||
|
@ -155,15 +154,12 @@ class MineTestCase(TestCase, LoaderModuleMockMixin):
|
|||
self.cache.fetch('minions/webserver', 'mine_cache'),
|
||||
{
|
||||
'ip_addr': {
|
||||
salt.utils.mine.MINE_ITEM_ACL_DATA: '2001:db8::1:3',
|
||||
salt.utils.mine.MINE_ITEM_ACL_DATA: self.ip_ret,
|
||||
salt.utils.mine.MINE_ITEM_ACL_ID: salt.utils.mine.MINE_ITEM_ACL_VERSION,
|
||||
'allow_tgt': 'web*',
|
||||
'allow_tgt_type': 'glob',
|
||||
},
|
||||
'foo.bar': {
|
||||
salt.utils.mine.MINE_ITEM_ACL_DATA: 'baz',
|
||||
salt.utils.mine.MINE_ITEM_ACL_ID: salt.utils.mine.MINE_ITEM_ACL_VERSION,
|
||||
},
|
||||
'foo.bar': self.foo_ret,
|
||||
}
|
||||
)
|
||||
with patch.dict(mine.__opts__, {
|
||||
|
@ -171,7 +167,7 @@ class MineTestCase(TestCase, LoaderModuleMockMixin):
|
|||
'id': 'webserver',
|
||||
}):
|
||||
ret_single = mine.get('*', 'ip_addr')
|
||||
self.assertEqual(ret_single, {'webserver': '2001:db8::1:3'})
|
||||
self.assertEqual(ret_single, {'webserver': self.ip_ret})
|
||||
|
||||
def test_send_master(self):
|
||||
'''
|
||||
|
@ -180,7 +176,7 @@ class MineTestCase(TestCase, LoaderModuleMockMixin):
|
|||
'''
|
||||
with patch.object(mine, '_mine_send', MagicMock(side_effect=lambda x, y: x)),\
|
||||
patch.dict(mine.__salt__, {
|
||||
'foo.bar': MagicMock(return_value='baz'),
|
||||
'foo.bar': MagicMock(return_value=self.foo_ret),
|
||||
}), \
|
||||
patch.dict(mine.__opts__, {
|
||||
'file_client': 'remote',
|
||||
|
@ -192,12 +188,7 @@ class MineTestCase(TestCase, LoaderModuleMockMixin):
|
|||
{
|
||||
'id': 'foo',
|
||||
'cmd': '_mine',
|
||||
'data': {
|
||||
'foo.bar': {
|
||||
salt.utils.mine.MINE_ITEM_ACL_DATA: 'baz',
|
||||
salt.utils.mine.MINE_ITEM_ACL_ID: salt.utils.mine.MINE_ITEM_ACL_VERSION,
|
||||
},
|
||||
},
|
||||
'data': {'foo.bar': self.foo_ret},
|
||||
'clear': False,
|
||||
}
|
||||
)
|
||||
|
@ -209,7 +200,7 @@ class MineTestCase(TestCase, LoaderModuleMockMixin):
|
|||
'''
|
||||
with patch.object(mine, '_mine_send', MagicMock(side_effect=lambda x, y: x)),\
|
||||
patch.dict(mine.__salt__, {
|
||||
'foo.bar': MagicMock(return_value='baz'),
|
||||
'foo.bar': MagicMock(return_value=self.foo_ret),
|
||||
}), \
|
||||
patch.dict(mine.__opts__, {
|
||||
'file_client': 'remote',
|
||||
|
@ -223,7 +214,7 @@ class MineTestCase(TestCase, LoaderModuleMockMixin):
|
|||
'cmd': '_mine',
|
||||
'data': {
|
||||
'foo.bar': {
|
||||
salt.utils.mine.MINE_ITEM_ACL_DATA: 'baz',
|
||||
salt.utils.mine.MINE_ITEM_ACL_DATA: self.foo_ret,
|
||||
salt.utils.mine.MINE_ITEM_ACL_ID: salt.utils.mine.MINE_ITEM_ACL_VERSION,
|
||||
'allow_tgt': 'roles:web',
|
||||
'allow_tgt_type': 'grains',
|
||||
|
@ -239,7 +230,7 @@ class MineTestCase(TestCase, LoaderModuleMockMixin):
|
|||
'''
|
||||
mock_load = {
|
||||
'tgt_type': 'qux',
|
||||
'tgt': 'baz',
|
||||
'tgt': self.foo_ret,
|
||||
'cmd': '_mine_get',
|
||||
'fun': 'foo.bar',
|
||||
'id': 'foo'
|
||||
|
@ -292,9 +283,9 @@ class MineTestCase(TestCase, LoaderModuleMockMixin):
|
|||
}), \
|
||||
patch.dict(mine.__salt__, {
|
||||
'config.merge': MagicMock(return_value=config_mine_functions),
|
||||
'grains.get': lambda x: 'Linux!',
|
||||
'network.ip_addrs': MagicMock(return_value='2001:db8::1:3'),
|
||||
'foo.bar': MagicMock(return_value='baz'),
|
||||
'grains.get': lambda x: self.kernel_ret,
|
||||
'network.ip_addrs': MagicMock(return_value=self.ip_ret),
|
||||
'foo.bar': MagicMock(return_value=self.foo_ret),
|
||||
}):
|
||||
ret = mine.update()
|
||||
self.assertEqual(ret, 'FakeCache:StoreSuccess!')
|
||||
|
@ -302,22 +293,16 @@ class MineTestCase(TestCase, LoaderModuleMockMixin):
|
|||
self.assertEqual(
|
||||
self.cache.fetch('minions/webserver', 'mine_cache'),
|
||||
{
|
||||
'ip_addr': {
|
||||
salt.utils.mine.MINE_ITEM_ACL_DATA: '2001:db8::1:3',
|
||||
salt.utils.mine.MINE_ITEM_ACL_ID: salt.utils.mine.MINE_ITEM_ACL_VERSION,
|
||||
},
|
||||
'network.ip_addrs': {
|
||||
salt.utils.mine.MINE_ITEM_ACL_DATA: '2001:db8::1:3',
|
||||
salt.utils.mine.MINE_ITEM_ACL_ID: salt.utils.mine.MINE_ITEM_ACL_VERSION,
|
||||
},
|
||||
'ip_addr': self.ip_ret,
|
||||
'network.ip_addrs': self.ip_ret,
|
||||
'foo.bar': {
|
||||
salt.utils.mine.MINE_ITEM_ACL_DATA: 'baz',
|
||||
salt.utils.mine.MINE_ITEM_ACL_DATA: self.foo_ret,
|
||||
salt.utils.mine.MINE_ITEM_ACL_ID: salt.utils.mine.MINE_ITEM_ACL_VERSION,
|
||||
'allow_tgt': 'G@roles:webserver',
|
||||
'allow_tgt_type': 'compound',
|
||||
},
|
||||
'kernel': {
|
||||
salt.utils.mine.MINE_ITEM_ACL_DATA: 'Linux!',
|
||||
salt.utils.mine.MINE_ITEM_ACL_DATA: self.kernel_ret,
|
||||
salt.utils.mine.MINE_ITEM_ACL_ID: salt.utils.mine.MINE_ITEM_ACL_VERSION,
|
||||
'allow_tgt': 'web*',
|
||||
},
|
||||
|
@ -343,8 +328,8 @@ class MineTestCase(TestCase, LoaderModuleMockMixin):
|
|||
patch.dict(mine.__salt__, {
|
||||
'config.merge': MagicMock(return_value={}),
|
||||
'grains.get': lambda x: 'Linux!!',
|
||||
'network.ip_addrs': MagicMock(return_value='2001:db8::1:4'),
|
||||
'foo.bar': MagicMock(return_value='baz'),
|
||||
'network.ip_addrs': MagicMock(return_value=self.ip_ret),
|
||||
'foo.bar': MagicMock(return_value=self.foo_ret),
|
||||
}):
|
||||
ret = mine.update(mine_functions=manual_mine_functions)
|
||||
self.assertEqual(ret, 'FakeCache:StoreSuccess!')
|
||||
|
@ -352,16 +337,10 @@ class MineTestCase(TestCase, LoaderModuleMockMixin):
|
|||
self.assertEqual(
|
||||
self.cache.fetch('minions/webserver', 'mine_cache'),
|
||||
{
|
||||
'ip_addr': {
|
||||
salt.utils.mine.MINE_ITEM_ACL_DATA: '2001:db8::1:4',
|
||||
salt.utils.mine.MINE_ITEM_ACL_ID: salt.utils.mine.MINE_ITEM_ACL_VERSION,
|
||||
},
|
||||
'network.ip_addrs': {
|
||||
salt.utils.mine.MINE_ITEM_ACL_DATA: '2001:db8::1:4',
|
||||
salt.utils.mine.MINE_ITEM_ACL_ID: salt.utils.mine.MINE_ITEM_ACL_VERSION,
|
||||
},
|
||||
'ip_addr': self.ip_ret,
|
||||
'network.ip_addrs': self.ip_ret,
|
||||
'foo.bar': {
|
||||
salt.utils.mine.MINE_ITEM_ACL_DATA: 'baz',
|
||||
salt.utils.mine.MINE_ITEM_ACL_DATA: self.foo_ret,
|
||||
salt.utils.mine.MINE_ITEM_ACL_ID: salt.utils.mine.MINE_ITEM_ACL_VERSION,
|
||||
'allow_tgt': 'G@roles:webserver',
|
||||
'allow_tgt_type': 'compound',
|
||||
|
@ -388,22 +367,10 @@ class MineTestCase(TestCase, LoaderModuleMockMixin):
|
|||
'id': 'webserver',
|
||||
'cmd': '_mine',
|
||||
'data': {
|
||||
'ip_addr': {
|
||||
salt.utils.mine.MINE_ITEM_ACL_DATA: '2001:db8::1:3',
|
||||
salt.utils.mine.MINE_ITEM_ACL_ID: salt.utils.mine.MINE_ITEM_ACL_VERSION,
|
||||
},
|
||||
'network.ip_addrs': {
|
||||
salt.utils.mine.MINE_ITEM_ACL_DATA: '2001:db8::1:3',
|
||||
salt.utils.mine.MINE_ITEM_ACL_ID: salt.utils.mine.MINE_ITEM_ACL_VERSION,
|
||||
},
|
||||
'foo.bar': {
|
||||
salt.utils.mine.MINE_ITEM_ACL_DATA: 'baz',
|
||||
salt.utils.mine.MINE_ITEM_ACL_ID: salt.utils.mine.MINE_ITEM_ACL_VERSION,
|
||||
},
|
||||
'kernel': {
|
||||
salt.utils.mine.MINE_ITEM_ACL_DATA: 'Linux!',
|
||||
salt.utils.mine.MINE_ITEM_ACL_ID: salt.utils.mine.MINE_ITEM_ACL_VERSION,
|
||||
},
|
||||
'ip_addr': self.ip_ret,
|
||||
'network.ip_addrs': self.ip_ret,
|
||||
'foo.bar': self.foo_ret,
|
||||
'kernel': self.kernel_ret,
|
||||
},
|
||||
'clear': False,
|
||||
}
|
||||
|
@ -415,9 +382,9 @@ class MineTestCase(TestCase, LoaderModuleMockMixin):
|
|||
}), \
|
||||
patch.dict(mine.__salt__, {
|
||||
'config.merge': MagicMock(return_value=config_mine_functions),
|
||||
'grains.get': lambda x: 'Linux!',
|
||||
'network.ip_addrs': MagicMock(return_value='2001:db8::1:3'),
|
||||
'foo.bar': MagicMock(return_value='baz'),
|
||||
'grains.get': lambda x: self.kernel_ret,
|
||||
'network.ip_addrs': MagicMock(return_value=self.ip_ret),
|
||||
'foo.bar': MagicMock(return_value=self.foo_ret),
|
||||
}):
|
||||
# Verify the correct load
|
||||
self.assertEqual(
|
||||
|
|
|
@ -14,6 +14,7 @@ from tests.support.runtests import RUNTIME_VARS
|
|||
import tests.support.helpers
|
||||
|
||||
# Import Salt libs
|
||||
import salt
|
||||
import salt.ext.six
|
||||
import salt.modules.cmdmod
|
||||
import salt.utils.platform
|
||||
|
@ -95,3 +96,10 @@ class VendorTornadoTest(TestCase):
|
|||
log.error("Test found bad line: %s", line)
|
||||
valid_lines.append(line)
|
||||
assert valid_lines == [], len(valid_lines)
|
||||
|
||||
def test_regression_56063(self):
|
||||
importer = salt.TornadoImporter()
|
||||
try:
|
||||
importer.find_module('tornado')
|
||||
except TypeError:
|
||||
assert False, 'TornadoImporter raised type error when one argument passed'
|
||||
|
|
|
@ -134,8 +134,9 @@ class BadTestModuleNamesTestCase(TestCase):
|
|||
'integration.logging.handlers.test_logstash_mod',
|
||||
'integration.master.test_event_return',
|
||||
'integration.minion.test_blackout',
|
||||
'integration.minion.test_pillar',
|
||||
'integration.minion.test_executor',
|
||||
'integration.minion.test_minion_cache',
|
||||
'integration.minion.test_pillar',
|
||||
'integration.minion.test_timeout',
|
||||
'integration.modules.test_decorators',
|
||||
'integration.modules.test_pkg',
|
||||
|
|
|
@ -37,8 +37,11 @@ class VersionTestCase(TestCase):
|
|||
('v2014.1.4.1', (2014, 1, 4, 1, '', 0, 0, None), None),
|
||||
('v2014.1.4.1rc3-n/a-abcdefff', (2014, 1, 4, 1, 'rc', 3, -1, 'abcdefff'), None),
|
||||
('v3.4.1.1', (3, 4, 1, 1, '', 0, 0, None), None),
|
||||
('v3000', (3000, None, None, 0, '', 0, 0, None), '3000'),
|
||||
('v3000rc1', (3000, None, None, 0, 'rc', 1, 0, None), '3000rc1'),
|
||||
('v3000', (3000, '', 0, 0, None), '3000'),
|
||||
('v3000.0', (3000, '', 0, 0, None), '3000'),
|
||||
('v4518.1', (4518, 1, '', 0, 0, None), '4518.1'),
|
||||
('v3000rc1', (3000, 'rc', 1, 0, None), '3000rc1'),
|
||||
('v3000rc1-n/a-abcdefff', (3000, 'rc', 1, -1, 'abcdefff'), None),
|
||||
|
||||
)
|
||||
|
||||
|
@ -76,6 +79,9 @@ class VersionTestCase(TestCase):
|
|||
# version scheme in the future
|
||||
# but still adding test for it
|
||||
('v3000', 'v3000.0rc1'),
|
||||
('v3000.1rc1', 'v3000.0rc1'),
|
||||
('v3000', 'v2019.2.1rc1'),
|
||||
('v3001rc1', 'v2019.2.1rc1'),
|
||||
)
|
||||
for higher_version, lower_version in examples:
|
||||
self.assertTrue(SaltStackVersion.parse(higher_version) > lower_version)
|
||||
|
@ -154,6 +160,45 @@ class VersionTestCase(TestCase):
|
|||
assert ver.bugfix == 0
|
||||
assert ver.string == '{0}.{1}.0'.format(maj_ver, min_ver)
|
||||
|
||||
def test_noc_info(self):
|
||||
'''
|
||||
Test noc_info property method
|
||||
'''
|
||||
expect = (
|
||||
('v2014.1.4.1rc3-n/a-abcdefff', (2014, 1, 4, 1, 'rc', 3, -1)),
|
||||
('v3.4.1.1', (3, 4, 1, 1, '', 0, 0)),
|
||||
('v3000', (3000, '', 0, 0)),
|
||||
('v3000.0', (3000, '', 0, 0)),
|
||||
('v4518.1', (4518, 1, '', 0, 0)),
|
||||
('v3000rc1', (3000, 'rc', 1, 0)),
|
||||
('v3000rc1-n/a-abcdefff', (3000, 'rc', 1, -1)),
|
||||
)
|
||||
|
||||
for vstr, noc_info in expect:
|
||||
saltstack_version = SaltStackVersion.parse(vstr)
|
||||
assert saltstack_version.noc_info, noc_info
|
||||
assert len(saltstack_version.noc_info) == len(noc_info)
|
||||
|
||||
def test_full_info(self):
|
||||
'''
|
||||
Test full_Info property method
|
||||
'''
|
||||
expect = (
|
||||
('v2014.1.4.1rc3-n/a-abcdefff', (2014, 1, 4, 1, 'rc', 3, -1, 'abcdefff')),
|
||||
('v3.4.1.1', (3, 4, 1, 1, '', 0, 0, None)),
|
||||
('v3000', (3000, '', 0, 0, None)),
|
||||
('v3000.0', (3000, '', 0, 0, None)),
|
||||
('v4518.1', (4518, 1, '', 0, 0, None)),
|
||||
('v3000rc1', (3000, 'rc', 1, 0, None)),
|
||||
('v3000rc1-n/a-abcdefff', (3000, 'rc', 1, -1, 'abcdefff')),
|
||||
|
||||
)
|
||||
|
||||
for vstr, full_info in expect:
|
||||
saltstack_version = SaltStackVersion.parse(vstr)
|
||||
assert saltstack_version.full_info, full_info
|
||||
assert len(saltstack_version.full_info) == len(full_info)
|
||||
|
||||
def test_discover_version(self):
|
||||
'''
|
||||
Test call to __discover_version
|
||||
|
@ -180,3 +225,31 @@ class VersionTestCase(TestCase):
|
|||
with proc_mock, patch_os:
|
||||
ret = getattr(salt.version, '__discover_version')(salt_ver)
|
||||
assert ret == exp
|
||||
|
||||
def test_info_new_version(self):
|
||||
'''
|
||||
test info property method with new versioning scheme
|
||||
'''
|
||||
vers = ((3000, None, None),
|
||||
(3000, 1, None),
|
||||
(3001, 0, None))
|
||||
for maj_ver, min_ver, bug_fix in vers:
|
||||
ver = SaltStackVersion(major=maj_ver, minor=min_ver, bugfix=bug_fix)
|
||||
if min_ver:
|
||||
assert ver.info == (maj_ver, min_ver)
|
||||
else:
|
||||
assert ver.info == (maj_ver,)
|
||||
|
||||
def test_info_old_version(self):
|
||||
'''
|
||||
test info property method with old versioning scheme
|
||||
'''
|
||||
vers = ((2019, 2, 1),
|
||||
(2018, 3, 0),
|
||||
(2017, 7, None))
|
||||
for maj_ver, min_ver, bug_fix in vers:
|
||||
ver = SaltStackVersion(major=maj_ver, minor=min_ver, bugfix=bug_fix)
|
||||
if bug_fix is None:
|
||||
assert ver.info == (maj_ver, min_ver, 0, 0)
|
||||
else:
|
||||
assert ver.info == (maj_ver, min_ver, bug_fix, 0)
|
||||
|
|
Loading…
Add table
Reference in a new issue