Remove recurse_list from pillar_source_merging_strategy and add pillar_merge_list (bool) instead

This commit is contained in:
Sean Jenkins 2015-12-29 10:22:54 -07:00 committed by rallytime
parent d70e6b312a
commit e44a30620b
10 changed files with 57 additions and 109 deletions

View file

@ -614,6 +614,9 @@
# on the "renderer" setting and is the default value.
#pillar_source_merging_strategy: smart
# Recursively merge lists by aggregating them instead of replacing them.
#pillar_merge_lists: True
##### Syndic settings #####
##########################################

View file

@ -2253,35 +2253,6 @@ strategy between different sources. It accepts 4 values:
element2: True
baz: quux
* recurse_list:
it will merge recursively mapping of data similar to ``recurse`` but merge
lists by aggregating them instead of replacing them.
.. code-block:: yaml
foo: 43
bar:
- 1
- 2
.. code-block:: yaml
bar:
- 3
baz: quux
will be merged as:
.. code-block:: yaml
foo: 42
bar:
- 1
- 2
- 3
baz: quux
* aggregate:
instructs aggregation of elements between sources that use the #!yamlex renderer.
@ -2351,6 +2322,21 @@ strategy between different sources. It accepts 4 values:
Guesses the best strategy based on the "renderer" setting.
``pillar_merge_lists``
----------------------------------
.. versionadded:: 2015.8.0
Default: ``True``
Recursively merge lists by aggregating them instead of replacing them.
.. code-block:: yaml
pillar_merge_lists: True
.. conf_master:: pillar_source_merging_strategy
Syndic Server Settings
======================

View file

