Merge "Improve deep merge for parameters"

This commit is contained in:
Jenkins 2016-08-25 09:34:28 +00:00 committed by Gerrit Code Review
commit ccaf7d1777
2 changed files with 83 additions and 13 deletions

View File

@ -12,7 +12,9 @@
# under the License. # under the License.
import collections 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) = ( ALLOWED_PARAM_MERGE_STRATEGIES = (OVERWRITE, MERGE, DEEP_MERGE) = (
'overwrite', 'merge', 'deep_merge') 'overwrite', 'merge', 'deep_merge')
@ -32,14 +34,37 @@ def get_param_merge_strategy(merge_strategies, param_key):
return env_default return env_default
def deep_update(old, new): def merge_list(old, new):
'''Merge nested dictionaries.''' """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(): for k, v in new.items():
if isinstance(v, collections.Mapping): if v:
r = deep_update(old.get(k, {}), v) if not deep_merge:
old[k] = r old[k] = v
else: elif isinstance(v, collections.Mapping):
old[k] = new[k] 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 return old
@ -60,8 +85,14 @@ def merge_environments(environment_files, files, params):
:param params: parameters describing the stack :param params: parameters describing the stack
:type dict: :type dict:
""" """
if environment_files: if not environment_files:
for filename in environment_files: return
raw_env = files[filename]
parsed_env = environment_format.parse(raw_env) for filename in environment_files:
deep_update(params, parsed_env) 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) 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): def test_merge_environments_no_env_files(self):
params = {'parameters': {'p0': 'CORRECT'}} params = {'parameters': {'p0': 'CORRECT'}}
env_1 = ''' env_1 = '''