Improve deep merge for parameters

This allows for merging lists and deep merging maps.
This does not support comma_delimited_lists and is
implemented by using parameter schema in the subsequent
patches.

Change-Id: I89df95eca713a7125cc53ed8bf352274f28bf6d9
Blueprint: environment_merging
This commit is contained in:
rabi 2016-08-04 10:56:41 +05:30
parent 59b2a559ec
commit 5b0fe81270
2 changed files with 83 additions and 13 deletions

View File

@ -12,7 +12,9 @@
# under the License.
import collections
from heat.common import environment_format
import six
from heat.common import environment_format as env_fmt
ALLOWED_PARAM_MERGE_STRATEGIES = (OVERWRITE, MERGE, DEEP_MERGE) = (
'overwrite', 'merge', 'deep_merge')
@ -32,14 +34,37 @@ def get_param_merge_strategy(merge_strategies, param_key):
return env_default
def deep_update(old, new):
'''Merge nested dictionaries.'''
def merge_list(old, new):
"""merges lists and comma delimited lists."""
if not old:
return new
if isinstance(new, list):
old.extend(new)
return old
else:
return ','.join([old, new])
def merge_map(old, new, deep_merge=False):
"""Merge nested dictionaries."""
if not old:
return new
for k, v in new.items():
if isinstance(v, collections.Mapping):
r = deep_update(old.get(k, {}), v)
old[k] = r
else:
old[k] = new[k]
if v:
if not deep_merge:
old[k] = v
elif isinstance(v, collections.Mapping):
old_v = old.get(k)
old[k] = merge_map(old_v, v, deep_merge) if old_v else v
elif (isinstance(v, collections.Sequence) and
not isinstance(v, six.string_types)):
old_v = old.get(k)
old[k] = merge_list(old_v, v) if old_v else v
else:
old[k] = v
return old
@ -60,8 +85,14 @@ def merge_environments(environment_files, files, params):
:param params: parameters describing the stack
:type dict:
"""
if environment_files:
for filename in environment_files:
raw_env = files[filename]
parsed_env = environment_format.parse(raw_env)
deep_update(params, parsed_env)
if not environment_files:
return
for filename in environment_files:
raw_env = files[filename]
parsed_env = env_fmt.parse(raw_env)
for section_key, section_value in parsed_env.items():
if section_value:
params[section_key] = merge_map(params[section_key],
section_value,
deep_merge=True)

View File

@ -83,6 +83,45 @@ class TestMergeEnvironments(common.HeatTestCase):
}}
self.assertEqual(expected, params)
def test_merge_environments_deep_merge(self):
# Setup
params = {'parameters': {
'p0': 'CORRECT',
'p1': ['CORRECT1'],
'p2': {'A': ['CORRECT1', 'CORRECT2']},
'p3': 'CORRECT1,CORRECT2'}
}
env_1 = '''
{'parameters' : {
'p1': ['CORRECT2', 'CORRECT3'],
'p2': {'B': ['CORRECT3', 'CORRECT4'],
'C': [CORRECT5]},
'p3': 'CORRECT3,CORRECT4'
}}'''
env_2 = '''
{'parameters': {
'p2': {'C': ['CORRECT6']},
'p3': 'CORRECT5,CORRECT6'
}}'''
files = {'env_1': env_1, 'env_2': env_2}
environment_files = ['env_1', 'env_2']
# Test
env_util.merge_environments(environment_files, files, params)
# Verify
# Does not work for comma_delimited_list.
expected = {'parameters': {
'p0': 'CORRECT',
'p1': ['CORRECT1', 'CORRECT2', 'CORRECT3'],
'p2': {'A': ['CORRECT1', 'CORRECT2'],
'B': ['CORRECT3', 'CORRECT4'],
'C': ['CORRECT5', 'CORRECT6']},
'p3': 'CORRECT5,CORRECT6'
}}
self.assertEqual(expected, params)
def test_merge_environments_no_env_files(self):
params = {'parameters': {'p0': 'CORRECT'}}
env_1 = '''