Merge pull request #43188 from alexbleotu/ext_pillar_config_param-gh

Ext pillar config param gh
This commit is contained in:
Mike Place 2017-09-11 12:58:01 -06:00 committed by GitHub
commit 0e42e8f9cc
5 changed files with 562 additions and 18 deletions

View File

@ -2113,6 +2113,41 @@ It will be interpreted as megabytes.
file_recv_max_size: 100
.. conf_minion:: pass_to_ext_pillars
``pass_to_ext_pillars``
-----------------------
Specify a list of configuration keys whose values are to be passed to
external pillar functions.
Suboptions can be specified using the ':' notation (i.e. ``option:suboption``)
The values are merged and included in the ``extra_minion_data`` optional
parameter of the external pillar function. The ``extra_minion_data`` parameter
is passed only to the external pillar functions that have it explicitly
specified in their definition.
If the config contains
.. code-block:: yaml
opt1: value1
opt2:
subopt1: value2
subopt2: value3
pass_to_ext_pillars:
- opt1
- opt2: subopt1
the ``extra_minion_data`` parameter will be
.. code-block:: python
{'opt1': 'value1',
'opt2': {'subopt1': 'value2'}}
Security Settings
=================

View File

@ -1080,6 +1080,11 @@ VALID_OPTS = {
# (in other words, require that minions have 'minion_sign_messages'
# turned on)
'require_minion_sign_messages': bool,
# The list of config entries to be passed to external pillar function as
# part of the extra_minion_data param
# Subconfig entries can be specified by using the ':' notation (e.g. key:subkey)
'pass_to_ext_pillars': (six.string_types, list),
}
# default configurations

View File

@ -1311,7 +1311,8 @@ class AESFuncs(object):
load.get(u'saltenv', load.get(u'env')),
ext=load.get(u'ext'),
pillar_override=load.get(u'pillar_override', {}),
pillarenv=load.get(u'pillarenv'))
pillarenv=load.get(u'pillarenv'),
extra_minion_data=load.get(u'extra_minion_data'))
data = pillar.compile_pillar()
self.fs_.update_opts()
if self.opts.get(u'minion_data_cache', False):

View File

