diff --git a/doc/ref/configuration/master.rst b/doc/ref/configuration/master.rst index d1a0eaa0b6..598ccbf682 100644 --- a/doc/ref/configuration/master.rst +++ b/doc/ref/configuration/master.rst @@ -4029,6 +4029,25 @@ Recursively merge lists by aggregating them instead of replacing them. pillar_merge_lists: False +.. conf_master:: pillar_includes_override_sls + +``pillar_includes_override_sls`` +******************************** + +.. versionadded:: 2017.7.6,2018.3.1 + +Default: ``False`` + +Prior to version 2017.7.3, keys from :ref:`pillar includes ` +would be merged on top of the pillar SLS. Since 2017.7.3, the includes are +merged together and then the pillar SLS is merged on top of that. + +Set this option to ``True`` to return to the old behavior. + +.. code-block:: yaml + + pillar_includes_override_sls: True + .. _pillar-cache-opts: Pillar Cache Options diff --git a/doc/topics/pillar/index.rst b/doc/topics/pillar/index.rst index f649d0bf40..2c683cb346 100644 --- a/doc/topics/pillar/index.rst +++ b/doc/topics/pillar/index.rst @@ -285,6 +285,8 @@ Since both pillar SLS files contained a ``bind`` key which contained a nested dictionary, the pillar dictionary's ``bind`` key contains the combined contents of both SLS files' ``bind`` keys. +.. _pillar-include: + Including Other Pillars ======================= diff --git a/doc/topics/releases/2017.7.6.rst b/doc/topics/releases/2017.7.6.rst new file mode 100644 index 0000000000..67e642ed78 --- /dev/null +++ b/doc/topics/releases/2017.7.6.rst @@ -0,0 +1,15 @@ +=========================== +Salt 2017.7.6 Release Notes +=========================== + +Version 2017.7.6 is a bugfix release for :ref:`2017.7.0 `. + +Option to Return to Previous Pillar Include Behavior +---------------------------------------------------- + +Prior to version 2017.7.3, keys from :ref:`pillar includes ` +would be merged on top of the pillar SLS. Since 2017.7.3, the includes are +merged together and then the pillar SLS is merged on top of that. + +The :conf_master:`pillar_includes_override_sls` option has been added allow +users to switch back to the pre-2017.7.3 behavior. diff --git a/salt/config/__init__.py b/salt/config/__init__.py index b3af2f755e..5007a493ca 100644 --- a/salt/config/__init__.py +++ b/salt/config/__init__.py @@ -680,6 +680,9 @@ VALID_OPTS = { # Recursively merge lists by aggregating them instead of replacing them. 'pillar_merge_lists': bool, + # If True, values from included pillar SLS targets will override + 'pillar_includes_override_sls': bool, + # How to merge multiple top files from multiple salt environments # (saltenvs); can be 'merge' or 'same' 'top_file_merging_strategy': str, @@ -1139,6 +1142,9 @@ DEFAULT_MINION_OPTS = { 'pillarenv': None, 'pillarenv_from_saltenv': False, 'pillar_opts': False, + 'pillar_source_merging_strategy': 'smart', + 'pillar_merge_lists': False, + 'pillar_includes_override_sls': False, # ``pillar_cache``, ``pillar_cache_ttl`` and ``pillar_cache_backend`` # are not used on the minion but are unavoidably in the code path 'pillar_cache': False, @@ -1467,6 +1473,7 @@ DEFAULT_MASTER_OPTS = { 'pillar_safe_render_error': True, 'pillar_source_merging_strategy': 'smart', 'pillar_merge_lists': False, + 'pillar_includes_override_sls': False, 'pillar_cache': False, 'pillar_cache_ttl': 3600, 'pillar_cache_backend': 'disk', diff --git a/salt/pillar/__init__.py b/salt/pillar/__init__.py index 7741661cae..63bc758277 100644 --- a/salt/pillar/__init__.py +++ b/salt/pillar/__init__.py @@ -696,11 +696,22 @@ class Pillar(object): nstate = { key_fragment: nstate } - include_states.append(nstate) + if not self.opts.get('pillar_includes_override_sls', False): + include_states.append(nstate) + else: + state = merge( + state, + nstate, + self.merge_strategy, + self.opts.get('renderer', 'yaml'), + self.opts.get('pillar_merge_lists', False)) if err: errors += err - if include_states: - # merge included state(s) with the current state merged last + + if not self.opts.get('pillar_includes_override_sls', False): + # merge included state(s) with the current state + # merged last to ensure that its values are + # authoritative. include_states.append(state) state = None for s in include_states: diff --git a/tests/unit/test_pillar.py b/tests/unit/test_pillar.py index 807eb502fb..dff2b887d8 100644 --- a/tests/unit/test_pillar.py +++ b/tests/unit/test_pillar.py @@ -186,6 +186,66 @@ class PillarTestCase(TestCase): ({'foo': 'bar', 'nested': {'level': {'foo': 'bar2'}}}, []) ) + def test_includes_override_sls(self): + opts = { + 'renderer': 'json', + 'renderer_blacklist': [], + 'renderer_whitelist': [], + 'state_top': '', + 'pillar_roots': [], + 'file_roots': [], + 'extension_modules': '' + } + grains = { + 'os': 'Ubuntu', + 'os_family': 'Debian', + 'oscodename': 'raring', + 'osfullname': 'Ubuntu', + 'osrelease': '13.04', + 'kernel': 'Linux' + } + with patch('salt.pillar.compile_template') as compile_template: + + # Test with option set to True + opts['pillar_includes_override_sls'] = True + pillar = salt.pillar.Pillar(opts, grains, 'mocked-minion', 'base') + # Mock getting the proper template files + pillar.client.get_state = MagicMock( + return_value={ + 'dest': '/path/to/pillar/files/foo.sls', + 'source': 'salt://foo.sls' + } + ) + + compile_template.side_effect = [ + {'foo': 'bar', 'include': ['blah']}, + {'foo': 'bar2'} + ] + self.assertEqual( + pillar.render_pillar({'base': ['foo.sls']}), + ({'foo': 'bar2'}, []) + ) + + # Test with option set to False + opts['pillar_includes_override_sls'] = False + pillar = salt.pillar.Pillar(opts, grains, 'mocked-minion', 'base') + # Mock getting the proper template files + pillar.client.get_state = MagicMock( + return_value={ + 'dest': '/path/to/pillar/files/foo.sls', + 'source': 'salt://foo.sls' + } + ) + + compile_template.side_effect = [ + {'foo': 'bar', 'include': ['blah']}, + {'foo': 'bar2'} + ] + self.assertEqual( + pillar.render_pillar({'base': ['foo.sls']}), + ({'foo': 'bar'}, []) + ) + def test_topfile_order(self): with patch('salt.pillar.salt.fileclient.get_file_client', autospec=True) as get_file_client, \ patch('salt.pillar.salt.minion.Matcher') as Matcher: # autospec=True disabled due to py3 mock bug