@ -211,12 +211,14 @@ class Authorize(object):
Gather and create the authorization data sets
'''
auth_data = self.opts['external_auth']
merge_lists = self.opts['pillar_merge_lists']
if 'django' in auth_data and '^model' in auth_data['django']:
auth_from_django = salt.auth.django.retrieve_auth_entries()
auth_data = salt.utils.dictupdate.merge(auth_data,
auth_from_django,
strategy='list')
strategy='list',
merge_lists=merge_lists)
#for auth_back in self.opts.get('external_auth_sources', []):
# fstr = '{0}.perms'.format(auth_back)

View file

@ -505,6 +505,9 @@ VALID_OPTS = {
# encountering duplicate values
'pillar_source_merging_strategy': str,
# Recursively merge lists by aggregating them instead of replacing them.
'pillar_merge_lists': bool,
# How to merge multiple top files from multiple salt environments
# (saltenvs); can be 'merge' or 'same'
'top_file_merging_strategy': str,
@ -1056,6 +1059,7 @@ DEFAULT_MASTER_OPTS = {
'pillar_opts': False,
'pillar_safe_render_error': True,
'pillar_source_merging_strategy': 'smart',
'pillar_merge_lists': True,
'ping_on_rotate': False,
'peer': {},
'preserve_minion_cache': False,

View file

@ -11,6 +11,7 @@ import os
import logging
# Import salt libs
import salt.config
import salt.utils
try:
# Gated for salt-ssh (salt.utils.cloud imports msgpack)
@ -357,10 +358,12 @@ def get(key, default='', delimiter=':', merge=None):
'to \'recurse\'.'.format(merge))
merge = 'recurse'
merge_lists = salt.config.master_config('/etc/salt/master').get('pillar_merge_lists')
data = copy.copy(__pillar__.get('master', {}))
data = salt.utils.dictupdate.merge(data, __pillar__, strategy=merge)
data = salt.utils.dictupdate.merge(data, __grains__, strategy=merge)
data = salt.utils.dictupdate.merge(data, __opts__, strategy=merge)
data = salt.utils.dictupdate.merge(data, __pillar__, strategy=merge, merge_lists=merge_lists)
data = salt.utils.dictupdate.merge(data, __grains__, strategy=merge, merge_lists=merge_lists)
data = salt.utils.dictupdate.merge(data, __opts__, strategy=merge, merge_lists=merge_lists)
ret = salt.utils.traverse_dict_and_list(data,
key,
'_|-',

View file

@ -532,7 +532,8 @@ class Pillar(object):
state,
nstate,
self.merge_strategy,
self.opts.get('renderer', 'yaml'))
self.opts.get('renderer', 'yaml'),
self.opts.get('pillar_merge_lists', 'True'))
if err:
errors += err
@ -569,7 +570,8 @@ class Pillar(object):
pillar,
pstate,
self.merge_strategy,
self.opts.get('renderer', 'yaml'))
self.opts.get('renderer', 'yaml'),
self.opts.get('pillar_merge_lists', 'True'))
return pillar, errors
@ -655,7 +657,8 @@ class Pillar(object):
pillar,
ext,
self.merge_strategy,
self.opts.get('renderer', 'yaml'))
self.opts.get('renderer', 'yaml'),
self.opts.get('pillar_merge_lists', 'True'))
ext = None
return pillar
@ -672,7 +675,8 @@ class Pillar(object):
pillar = merge(pillar,
self.opts['pillar'],
self.merge_strategy,
self.opts.get('renderer', 'yaml'))
self.opts.get('renderer', 'yaml'),
self.opts.get('pillar_merge_lists', 'True'))
else:
matches = self.top_matches(top)
pillar, errors = self.render_pillar(matches)

View file

@ -260,6 +260,10 @@ def ext_pillar(minion_id, repo, pillar_dirs):
'pillar_source_merging_strategy',
'smart'
)
merge_lists = __opts__.get(
'pillar_merge_lists',
'True'
)
for pillar_dir, env in six.iteritems(pillar.pillar_dirs):
log.debug(
'git_pillar is processing pillar SLS from {0} for pillar '
@ -273,7 +277,8 @@ def ext_pillar(minion_id, repo, pillar_dirs):
ret = salt.utils.dictupdate.merge(
ret,
local_pillar.compile_pillar(ext=False),
strategy=merge_strategy
strategy=merge_strategy,
merge_lists=merge_lists
)
return ret

View file

@ -17,7 +17,7 @@ from salt.serializers.yamlex import merge_recursive as _yamlex_merge_recursive
log = logging.getLogger(__name__)
def update(dest, upd, recursive_update=True, merge_lists=False):
def update(dest, upd, recursive_update=True, merge_lists=True):
'''
Recursive version of the default dict.update
@ -76,7 +76,7 @@ def merge_list(obj_a, obj_b):
return ret
def merge_recurse(obj_a, obj_b, merge_lists=False):
def merge_recurse(obj_a, obj_b, merge_lists=True):
copied = copy.deepcopy(obj_a)
return update(copied, obj_b, merge_lists=merge_lists)
@ -85,14 +85,14 @@ def merge_aggregate(obj_a, obj_b):
return _yamlex_merge_recursive(obj_a, obj_b, level=1)
def merge_overwrite(obj_a, obj_b):
def merge_overwrite(obj_a, obj_b, merge_lists=True):
for obj in obj_b:
if obj in obj_a:
obj_a[obj] = obj_b[obj]
return merge_recurse(obj_a, obj_b)
return merge_recurse(obj_a, obj_b, merge_lists=merge_lists)
def merge(obj_a, obj_b, strategy='smart', renderer='yaml'):
def merge(obj_a, obj_b, strategy='smart', renderer='yaml', merge_lists=True):
if strategy == 'smart':
if renderer == 'yamlex' or renderer.startswith('yamlex_'):
strategy = 'aggregate'
@ -102,14 +102,12 @@ def merge(obj_a, obj_b, strategy='smart', renderer='yaml'):
if strategy == 'list':
merged = merge_list(obj_a, obj_b)
elif strategy == 'recurse':
merged = merge_recurse(obj_a, obj_b)
elif strategy == 'recurse_list':
merged = merge_recurse(obj_a, obj_b, merge_lists=True)
merged = merge_recurse(obj_a, obj_b, merge_lists)
elif strategy == 'aggregate':
#: level = 1 merge at least root data
merged = merge_aggregate(obj_a, obj_b)
elif strategy == 'overwrite':
merged = merge_overwrite(obj_a, obj_b)
merged = merge_overwrite(obj_a, obj_b, merge_lists)
else:
log.warning('Unknown merging strategy \'{0}\', '
'fallback to recurse'.format(strategy))

View file