@ -13,6 +13,7 @@ import logging
import tornado.gen
import sys
import traceback
import inspect
# Import salt libs
import salt.loader
@ -23,6 +24,8 @@ import salt.transport
import salt.utils.url
import salt.utils.cache
import salt.utils.crypt
import salt.utils.dictupdate
import salt.utils.args
from salt.exceptions import SaltClientError
from salt.template import compile_template
from salt.utils.dictupdate import merge
@ -36,7 +39,7 @@ log = logging.getLogger(__name__)
def get_pillar(opts, grains, minion_id, saltenv=None, ext=None, funcs=None,
pillar_override=None, pillarenv=None):
pillar_override=None, pillarenv=None, extra_minion_data=None):
'''
Return the correct pillar driver based on the file_client option
'''
@ -55,12 +58,14 @@ def get_pillar(opts, grains, minion_id, saltenv=None, ext=None, funcs=None,
return PillarCache(opts, grains, minion_id, saltenv, ext=ext, functions=funcs,
pillar_override=pillar_override, pillarenv=pillarenv)
return ptype(opts, grains, minion_id, saltenv, ext, functions=funcs,
pillar_override=pillar_override, pillarenv=pillarenv)
pillar_override=pillar_override, pillarenv=pillarenv,
extra_minion_data=extra_minion_data)
# TODO: migrate everyone to this one!
def get_async_pillar(opts, grains, minion_id, saltenv=None, ext=None, funcs=None,
pillar_override=None, pillarenv=None):
pillar_override=None, pillarenv=None,
extra_minion_data=None):
'''
Return the correct pillar driver based on the file_client option
'''
@ -72,15 +77,62 @@ def get_async_pillar(opts, grains, minion_id, saltenv=None, ext=None, funcs=None
'local': AsyncPillar,
}.get(file_client, AsyncPillar)
return ptype(opts, grains, minion_id, saltenv, ext, functions=funcs,
pillar_override=pillar_override, pillarenv=pillarenv)
pillar_override=pillar_override, pillarenv=pillarenv,
extra_minion_data=extra_minion_data)
class AsyncRemotePillar(object):
class RemotePillarMixin(object):
'''
Common remote pillar functionality
'''
def get_ext_pillar_extra_minion_data(self, opts):
'''
Returns the extra data from the minion's opts dict (the config file).
This data will be passed to external pillar functions.
'''
def get_subconfig(opts_key):
'''
Returns a dict containing the opts key subtree, while maintaining
the opts structure
'''
ret_dict = aux_dict = {}
config_val = opts
subkeys = opts_key.split(':')
# Build an empty dict with the opts path
for subkey in subkeys[:-1]:
aux_dict[subkey] = {}
aux_dict = aux_dict[subkey]
if not config_val.get(subkey):
# The subkey is not in the config
return {}
config_val = config_val[subkey]
if subkeys[-1] not in config_val:
return {}
aux_dict[subkeys[-1]] = config_val[subkeys[-1]]
return ret_dict
extra_data = {}
if 'pass_to_ext_pillars' in opts:
if not isinstance(opts['pass_to_ext_pillars'], list):
log.exception('\'pass_to_ext_pillars\' config is malformed.')
raise SaltClientError('\'pass_to_ext_pillars\' config is '
'malformed.')
for key in opts['pass_to_ext_pillars']:
salt.utils.dictupdate.update(extra_data,
get_subconfig(key),
recursive_update=True,
merge_lists=True)
log.trace('ext_pillar_extra_data = {0}'.format(extra_data))
return extra_data
class AsyncRemotePillar(RemotePillarMixin):
'''
Get the pillar from the master
'''
def __init__(self, opts, grains, minion_id, saltenv, ext=None, functions=None,
pillar_override=None, pillarenv=None):
pillar_override=None, pillarenv=None, extra_minion_data=None):
self.opts = opts
self.opts['environment'] = saltenv
self.ext = ext
@ -93,6 +145,14 @@ class AsyncRemotePillar(object):
if not isinstance(self.pillar_override, dict):
self.pillar_override = {}
log.error('Pillar data must be a dictionary')
self.extra_minion_data = extra_minion_data or {}
if not isinstance(self.extra_minion_data, dict):
self.extra_minion_data = {}
log.error('Extra minion data must be a dictionary')
salt.utils.dictupdate.update(self.extra_minion_data,
self.get_ext_pillar_extra_minion_data(opts),
recursive_update=True,
merge_lists=True)
@tornado.gen.coroutine
def compile_pillar(self):
@ -104,6 +164,7 @@ class AsyncRemotePillar(object):
'saltenv': self.opts['environment'],
'pillarenv': self.opts['pillarenv'],
'pillar_override': self.pillar_override,
'extra_minion_data': self.extra_minion_data,
'ver': '2',
'cmd': '_pillar'}
if self.ext:
@ -126,12 +187,12 @@ class AsyncRemotePillar(object):
raise tornado.gen.Return(ret_pillar)
class RemotePillar(object):
class RemotePillar(RemotePillarMixin):
'''
Get the pillar from the master
'''
def __init__(self, opts, grains, minion_id, saltenv, ext=None, functions=None,
pillar_override=None, pillarenv=None):
pillar_override=None, pillarenv=None, extra_minion_data=None):
self.opts = opts
self.opts['environment'] = saltenv
self.ext = ext
@ -144,6 +205,14 @@ class RemotePillar(object):
if not isinstance(self.pillar_override, dict):
self.pillar_override = {}
log.error('Pillar data must be a dictionary')
self.extra_minion_data = extra_minion_data or {}
if not isinstance(self.extra_minion_data, dict):
self.extra_minion_data = {}
log.error('Extra minion data must be a dictionary')
salt.utils.dictupdate.update(self.extra_minion_data,
self.get_ext_pillar_extra_minion_data(opts),
recursive_update=True,
merge_lists=True)
def compile_pillar(self):
'''
@ -154,6 +223,7 @@ class RemotePillar(object):
'saltenv': self.opts['environment'],
'pillarenv': self.opts['pillarenv'],
'pillar_override': self.pillar_override,
'extra_minion_data': self.extra_minion_data,
'ver': '2',
'cmd': '_pillar'}
if self.ext:
@ -187,7 +257,7 @@ class PillarCache(object):
'''
# TODO ABC?
def __init__(self, opts, grains, minion_id, saltenv, ext=None, functions=None,
pillar_override=None, pillarenv=None):
pillar_override=None, pillarenv=None, extra_minion_data=None):
# Yes, we need all of these because we need to route to the Pillar object
# if we have no cache. This is another refactor target.
@ -265,7 +335,7 @@ class Pillar(object):
Read over the pillar top files and render the pillar data
'''
def __init__(self, opts, grains, minion_id, saltenv, ext=None, functions=None,
pillar_override=None, pillarenv=None):
pillar_override=None, pillarenv=None, extra_minion_data=None):
self.minion_id = minion_id
self.ext = ext
if pillarenv is None:
@ -311,6 +381,10 @@ class Pillar(object):
if not isinstance(self.pillar_override, dict):
self.pillar_override = {}
log.error('Pillar data must be a dictionary')
self.extra_minion_data = extra_minion_data or {}
if not isinstance(self.extra_minion_data, dict):
self.extra_minion_data = {}
log.error('Extra minion data must be a dictionary')
def __valid_on_demand_ext_pillar(self, opts):
'''
@ -768,17 +842,35 @@ class Pillar(object):
Builds actual pillar data structure and updates the ``pillar`` variable
'''
ext = None
args = salt.utils.args.get_function_argspec(self.ext_pillars[key]).args
if isinstance(val, dict):
ext = self.ext_pillars[key](self.minion_id, pillar, **val)
if ('extra_minion_data' in args) and self.extra_minion_data:
ext = self.ext_pillars[key](
self.minion_id, pillar,
extra_minion_data=self.extra_minion_data, **val)
else:
ext = self.ext_pillars[key](self.minion_id, pillar, **val)
elif isinstance(val, list):
ext = self.ext_pillars[key](self.minion_id,
pillar,
*val)
if ('extra_minion_data' in args) and self.extra_minion_data:
ext = self.ext_pillars[key](
self.minion_id, pillar, *val,
extra_minion_data=self.extra_minion_data)
else:
ext = self.ext_pillars[key](self.minion_id,
pillar,
*val)
else:
ext = self.ext_pillars[key](self.minion_id,
pillar,
val)
if ('extra_minion_data' in args) and self.extra_minion_data:
ext = self.ext_pillars[key](
self.minion_id,
pillar,
val,
extra_minion_data=self.extra_minion_data)
else:
ext = self.ext_pillars[key](self.minion_id,
pillar,
val)
return ext
def ext_pillar(self, pillar, errors=None):

View File

@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
'''
:codeauthor: :email:`Pedro Algarvio (pedro@algarvio.me)`
:codeauthor: :email:`Alexandru Bleotu (alexandru.bleotu@morganstanley.com)`
tests.unit.pillar_test
@ -19,6 +20,7 @@ from tests.support.paths import TMP
# Import salt libs
import salt.pillar
import salt.utils.stringutils
import salt.exceptions
@skipIf(NO_MOCK, NO_MOCK_REASON)
@ -56,6 +58,244 @@ class PillarTestCase(TestCase):
self.assertEqual(pillar.opts['environment'], 'dev')
self.assertEqual(pillar.opts['pillarenv'], 'dev')
def test_ext_pillar_no_extra_minion_data_val_dict(self):
opts = {
'renderer': 'json',
'renderer_blacklist': [],
'renderer_whitelist': [],
'state_top': '',
'pillar_roots': {
'dev': [],
'base': []
},
'file_roots': {
'dev': [],
'base': []
},
'extension_modules': '',
'pillarenv_from_saltenv': True
}
mock_ext_pillar_func = MagicMock()
with patch('salt.loader.pillars',
MagicMock(return_value={'fake_ext_pillar':
mock_ext_pillar_func})):
pillar = salt.pillar.Pillar(opts, {}, 'mocked-minion', 'dev')
# ext pillar function doesn't have the extra_minion_data arg
with patch('salt.utils.args.get_function_argspec',
MagicMock(return_value=MagicMock(args=[]))):
pillar._external_pillar_data('fake_pillar', {'arg': 'foo'},
'fake_ext_pillar')
mock_ext_pillar_func.assert_called_once_with('mocked-minion',
'fake_pillar',
arg='foo')
# ext pillar function has the extra_minion_data arg
mock_ext_pillar_func.reset_mock()
with patch('salt.utils.args.get_function_argspec',
MagicMock(return_value=MagicMock(args=['extra_minion_data']))):
pillar._external_pillar_data('fake_pillar', {'arg': 'foo'},
'fake_ext_pillar')
mock_ext_pillar_func.assert_called_once_with('mocked-minion',
'fake_pillar',
arg='foo')
def test_ext_pillar_no_extra_minion_data_val_list(self):
opts = {
'renderer': 'json',
'renderer_blacklist': [],
'renderer_whitelist': [],
'state_top': '',
'pillar_roots': {
'dev': [],
'base': []
},
'file_roots': {
'dev': [],
'base': []
},
'extension_modules': '',
'pillarenv_from_saltenv': True
}
mock_ext_pillar_func = MagicMock()
with patch('salt.loader.pillars',
MagicMock(return_value={'fake_ext_pillar':
mock_ext_pillar_func})):
pillar = salt.pillar.Pillar(opts, {}, 'mocked-minion', 'dev')
# ext pillar function doesn't have the extra_minion_data arg
with patch('salt.utils.args.get_function_argspec',
MagicMock(return_value=MagicMock(args=[]))):
pillar._external_pillar_data('fake_pillar', ['foo'],
'fake_ext_pillar')
mock_ext_pillar_func.assert_called_once_with('mocked-minion',
'fake_pillar',
'foo')
# ext pillar function has the extra_minion_data arg
mock_ext_pillar_func.reset_mock()
with patch('salt.utils.args.get_function_argspec',
MagicMock(return_value=MagicMock(args=['extra_minion_data']))):
pillar._external_pillar_data('fake_pillar', ['foo'],
'fake_ext_pillar')
mock_ext_pillar_func.assert_called_once_with('mocked-minion',
'fake_pillar',
'foo')
def test_ext_pillar_no_extra_minion_data_val_elem(self):
opts = {
'renderer': 'json',
'renderer_blacklist': [],
'renderer_whitelist': [],
'state_top': '',
'pillar_roots': {
'dev': [],
'base': []
},
'file_roots': {
'dev': [],
'base': []
},
'extension_modules': '',
'pillarenv_from_saltenv': True
}
mock_ext_pillar_func = MagicMock()
with patch('salt.loader.pillars',
MagicMock(return_value={'fake_ext_pillar':
mock_ext_pillar_func})):
pillar = salt.pillar.Pillar(opts, {}, 'mocked-minion', 'dev')
# ext pillar function doesn't have the extra_minion_data arg
with patch('salt.utils.args.get_function_argspec',
MagicMock(return_value=MagicMock(args=[]))):
pillar._external_pillar_data('fake_pillar', 'fake_val',
'fake_ext_pillar')
mock_ext_pillar_func.assert_called_once_with('mocked-minion',
'fake_pillar', 'fake_val')
# ext pillar function has the extra_minion_data arg
mock_ext_pillar_func.reset_mock()
with patch('salt.utils.args.get_function_argspec',
MagicMock(return_value=MagicMock(args=['extra_minion_data']))):
pillar._external_pillar_data('fake_pillar', 'fake_val',
'fake_ext_pillar')
mock_ext_pillar_func.assert_called_once_with('mocked-minion',
'fake_pillar', 'fake_val')
def test_ext_pillar_with_extra_minion_data_val_dict(self):
opts = {
'renderer': 'json',
'renderer_blacklist': [],
'renderer_whitelist': [],
'state_top': '',
'pillar_roots': {
'dev': [],
'base': []
},
'file_roots': {
'dev': [],
'base': []
},
'extension_modules': '',
'pillarenv_from_saltenv': True
}
mock_ext_pillar_func = MagicMock()
with patch('salt.loader.pillars',
MagicMock(return_value={'fake_ext_pillar':
mock_ext_pillar_func})):
pillar = salt.pillar.Pillar(opts, {}, 'mocked-minion', 'dev',
extra_minion_data={'fake_key': 'foo'})
# ext pillar function doesn't have the extra_minion_data arg
with patch('salt.utils.args.get_function_argspec',
MagicMock(return_value=MagicMock(args=[]))):
pillar._external_pillar_data('fake_pillar', {'arg': 'foo'},
'fake_ext_pillar')
mock_ext_pillar_func.assert_called_once_with(
'mocked-minion', 'fake_pillar', arg='foo')
# ext pillar function has the extra_minion_data arg
mock_ext_pillar_func.reset_mock()
with patch('salt.utils.args.get_function_argspec',
MagicMock(return_value=MagicMock(args=['extra_minion_data']))):
pillar._external_pillar_data('fake_pillar', {'arg': 'foo'},
'fake_ext_pillar')
mock_ext_pillar_func.assert_called_once_with(
'mocked-minion', 'fake_pillar', arg='foo',
extra_minion_data={'fake_key': 'foo'})
def test_ext_pillar_with_extra_minion_data_val_list(self):
opts = {
'renderer': 'json',
'renderer_blacklist': [],
'renderer_whitelist': [],
'state_top': '',
'pillar_roots': {
'dev': [],
'base': []
},
'file_roots': {
'dev': [],
'base': []
},
'extension_modules': '',
'pillarenv_from_saltenv': True
}
mock_ext_pillar_func = MagicMock()
with patch('salt.loader.pillars',
MagicMock(return_value={'fake_ext_pillar':
mock_ext_pillar_func})):
pillar = salt.pillar.Pillar(opts, {}, 'mocked-minion', 'dev',
extra_minion_data={'fake_key': 'foo'})
# ext pillar function doesn't have the extra_minion_data arg
with patch('salt.utils.args.get_function_argspec',
MagicMock(return_value=MagicMock(args=[]))):
pillar._external_pillar_data('fake_pillar', ['bar'],
'fake_ext_pillar')
mock_ext_pillar_func.assert_called_once_with(
'mocked-minion', 'fake_pillar', 'bar')
# ext pillar function has the extra_minion_data arg
mock_ext_pillar_func.reset_mock()
with patch('salt.utils.args.get_function_argspec',
MagicMock(return_value=MagicMock(args=['extra_minion_data']))):
pillar._external_pillar_data('fake_pillar', ['bar'],
'fake_ext_pillar')
mock_ext_pillar_func.assert_called_once_with(
'mocked-minion', 'fake_pillar', 'bar',
extra_minion_data={'fake_key': 'foo'})
def test_ext_pillar_with_extra_minion_data_val_elem(self):
opts = {
'renderer': 'json',
'renderer_blacklist': [],
'renderer_whitelist': [],
'state_top': '',
'pillar_roots': {
'dev': [],
'base': []
},
'file_roots': {
'dev': [],
'base': []
},
'extension_modules': '',
'pillarenv_from_saltenv': True
}
mock_ext_pillar_func = MagicMock()
with patch('salt.loader.pillars',
MagicMock(return_value={'fake_ext_pillar':
mock_ext_pillar_func})):
pillar = salt.pillar.Pillar(opts, {}, 'mocked-minion', 'dev',
extra_minion_data={'fake_key': 'foo'})
# ext pillar function doesn't have the extra_minion_data arg
with patch('salt.utils.args.get_function_argspec',
MagicMock(return_value=MagicMock(args=[]))):
pillar._external_pillar_data('fake_pillar', 'bar',
'fake_ext_pillar')
mock_ext_pillar_func.assert_called_once_with(
'mocked-minion', 'fake_pillar', 'bar')
# ext pillar function has the extra_minion_data arg
mock_ext_pillar_func.reset_mock()
with patch('salt.utils.args.get_function_argspec',
MagicMock(return_value=MagicMock(args=['extra_minion_data']))):
pillar._external_pillar_data('fake_pillar', 'bar',
'fake_ext_pillar')
mock_ext_pillar_func.assert_called_once_with(
'mocked-minion', 'fake_pillar', 'bar',
extra_minion_data={'fake_key': 'foo'})
def test_malformed_pillar_sls(self):
with patch('salt.pillar.compile_template') as compile_template:
opts = {
@ -318,3 +558,174 @@ p2:
}[sls]
client.get_state.side_effect = get_state
@skipIf(NO_MOCK, NO_MOCK_REASON)
@patch('salt.transport.Channel.factory', MagicMock())
class RemotePillarTestCase(TestCase):
'''
Tests for instantiating a RemotePillar in salt.pillar
'''
def setUp(self):
self.grains = {}
def tearDown(self):
for attr in ('grains',):
try:
delattr(self, attr)
except AttributeError:
continue
def test_get_opts_in_pillar_override_call(self):
mock_get_extra_minion_data = MagicMock(return_value={})
with patch(
'salt.pillar.RemotePillarMixin.get_ext_pillar_extra_minion_data',
mock_get_extra_minion_data):
salt.pillar.RemotePillar({}, self.grains, 'mocked-minion', 'dev')
mock_get_extra_minion_data.assert_called_once_with(
{'environment': 'dev'})
def test_multiple_keys_in_opts_added_to_pillar(self):
opts = {
'renderer': 'json',
'path_to_add': 'fake_data',
'path_to_add2': {'fake_data2': ['fake_data3', 'fake_data4']},
'pass_to_ext_pillars': ['path_to_add', 'path_to_add2']
}
pillar = salt.pillar.RemotePillar(opts, self.grains,
'mocked-minion', 'dev')
self.assertEqual(pillar.extra_minion_data,
{'path_to_add': 'fake_data',
'path_to_add2': {'fake_data2': ['fake_data3',
'fake_data4']}})
def test_subkey_in_opts_added_to_pillar(self):
opts = {
'renderer': 'json',
'path_to_add': 'fake_data',
'path_to_add2': {'fake_data5': 'fake_data6',
'fake_data2': ['fake_data3', 'fake_data4']},
'pass_to_ext_pillars': ['path_to_add2:fake_data5']
}
pillar = salt.pillar.RemotePillar(opts, self.grains,
'mocked-minion', 'dev')
self.assertEqual(pillar.extra_minion_data,
{'path_to_add2': {'fake_data5': 'fake_data6'}})
def test_non_existent_leaf_opt_in_add_to_pillar(self):
opts = {
'renderer': 'json',
'path_to_add': 'fake_data',
'path_to_add2': {'fake_data5': 'fake_data6',
'fake_data2': ['fake_data3', 'fake_data4']},
'pass_to_ext_pillars': ['path_to_add2:fake_data_non_exist']
}
pillar = salt.pillar.RemotePillar(opts, self.grains,
'mocked-minion', 'dev')
self.assertEqual(pillar.pillar_override, {})
def test_non_existent_intermediate_opt_in_add_to_pillar(self):
opts = {
'renderer': 'json',
'path_to_add': 'fake_data',
'path_to_add2': {'fake_data5': 'fake_data6',
'fake_data2': ['fake_data3', 'fake_data4']},
'pass_to_ext_pillars': ['path_to_add_no_exist']
}
pillar = salt.pillar.RemotePillar(opts, self.grains,
'mocked-minion', 'dev')
self.assertEqual(pillar.pillar_override, {})
def test_malformed_add_to_pillar(self):
opts = {
'renderer': 'json',
'path_to_add': 'fake_data',
'path_to_add2': {'fake_data5': 'fake_data6',
'fake_data2': ['fake_data3', 'fake_data4']},
'pass_to_ext_pillars': MagicMock()
}
with self.assertRaises(salt.exceptions.SaltClientError) as excinfo:
salt.pillar.RemotePillar(opts, self.grains, 'mocked-minion', 'dev')
self.assertEqual(excinfo.exception.strerror,
'\'pass_to_ext_pillars\' config is malformed.')
def test_pillar_send_extra_minion_data_from_config(self):
opts = {
'renderer': 'json',
'pillarenv': 'fake_pillar_env',
'path_to_add': 'fake_data',
'path_to_add2': {'fake_data5': 'fake_data6',
'fake_data2': ['fake_data3', 'fake_data4']},
'pass_to_ext_pillars': ['path_to_add']}
mock_channel = MagicMock(
crypted_transfer_decode_dictentry=MagicMock(return_value={}))
with patch('salt.transport.Channel.factory',
MagicMock(return_value=mock_channel)):
pillar = salt.pillar.RemotePillar(opts, self.grains,
'mocked_minion', 'fake_env')
ret = pillar.compile_pillar()
self.assertEqual(pillar.channel, mock_channel)
mock_channel.crypted_transfer_decode_dictentry.assert_called_once_with(
{'cmd': '_pillar', 'ver': '2',
'id': 'mocked_minion',
'grains': {},
'saltenv': 'fake_env',
'pillarenv': 'fake_pillar_env',
'pillar_override': {},
'extra_minion_data': {'path_to_add': 'fake_data'}},
dictkey='pillar')
@skipIf(NO_MOCK, NO_MOCK_REASON)
@patch('salt.transport.client.AsyncReqChannel.factory', MagicMock())
class AsyncRemotePillarTestCase(TestCase):
'''
Tests for instantiating a AsyncRemotePillar in salt.pillar
'''
def setUp(self):
self.grains = {}
def tearDown(self):
for attr in ('grains',):
try:
delattr(self, attr)
except AttributeError:
continue
def test_get_opts_in_pillar_override_call(self):
mock_get_extra_minion_data = MagicMock(return_value={})
with patch(
'salt.pillar.RemotePillarMixin.get_ext_pillar_extra_minion_data',
mock_get_extra_minion_data):
salt.pillar.RemotePillar({}, self.grains, 'mocked-minion', 'dev')
mock_get_extra_minion_data.assert_called_once_with(
{'environment': 'dev'})
def test_pillar_send_extra_minion_data_from_config(self):
opts = {
'renderer': 'json',
'pillarenv': 'fake_pillar_env',
'path_to_add': 'fake_data',
'path_to_add2': {'fake_data5': 'fake_data6',
'fake_data2': ['fake_data3', 'fake_data4']},
'pass_to_ext_pillars': ['path_to_add']}
mock_channel = MagicMock(
crypted_transfer_decode_dictentry=MagicMock(return_value={}))
with patch('salt.transport.client.AsyncReqChannel.factory',
MagicMock(return_value=mock_channel)):
pillar = salt.pillar.RemotePillar(opts, self.grains,
'mocked_minion', 'fake_env')
ret = pillar.compile_pillar()
mock_channel.crypted_transfer_decode_dictentry.assert_called_once_with(
{'cmd': '_pillar', 'ver': '2',
'id': 'mocked_minion',
'grains': {},
'saltenv': 'fake_env',
'pillarenv': 'fake_pillar_env',
'pillar_override': {},
'extra_minion_data': {'path_to_add': 'fake_data'}},
dictkey='pillar')