Move merge_environment to environment_util module
This moves the merge_environment utility function from service.py to environment_util.py. Change-Id: Ia005cf47d5e655e60359f8da397a712e749ce13c Blueprint: environment-merging
This commit is contained in:
parent
36a0ad29e5
commit
59b2a559ec
@ -10,6 +10,9 @@
|
|||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
import collections
|
||||||
|
|
||||||
|
from heat.common import environment_format
|
||||||
|
|
||||||
ALLOWED_PARAM_MERGE_STRATEGIES = (OVERWRITE, MERGE, DEEP_MERGE) = (
|
ALLOWED_PARAM_MERGE_STRATEGIES = (OVERWRITE, MERGE, DEEP_MERGE) = (
|
||||||
'overwrite', 'merge', 'deep_merge')
|
'overwrite', 'merge', 'deep_merge')
|
||||||
@ -27,3 +30,38 @@ def get_param_merge_strategy(merge_strategies, param_key):
|
|||||||
return merge_strategy
|
return merge_strategy
|
||||||
|
|
||||||
return env_default
|
return env_default
|
||||||
|
|
||||||
|
|
||||||
|
def deep_update(old, new):
|
||||||
|
'''Merge nested dictionaries.'''
|
||||||
|
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]
|
||||||
|
return old
|
||||||
|
|
||||||
|
|
||||||
|
def merge_environments(environment_files, files, params):
|
||||||
|
"""Merges environment files into the stack input parameters.
|
||||||
|
|
||||||
|
If a list of environment files have been specified, this call will
|
||||||
|
pull the contents of each from the files dict, parse them as
|
||||||
|
environments, and merge them into the stack input params. This
|
||||||
|
behavior is the same as earlier versions of the Heat client that
|
||||||
|
performed this params population client-side.
|
||||||
|
|
||||||
|
:param environment_files: ordered names of the environment files
|
||||||
|
found in the files dict
|
||||||
|
:type environment_files: list or None
|
||||||
|
:param files: mapping of stack filenames to contents
|
||||||
|
:type files: dict
|
||||||
|
: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)
|
||||||
|
@ -33,6 +33,7 @@ import webob
|
|||||||
|
|
||||||
from heat.common import context
|
from heat.common import context
|
||||||
from heat.common import environment_format as env_fmt
|
from heat.common import environment_format as env_fmt
|
||||||
|
from heat.common import environment_util as env_util
|
||||||
from heat.common import exception
|
from heat.common import exception
|
||||||
from heat.common.i18n import _
|
from heat.common.i18n import _
|
||||||
from heat.common.i18n import _LE
|
from heat.common.i18n import _LE
|
||||||
@ -69,8 +70,6 @@ from heat.objects import watch_data
|
|||||||
from heat.objects import watch_rule
|
from heat.objects import watch_rule
|
||||||
from heat.rpc import api as rpc_api
|
from heat.rpc import api as rpc_api
|
||||||
from heat.rpc import worker_api as rpc_worker_api
|
from heat.rpc import worker_api as rpc_worker_api
|
||||||
from heatclient.common import environment_format
|
|
||||||
from heatclient.common import template_utils
|
|
||||||
|
|
||||||
cfg.CONF.import_opt('engine_life_check_timeout', 'heat.common.config')
|
cfg.CONF.import_opt('engine_life_check_timeout', 'heat.common.config')
|
||||||
cfg.CONF.import_opt('max_resources_per_stack', 'heat.common.config')
|
cfg.CONF.import_opt('max_resources_per_stack', 'heat.common.config')
|
||||||
@ -680,7 +679,7 @@ class EngineService(service.Service):
|
|||||||
tmpl = templatem.Template.load(cnxt, template_id)
|
tmpl = templatem.Template.load(cnxt, template_id)
|
||||||
env = tmpl.env
|
env = tmpl.env
|
||||||
else:
|
else:
|
||||||
self._merge_environments(environment_files, files, params)
|
env_util.merge_environments(environment_files, files, params)
|
||||||
env = environment.Environment(params)
|
env = environment.Environment(params)
|
||||||
tmpl = templatem.Template(template, files=files, env=env)
|
tmpl = templatem.Template(template, files=files, env=env)
|
||||||
self._validate_new_stack(cnxt, stack_name, tmpl)
|
self._validate_new_stack(cnxt, stack_name, tmpl)
|
||||||
@ -701,30 +700,6 @@ class EngineService(service.Service):
|
|||||||
env.registry.log_resource_info(prefix=stack_name)
|
env.registry.log_resource_info(prefix=stack_name)
|
||||||
return stack
|
return stack
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _merge_environments(environment_files, files, params):
|
|
||||||
"""Merges environment files into the stack input parameters.
|
|
||||||
|
|
||||||
If a list of environment files have been specified, this call will
|
|
||||||
pull the contents of each from the files dict, parse them as
|
|
||||||
environments, and merge them into the stack input params. This
|
|
||||||
behavior is the same as earlier versions of the Heat client that
|
|
||||||
performed this params population client-side.
|
|
||||||
|
|
||||||
:param environment_files: ordered names of the environment files
|
|
||||||
found in the files dict
|
|
||||||
:type environment_files: list or None
|
|
||||||
:param files: mapping of stack filenames to contents
|
|
||||||
:type files: dict
|
|
||||||
: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)
|
|
||||||
template_utils.deep_update(params, parsed_env)
|
|
||||||
|
|
||||||
@context.request_context
|
@context.request_context
|
||||||
def preview_stack(self, cnxt, stack_name, template, params, files,
|
def preview_stack(self, cnxt, stack_name, template, params, files,
|
||||||
args, environment_files=None):
|
args, environment_files=None):
|
||||||
@ -969,7 +944,7 @@ class EngineService(service.Service):
|
|||||||
:param template_id: the ID of a pre-stored template in the DB
|
:param template_id: the ID of a pre-stored template in the DB
|
||||||
"""
|
"""
|
||||||
# Handle server-side environment file resolution
|
# Handle server-side environment file resolution
|
||||||
self._merge_environments(environment_files, files, params)
|
env_util.merge_environments(environment_files, files, params)
|
||||||
|
|
||||||
# Get the database representation of the existing stack
|
# Get the database representation of the existing stack
|
||||||
db_stack = self._get_stack(cnxt, stack_identity)
|
db_stack = self._get_stack(cnxt, stack_identity)
|
||||||
@ -1026,7 +1001,7 @@ class EngineService(service.Service):
|
|||||||
heat-api process if using a template-url.
|
heat-api process if using a template-url.
|
||||||
"""
|
"""
|
||||||
# Handle server-side environment file resolution
|
# Handle server-side environment file resolution
|
||||||
self._merge_environments(environment_files, files, params)
|
env_util.merge_environments(environment_files, files, params)
|
||||||
|
|
||||||
# Get the database representation of the existing stack
|
# Get the database representation of the existing stack
|
||||||
db_stack = self._get_stack(cnxt, stack_identity)
|
db_stack = self._get_stack(cnxt, stack_identity)
|
||||||
@ -1206,7 +1181,7 @@ class EngineService(service.Service):
|
|||||||
|
|
||||||
service_check_defer = True
|
service_check_defer = True
|
||||||
|
|
||||||
self._merge_environments(environment_files, files, params)
|
env_util.merge_environments(environment_files, files, params)
|
||||||
env = environment.Environment(params)
|
env = environment.Environment(params)
|
||||||
tmpl = templatem.Template(template, files=files, env=env)
|
tmpl = templatem.Template(template, files=files, env=env)
|
||||||
try:
|
try:
|
||||||
|
@ -411,50 +411,3 @@ class ServiceEngineTest(common.HeatTestCase):
|
|||||||
self.eng.start()
|
self.eng.start()
|
||||||
self.assertEqual(cfg.CONF.executor_thread_pool_size,
|
self.assertEqual(cfg.CONF.executor_thread_pool_size,
|
||||||
cfg.CONF.database.max_overflow)
|
cfg.CONF.database.max_overflow)
|
||||||
|
|
||||||
def test_merge_environments(self):
|
|
||||||
# Setup
|
|
||||||
params = {'parameters': {
|
|
||||||
'p0': 'CORRECT',
|
|
||||||
'p1': 'INCORRECT',
|
|
||||||
'p2': 'INCORRECT'}
|
|
||||||
}
|
|
||||||
env_1 = '''
|
|
||||||
{'parameters' : {
|
|
||||||
'p1': 'CORRECT',
|
|
||||||
'p2': 'INCORRECT-ENV-1',
|
|
||||||
}}'''
|
|
||||||
env_2 = '''
|
|
||||||
{'parameters': {
|
|
||||||
'p2': 'CORRECT'
|
|
||||||
}}'''
|
|
||||||
|
|
||||||
files = {'env_1': env_1, 'env_2': env_2}
|
|
||||||
environment_files = ['env_1', 'env_2']
|
|
||||||
|
|
||||||
# Test
|
|
||||||
self.eng._merge_environments(environment_files, files, params)
|
|
||||||
|
|
||||||
# Verify
|
|
||||||
expected = {'parameters': {
|
|
||||||
'p0': 'CORRECT',
|
|
||||||
'p1': 'CORRECT',
|
|
||||||
'p2': 'CORRECT',
|
|
||||||
}}
|
|
||||||
self.assertEqual(expected, params)
|
|
||||||
|
|
||||||
def test_merge_environments_no_env_files(self):
|
|
||||||
params = {'parameters': {'p0': 'CORRECT'}}
|
|
||||||
env_1 = '''
|
|
||||||
{'parameters' : {
|
|
||||||
'p0': 'INCORRECT',
|
|
||||||
}}'''
|
|
||||||
|
|
||||||
files = {'env_1': env_1}
|
|
||||||
|
|
||||||
# Test - Should ignore env_1 in files
|
|
||||||
self.eng._merge_environments(None, files, params)
|
|
||||||
|
|
||||||
# Verify
|
|
||||||
expected = {'parameters': {'p0': 'CORRECT'}}
|
|
||||||
self.assertEqual(expected, params)
|
|
||||||
|
@ -16,6 +16,7 @@ from oslo_messaging.rpc import dispatcher
|
|||||||
from oslo_service import threadgroup
|
from oslo_service import threadgroup
|
||||||
import six
|
import six
|
||||||
|
|
||||||
|
from heat.common import environment_util as env_util
|
||||||
from heat.common import exception
|
from heat.common import exception
|
||||||
from heat.engine.clients.os import glance
|
from heat.engine.clients.os import glance
|
||||||
from heat.engine.clients.os import nova
|
from heat.engine.clients.os import nova
|
||||||
@ -54,7 +55,7 @@ class StackCreateTest(common.HeatTestCase):
|
|||||||
mock_env = self.patchobject(environment, 'Environment',
|
mock_env = self.patchobject(environment, 'Environment',
|
||||||
return_value=stk.env)
|
return_value=stk.env)
|
||||||
mock_stack = self.patchobject(stack, 'Stack', return_value=stk)
|
mock_stack = self.patchobject(stack, 'Stack', return_value=stk)
|
||||||
mock_merge = self.patchobject(self.man, '_merge_environments')
|
mock_merge = self.patchobject(env_util, 'merge_environments')
|
||||||
result = self.man.create_stack(self.ctx, stack_name,
|
result = self.man.create_stack(self.ctx, stack_name,
|
||||||
template, params, None, {},
|
template, params, None, {},
|
||||||
environment_files=environment_files)
|
environment_files=environment_files)
|
||||||
|
@ -18,6 +18,7 @@ from oslo_config import cfg
|
|||||||
from oslo_messaging.rpc import dispatcher
|
from oslo_messaging.rpc import dispatcher
|
||||||
import six
|
import six
|
||||||
|
|
||||||
|
from heat.common import environment_util as env_util
|
||||||
from heat.common import exception
|
from heat.common import exception
|
||||||
from heat.common import messaging
|
from heat.common import messaging
|
||||||
from heat.common import service_utils
|
from heat.common import service_utils
|
||||||
@ -121,7 +122,7 @@ class ServiceStackUpdateTest(common.HeatTestCase):
|
|||||||
self.patchobject(eventlet.queue, 'LightQueue',
|
self.patchobject(eventlet.queue, 'LightQueue',
|
||||||
return_value=mock.Mock())
|
return_value=mock.Mock())
|
||||||
|
|
||||||
mock_merge = self.patchobject(self.man, '_merge_environments')
|
mock_merge = self.patchobject(env_util, 'merge_environments')
|
||||||
|
|
||||||
# Test
|
# Test
|
||||||
environment_files = ['env_1']
|
environment_files = ['env_1']
|
||||||
@ -991,7 +992,7 @@ resources:
|
|||||||
mock_env = self.patchobject(environment, 'Environment',
|
mock_env = self.patchobject(environment, 'Environment',
|
||||||
return_value=stk.env)
|
return_value=stk.env)
|
||||||
mock_validate = self.patchobject(stk, 'validate', return_value=None)
|
mock_validate = self.patchobject(stk, 'validate', return_value=None)
|
||||||
mock_merge = self.patchobject(self.man, '_merge_environments')
|
mock_merge = self.patchobject(env_util, 'merge_environments')
|
||||||
|
|
||||||
# Patch _resolve_all_attributes or it tries to call novaclient
|
# Patch _resolve_all_attributes or it tries to call novaclient
|
||||||
self.patchobject(resource.Resource, '_resolve_all_attributes',
|
self.patchobject(resource.Resource, '_resolve_all_attributes',
|
||||||
|
@ -48,3 +48,53 @@ class TestEnvironmentUtil(common.HeatTestCase):
|
|||||||
param_strategy = env_util.get_param_merge_strategy(merge_strategies,
|
param_strategy = env_util.get_param_merge_strategy(merge_strategies,
|
||||||
'param1')
|
'param1')
|
||||||
self.assertEqual(env_util.OVERWRITE, param_strategy)
|
self.assertEqual(env_util.OVERWRITE, param_strategy)
|
||||||
|
|
||||||
|
|
||||||
|
class TestMergeEnvironments(common.HeatTestCase):
|
||||||
|
|
||||||
|
def test_merge_environments(self):
|
||||||
|
# Setup
|
||||||
|
params = {'parameters': {
|
||||||
|
'p0': 'CORRECT',
|
||||||
|
'p1': 'INCORRECT',
|
||||||
|
'p2': 'INCORRECT'}
|
||||||
|
}
|
||||||
|
env_1 = '''
|
||||||
|
{'parameters' : {
|
||||||
|
'p1': 'CORRECT',
|
||||||
|
'p2': 'INCORRECT-ENV-1',
|
||||||
|
}}'''
|
||||||
|
env_2 = '''
|
||||||
|
{'parameters': {
|
||||||
|
'p2': 'CORRECT'
|
||||||
|
}}'''
|
||||||
|
|
||||||
|
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
|
||||||
|
expected = {'parameters': {
|
||||||
|
'p0': 'CORRECT',
|
||||||
|
'p1': 'CORRECT',
|
||||||
|
'p2': 'CORRECT',
|
||||||
|
}}
|
||||||
|
self.assertEqual(expected, params)
|
||||||
|
|
||||||
|
def test_merge_environments_no_env_files(self):
|
||||||
|
params = {'parameters': {'p0': 'CORRECT'}}
|
||||||
|
env_1 = '''
|
||||||
|
{'parameters' : {
|
||||||
|
'p0': 'INCORRECT',
|
||||||
|
}}'''
|
||||||
|
|
||||||
|
files = {'env_1': env_1}
|
||||||
|
|
||||||
|
# Test - Should ignore env_1 in files
|
||||||
|
env_util.merge_environments(None, files, params)
|
||||||
|
|
||||||
|
# Verify
|
||||||
|
expected = {'parameters': {'p0': 'CORRECT'}}
|
||||||
|
self.assertEqual(expected, params)
|
||||||
|
@ -21,6 +21,7 @@ from oslo_serialization import jsonutils as json
|
|||||||
import six
|
import six
|
||||||
|
|
||||||
from heat.common import context
|
from heat.common import context
|
||||||
|
from heat.common import environment_util as env_util
|
||||||
from heat.common import exception
|
from heat.common import exception
|
||||||
from heat.common import identifier
|
from heat.common import identifier
|
||||||
from heat.common import template_format
|
from heat.common import template_format
|
||||||
@ -1165,7 +1166,7 @@ class StackServiceTest(common.HeatTestCase):
|
|||||||
self._preview_stack)
|
self._preview_stack)
|
||||||
self.assertEqual(exception.StackValidationFailed, ex.exc_info[0])
|
self.assertEqual(exception.StackValidationFailed, ex.exc_info[0])
|
||||||
|
|
||||||
@mock.patch.object(service.EngineService, '_merge_environments')
|
@mock.patch.object(env_util, 'merge_environments')
|
||||||
def test_preview_environment_files(self, mock_merge):
|
def test_preview_environment_files(self, mock_merge):
|
||||||
# Setup
|
# Setup
|
||||||
environment_files = ['env_1']
|
environment_files = ['env_1']
|
||||||
|
Loading…
Reference in New Issue
Block a user