@ -130,38 +130,6 @@ class PillarTestCase(TestCase):
pillar = salt.pillar.Pillar(opts, grains, 'mocked-minion', 'base')
self.assertEqual(pillar.compile_pillar()['ssh'], 'foo')
@patch('salt.pillar.salt.fileclient.get_file_client', autospec=True)
@patch('salt.pillar.salt.minion.Matcher') # autospec=True disabled due to py3 mock bug
def test_pillar_multiple_matches(self, Matcher, get_file_client):
# Uses the ``recurse_list`` strategy.
opts = {
'renderer': 'yaml',
'state_top': '',
'pillar_roots': [],
'extension_modules': '',
'environment': 'base',
'file_roots': [],
'pillar_source_merging_strategy': 'recurse_list',
}
grains = {
'os': 'Ubuntu',
'os_family': 'Debian',
'oscodename': 'raring',
'osfullname': 'Ubuntu',
'osrelease': '13.04',
'kernel': 'Linux'
}
self._setup_test_topfile_mocks(Matcher, get_file_client, 1, 2)
pillar = salt.pillar.Pillar(opts, grains, 'mocked-minion', 'base')
# Pillars should be merged, but only once per pillar file.
self.assertDictEqual(pillar.compile_pillar()['generic'], {
'key1': ['value1', 'value2', 'value3'],
'key2': {
'sub_key1': [],
'sub_key2': [],
}
})
def _setup_test_topfile_mocks(self, Matcher, get_file_client,
nodegroup_order, glob_order):
# Write a simple topfile and two pillar state files
@ -232,33 +200,6 @@ generic:
client.get_state.side_effect = get_state
@patch('salt.pillar.salt.fileclient.get_file_client', autospec=True)
@patch('salt.pillar.salt.minion.Matcher') # autospec=True disabled due to py3 mock bug
def test_pillar_include(self, Matcher, get_file_client):
# Uses the ``recurse_list`` strategy.
opts = {
'renderer': 'yaml',
'state_top': '',
'pillar_roots': [],
'extension_modules': '',
'environment': 'base',
'file_roots': [],
'pillar_source_merging_strategy': 'recurse_list',
}
grains = {
'os': 'Ubuntu',
'os_family': 'Debian',
'oscodename': 'raring',
'osfullname': 'Ubuntu',
'osrelease': '13.04',
'kernel': 'Linux'
}
self._setup_test_include_mocks(Matcher, get_file_client)
pillar = salt.pillar.Pillar(opts, grains, 'mocked-minion', 'base').compile_pillar()
# Both pillar modules should only be loaded once.
self.assertEqual(pillar['p1'], ['value1_3', 'value1_1', 'value1_2'])
self.assertEqual(pillar['p2'], ['value2_1', 'value2_2'])
def _setup_test_include_mocks(self, Matcher, get_file_client):
self.top_file = top_file = tempfile.NamedTemporaryFile()
top_file.write(b'''

View file

@ -29,7 +29,8 @@ class UtilDictupdateTestCase(TestCase):
# level 1 value changes (list replacement)
mdict = copy.deepcopy(self.dict1)
mdict['A'] = [1, 2]
res = dictupdate.update(copy.deepcopy(mdict), {'A': [2, 3]})
res = dictupdate.update(copy.deepcopy(mdict), {'A': [2, 3]},
merge_lists=False)
mdict['A'] = [2, 3]
self.assertEqual(res, mdict)
@ -50,7 +51,8 @@ class UtilDictupdateTestCase(TestCase):
# level 2 value changes (list replacement)
mdict = copy.deepcopy(self.dict1)
mdict['C']['D'] = ['a', 'b']
res = dictupdate.update(copy.deepcopy(mdict), {'C': {'D': ['c', 'd']}})
res = dictupdate.update(copy.deepcopy(mdict), {'C': {'D': ['c', 'd']}},
merge_lists=False)
mdict['C']['D'] = ['c', 'd']
self.assertEqual(res, mdict)
@ -75,7 +77,7 @@ class UtilDictupdateTestCase(TestCase):
mdict = copy.deepcopy(self.dict1)
mdict['C']['F']['G'] = ['a', 'b']
res = dictupdate.update(copy.deepcopy(mdict),
{'C': {'F': {'G': ['c', 'd']}}})
{'C': {'F': {'G': ['c', 'd']}}}, merge_lists=False)
mdict['C']['F']['G'] = ['c', 'd']
self.assertEqual(res, mdict)