Add support for heat environments
Enable the user to choose one environment from all environment files located in the package under /Resources/HotEnvironments to be deployed with the heat template as part of a Murano environment. Partially implements: blueprint add-support-for-heat-environments-and-files Change-Id: Id14fea94854221ba92b3eb09189c211c3d10d82f
This commit is contained in:
parent
9ac9ad538d
commit
e811406945
|
@ -40,6 +40,7 @@ class HeatStack(object):
|
|||
self._template = None
|
||||
self._parameters = {}
|
||||
self._files = {}
|
||||
self._hot_environment = ''
|
||||
self._applied = True
|
||||
self._description = description
|
||||
self._clients = helpers.get_environment().clients
|
||||
|
@ -88,6 +89,10 @@ class HeatStack(object):
|
|||
self._files = files
|
||||
self._applied = False
|
||||
|
||||
def set_hot_environment(self, hot_environment):
|
||||
self._hot_environment = hot_environment
|
||||
self._applied = False
|
||||
|
||||
def update_template(self, template):
|
||||
template_version = template.get('heat_template_version',
|
||||
HEAT_TEMPLATE_VERSION)
|
||||
|
@ -186,6 +191,7 @@ class HeatStack(object):
|
|||
parameters=self._parameters,
|
||||
template=template,
|
||||
files=self._files,
|
||||
environment=self._hot_environment,
|
||||
disable_rollback=True)
|
||||
|
||||
self._wait_state(lambda status: status == 'CREATE_COMPLETE')
|
||||
|
@ -197,6 +203,7 @@ class HeatStack(object):
|
|||
stack_id=self._name,
|
||||
parameters=self._parameters,
|
||||
files=self._files,
|
||||
environment=self._hot_environment,
|
||||
template=template,
|
||||
disable_rollback=True)
|
||||
self._wait_state(
|
||||
|
|
|
@ -23,8 +23,10 @@ from murano.dsl import yaql_expression
|
|||
import murano.packages.application_package
|
||||
from murano.packages import exceptions
|
||||
|
||||
|
||||
YAQL = yaql_expression.YaqlExpression
|
||||
RESOURCES_DIR_NAME = 'Resources/'
|
||||
HOT_FILES_DIR_NAME = 'HotFiles/'
|
||||
HOT_ENV_DIR_NAME = 'HotEnvironments/'
|
||||
|
||||
|
||||
class Dumper(yaml.Dumper):
|
||||
|
@ -34,6 +36,7 @@ class Dumper(yaml.Dumper):
|
|||
def yaql_representer(dumper, data):
|
||||
return dumper.represent_scalar(u'!yaql', str(data))
|
||||
|
||||
|
||||
Dumper.add_representer(YAQL, yaql_representer)
|
||||
|
||||
|
||||
|
@ -90,7 +93,17 @@ class HotPackage(murano.packages.application_package.ApplicationPackage):
|
|||
'Extends': 'io.murano.Application'
|
||||
}
|
||||
|
||||
parameters = HotPackage._translate_parameters(hot)
|
||||
hot_envs_path = os.path.join(self._source_directory,
|
||||
RESOURCES_DIR_NAME,
|
||||
HOT_ENV_DIR_NAME)
|
||||
|
||||
# if using hot environments, doing parameter validation with contracts
|
||||
# will overwrite the parameters in the hot environment.
|
||||
# don't validate parameters if hot environments exist.
|
||||
validate_hot_parameters = (not os.path.isdir(hot_envs_path) or
|
||||
not os.listdir(hot_envs_path))
|
||||
|
||||
parameters = HotPackage._build_properties(hot, validate_hot_parameters)
|
||||
parameters.update(HotPackage._translate_outputs(hot))
|
||||
translated['Properties'] = parameters
|
||||
|
||||
|
@ -99,21 +112,44 @@ class HotPackage(murano.packages.application_package.ApplicationPackage):
|
|||
self._translated_class = translated
|
||||
|
||||
@staticmethod
|
||||
def _translate_parameters(hot):
|
||||
def _build_properties(hot, validate_hot_parameters):
|
||||
result = {
|
||||
'generatedHeatStackName': {
|
||||
'Contract': YAQL('$.string()'),
|
||||
'Usage': 'Out'
|
||||
},
|
||||
'hotEnvironment': {
|
||||
'Contract': YAQL('$.string()'),
|
||||
'Usage': 'In'
|
||||
},
|
||||
'name': {
|
||||
'Contract': YAQL('$.string().notNull()'),
|
||||
'Usage': 'In',
|
||||
|
||||
}
|
||||
}
|
||||
for key, value in (hot.get('parameters') or {}).items():
|
||||
result[key] = HotPackage._translate_parameter(value)
|
||||
result['name'] = {'Usage': 'In',
|
||||
'Contract': YAQL('$.string().notNull()')}
|
||||
|
||||
if validate_hot_parameters:
|
||||
params_dict = {}
|
||||
for key, value in (hot.get('parameters') or {}).items():
|
||||
param_contract = HotPackage._translate_param_to_contract(value)
|
||||
params_dict[key] = param_contract
|
||||
result['templateParameters'] = {
|
||||
'Contract': params_dict,
|
||||
'Default': {},
|
||||
'Usage': 'In'
|
||||
}
|
||||
else:
|
||||
result['templateParameters'] = {
|
||||
'Contract': {},
|
||||
'Default': {},
|
||||
'Usage': 'In'
|
||||
}
|
||||
|
||||
return result
|
||||
|
||||
@staticmethod
|
||||
def _translate_parameter(value):
|
||||
def _translate_param_to_contract(value):
|
||||
contract = '$'
|
||||
|
||||
parameter_type = value['type']
|
||||
|
@ -132,12 +168,7 @@ class HotPackage(murano.packages.application_package.ApplicationPackage):
|
|||
if translated:
|
||||
contract += translated
|
||||
|
||||
result = {
|
||||
'Contract': YAQL(contract),
|
||||
"Usage": "In"
|
||||
}
|
||||
if 'default' in value:
|
||||
result['Default'] = value['default']
|
||||
result = YAQL(contract)
|
||||
return result
|
||||
|
||||
@staticmethod
|
||||
|
@ -152,20 +183,21 @@ class HotPackage(murano.packages.application_package.ApplicationPackage):
|
|||
|
||||
@staticmethod
|
||||
def _translate_files(source_directory):
|
||||
heat_files_dir = os.path.join(source_directory, 'Resources/HotFiles')
|
||||
result = {}
|
||||
if os.path.isdir(heat_files_dir):
|
||||
result = HotPackage._build_heat_files_dict(heat_files_dir)
|
||||
return result
|
||||
hot_files_path = os.path.join(source_directory,
|
||||
RESOURCES_DIR_NAME,
|
||||
HOT_FILES_DIR_NAME)
|
||||
|
||||
return HotPackage._build_hot_resources_dict(hot_files_path)
|
||||
|
||||
@staticmethod
|
||||
def _build_heat_files_dict(basedir):
|
||||
def _build_hot_resources_dict(basedir):
|
||||
result = []
|
||||
for root, _, files in os.walk(os.path.abspath(basedir)):
|
||||
for f in files:
|
||||
full_path = os.path.join(root, f)
|
||||
relative_path = os.path.relpath(full_path, basedir)
|
||||
result.append(relative_path)
|
||||
if os.path.isdir(basedir):
|
||||
for root, _, files in os.walk(os.path.abspath(basedir)):
|
||||
for f in files:
|
||||
full_path = os.path.join(root, f)
|
||||
relative_path = os.path.relpath(full_path, basedir)
|
||||
result.append(relative_path)
|
||||
return result
|
||||
|
||||
@staticmethod
|
||||
|
@ -220,14 +252,14 @@ class HotPackage(murano.packages.application_package.ApplicationPackage):
|
|||
|
||||
@staticmethod
|
||||
def _generate_workflow(hot, files):
|
||||
template_parameters = {}
|
||||
for key, value in (hot.get('parameters') or {}).items():
|
||||
template_parameters[key] = YAQL("$." + key)
|
||||
hot_files_map = {}
|
||||
for f in files:
|
||||
file_path = "$resources.string('HotFiles/%s')" % f
|
||||
file_path = "$resources.string('{0}{1}')".format(
|
||||
HOT_FILES_DIR_NAME, f)
|
||||
hot_files_map[f] = YAQL(file_path)
|
||||
|
||||
hot_env = YAQL("$.hotEnvironment")
|
||||
|
||||
copy_outputs = []
|
||||
for key, value in (hot.get('outputs') or {}).items():
|
||||
copy_outputs.append({YAQL('$.' + key): YAQL('$outputs.' + key)})
|
||||
|
@ -254,12 +286,24 @@ class HotPackage(murano.packages.application_package.ApplicationPackage):
|
|||
"'Application deployment has started')"),
|
||||
|
||||
{YAQL('$resources'): YAQL("new('io.murano.system.Resources')")},
|
||||
|
||||
{YAQL('$template'): YAQL("$resources.yaml(type($this))")},
|
||||
{YAQL('$parameters'): template_parameters},
|
||||
{YAQL('$files'): hot_files_map},
|
||||
YAQL('$stack.setTemplate($template)'),
|
||||
{YAQL('$parameters'): YAQL("$.templateParameters")},
|
||||
YAQL('$stack.setParameters($parameters)'),
|
||||
{YAQL('$files'): hot_files_map},
|
||||
YAQL('$stack.setFiles($files)'),
|
||||
{YAQL('$hotEnv'): hot_env},
|
||||
{
|
||||
'If': YAQL("bool($hotEnv)"),
|
||||
'Then': [
|
||||
{YAQL('$envRelPath'): YAQL("'{0}' + $hotEnv".format(
|
||||
HOT_ENV_DIR_NAME))},
|
||||
{YAQL('$hotEnvContent'): YAQL("$resources.string("
|
||||
"$envRelPath)")},
|
||||
YAQL('$stack.setHotEnvironment($hotEnvContent)')
|
||||
]
|
||||
},
|
||||
|
||||
YAQL("$reporter.report($this, 'Stack creation has started')"),
|
||||
{
|
||||
|
@ -323,8 +367,8 @@ class HotPackage(murano.packages.application_package.ApplicationPackage):
|
|||
'label': 'Application Name',
|
||||
'required': True,
|
||||
'description':
|
||||
'Enter a desired name for the application.'
|
||||
' Just A-Z, a-z, 0-9, and dash are allowed'
|
||||
'Enter a desired name for the application.'
|
||||
' Just A-Z, a-z, 0-9, and dash are allowed'
|
||||
}
|
||||
]
|
||||
used_parameters = set()
|
||||
|
@ -367,7 +411,7 @@ class HotPackage(murano.packages.application_package.ApplicationPackage):
|
|||
translated['type'] = 'boolean'
|
||||
else:
|
||||
# string, json, and comma_delimited_list parameters are all
|
||||
# displayed as strings in UI. Any unsuported parameter would also
|
||||
# displayed as strings in UI. Any unsupported parameter would also
|
||||
# be displayed as strings.
|
||||
translated['type'] = 'string'
|
||||
|
||||
|
@ -452,8 +496,12 @@ class HotPackage(murano.packages.application_package.ApplicationPackage):
|
|||
}
|
||||
}
|
||||
for i, record in enumerate(groups):
|
||||
if i == 0:
|
||||
section = app
|
||||
else:
|
||||
section = app.setdefault('templateParameters', {})
|
||||
for property_name in record[1]:
|
||||
app[property_name] = YAQL(
|
||||
section[property_name] = YAQL(
|
||||
'$.group{0}.{1}'.format(i, property_name))
|
||||
app['name'] = YAQL('$.group0.name')
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ class TestHotPackage(test_base.MuranoTestCase):
|
|||
)
|
||||
load_utils.load_from_dir(package_dir)
|
||||
|
||||
result = murano.packages.hot_package.HotPackage._translate_files(
|
||||
files = murano.packages.hot_package.HotPackage._translate_files(
|
||||
package_dir)
|
||||
expected_result = [
|
||||
"testHeatFile",
|
||||
|
@ -36,7 +36,7 @@ class TestHotPackage(test_base.MuranoTestCase):
|
|||
"middle_file/inner_file2/testHeatFile"
|
||||
]
|
||||
msg = "hot files were not generated correctly"
|
||||
self.assertEqual(expected_result, result, msg)
|
||||
self.assertEqual(expected_result, files, msg)
|
||||
|
||||
def test_heat_files_generated_empty(self):
|
||||
package_dir = os.path.abspath(
|
||||
|
@ -45,7 +45,7 @@ class TestHotPackage(test_base.MuranoTestCase):
|
|||
)
|
||||
load_utils.load_from_dir(package_dir)
|
||||
|
||||
result = murano.packages.hot_package.HotPackage._translate_files(
|
||||
package_dir)
|
||||
files = murano.packages.hot_package.HotPackage \
|
||||
._translate_files(package_dir)
|
||||
msg = "heat files were not generated correctly. Expected empty dict"
|
||||
self.assertEqual(result, {}, msg)
|
||||
self.assertEqual(files, [], msg)
|
||||
|
|
|
@ -26,7 +26,6 @@ from murano.engine import environment
|
|||
from murano.engine.system import heat_stack
|
||||
from murano.tests.unit import base
|
||||
|
||||
|
||||
MOD_NAME = 'murano.engine.system.heat_stack'
|
||||
|
||||
|
||||
|
@ -48,107 +47,150 @@ class TestHeatStack(base.MuranoTestCase):
|
|||
self.heat_client_mock
|
||||
self.environment_mock.clients = client_manager_mock
|
||||
|
||||
def test_push_adds_version(self):
|
||||
@mock.patch(MOD_NAME + '.HeatStack._wait_state')
|
||||
@mock.patch(MOD_NAME + '.HeatStack._get_status')
|
||||
def test_push_adds_version(self, status_get, wait_st):
|
||||
"""Assert that if heat_template_version is omitted, it's added."""
|
||||
# Note that the 'with x as y, a as b:' syntax was introduced in
|
||||
# python 2.7, and contextlib.nested was deprecated in py2.7
|
||||
with mock.patch(MOD_NAME + '.HeatStack._get_status') as status_get:
|
||||
with mock.patch(MOD_NAME + '.HeatStack._wait_state') as wait_st:
|
||||
|
||||
status_get.return_value = 'NOT_FOUND'
|
||||
wait_st.return_value = {}
|
||||
status_get.return_value = 'NOT_FOUND'
|
||||
wait_st.return_value = {}
|
||||
|
||||
context = {constants.CTX_ENVIRONMENT: self.environment_mock}
|
||||
context = {constants.CTX_ENVIRONMENT: self.environment_mock}
|
||||
|
||||
with helpers.contextual(context):
|
||||
hs = heat_stack.HeatStack(
|
||||
'test-stack', 'Generated by TestHeatStack')
|
||||
hs._template = {'resources': {'test': 1}}
|
||||
hs._files = {}
|
||||
hs._parameters = {}
|
||||
hs._applied = False
|
||||
hs.push()
|
||||
with helpers.contextual(context):
|
||||
hs = heat_stack.HeatStack(
|
||||
'test-stack', 'Generated by TestHeatStack')
|
||||
hs._template = {'resources': {'test': 1}}
|
||||
hs._files = {}
|
||||
hs._hot_environment = ''
|
||||
hs._parameters = {}
|
||||
hs._applied = False
|
||||
hs.push()
|
||||
|
||||
expected_template = {
|
||||
'heat_template_version': '2013-05-23',
|
||||
'description': 'Generated by TestHeatStack',
|
||||
'resources': {'test': 1}
|
||||
}
|
||||
self.heat_client_mock.stacks.create.assert_called_with(
|
||||
stack_name='test-stack',
|
||||
disable_rollback=True,
|
||||
parameters={},
|
||||
template=expected_template,
|
||||
files={}
|
||||
)
|
||||
self.assertTrue(hs._applied)
|
||||
with helpers.contextual(context):
|
||||
hs = heat_stack.HeatStack(
|
||||
'test-stack', 'Generated by TestHeatStack')
|
||||
hs._template = {'resources': {'test': 1}}
|
||||
hs._files = {}
|
||||
hs._parameters = {}
|
||||
hs._applied = False
|
||||
hs.push()
|
||||
|
||||
def test_description_is_optional(self):
|
||||
expected_template = {
|
||||
'heat_template_version': '2013-05-23',
|
||||
'description': 'Generated by TestHeatStack',
|
||||
'resources': {'test': 1}
|
||||
}
|
||||
self.heat_client_mock.stacks.create.assert_called_with(
|
||||
stack_name='test-stack',
|
||||
disable_rollback=True,
|
||||
parameters={},
|
||||
template=expected_template,
|
||||
files={},
|
||||
environment=''
|
||||
)
|
||||
self.assertTrue(hs._applied)
|
||||
|
||||
@mock.patch(MOD_NAME + '.HeatStack._wait_state')
|
||||
@mock.patch(MOD_NAME + '.HeatStack._get_status')
|
||||
def test_description_is_optional(self, status_get, wait_st):
|
||||
"""Assert that if heat_template_version is omitted, it's added."""
|
||||
# Note that the 'with x as y, a as b:' syntax was introduced in
|
||||
# python 2.7, and contextlib.nested was deprecated in py2.7
|
||||
with mock.patch(MOD_NAME + '.HeatStack._get_status') as status_get:
|
||||
with mock.patch(MOD_NAME + '.HeatStack._wait_state') as wait_st:
|
||||
|
||||
status_get.return_value = 'NOT_FOUND'
|
||||
wait_st.return_value = {}
|
||||
context = {constants.CTX_ENVIRONMENT: self.environment_mock}
|
||||
status_get.return_value = 'NOT_FOUND'
|
||||
wait_st.return_value = {}
|
||||
context = {constants.CTX_ENVIRONMENT: self.environment_mock}
|
||||
|
||||
with helpers.contextual(context):
|
||||
hs = heat_stack.HeatStack('test-stack', None)
|
||||
hs._template = {'resources': {'test': 1}}
|
||||
hs._files = {}
|
||||
hs._parameters = {}
|
||||
hs._applied = False
|
||||
hs.push()
|
||||
with helpers.contextual(context):
|
||||
hs = heat_stack.HeatStack('test-stack', None)
|
||||
hs._template = {'resources': {'test': 1}}
|
||||
hs._files = {}
|
||||
hs._hot_environment = ''
|
||||
hs._parameters = {}
|
||||
hs._applied = False
|
||||
hs.push()
|
||||
|
||||
expected_template = {
|
||||
'heat_template_version': '2013-05-23',
|
||||
'resources': {'test': 1}
|
||||
}
|
||||
self.heat_client_mock.stacks.create.assert_called_with(
|
||||
stack_name='test-stack',
|
||||
disable_rollback=True,
|
||||
parameters={},
|
||||
template=expected_template,
|
||||
files={}
|
||||
)
|
||||
self.assertTrue(hs._applied)
|
||||
expected_template = {
|
||||
'heat_template_version': '2013-05-23',
|
||||
'resources': {'test': 1}
|
||||
}
|
||||
self.heat_client_mock.stacks.create.assert_called_with(
|
||||
stack_name='test-stack',
|
||||
disable_rollback=True,
|
||||
parameters={},
|
||||
template=expected_template,
|
||||
files={},
|
||||
environment=''
|
||||
)
|
||||
self.assertTrue(hs._applied)
|
||||
|
||||
def test_heat_files_are_sent(self):
|
||||
@mock.patch(MOD_NAME + '.HeatStack._wait_state')
|
||||
@mock.patch(MOD_NAME + '.HeatStack._get_status')
|
||||
def test_heat_files_are_sent(self, status_get, wait_st):
|
||||
"""Assert that if heat_template_version is omitted, it's added."""
|
||||
# Note that the 'with x as y, a as b:' syntax was introduced in
|
||||
# python 2.7, and contextlib.nested was deprecated in py2.7
|
||||
with mock.patch(MOD_NAME + '.HeatStack._get_status') as status_get:
|
||||
with mock.patch(MOD_NAME + '.HeatStack._wait_state') as wait_st:
|
||||
|
||||
status_get.return_value = 'NOT_FOUND'
|
||||
wait_st.return_value = {}
|
||||
context = {constants.CTX_ENVIRONMENT: self.environment_mock}
|
||||
status_get.return_value = 'NOT_FOUND'
|
||||
wait_st.return_value = {}
|
||||
context = {constants.CTX_ENVIRONMENT: self.environment_mock}
|
||||
|
||||
with helpers.contextual(context):
|
||||
hs = heat_stack.HeatStack('test-stack', None)
|
||||
hs._description = None
|
||||
hs._template = {'resources': {'test': 1}}
|
||||
hs._files = {"heatFile": "file"}
|
||||
hs._parameters = {}
|
||||
hs._applied = False
|
||||
hs.push()
|
||||
with helpers.contextual(context):
|
||||
hs = heat_stack.HeatStack('test-stack', None)
|
||||
hs._description = None
|
||||
hs._template = {'resources': {'test': 1}}
|
||||
hs._files = {"heatFile": "file"}
|
||||
hs._hot_environment = ''
|
||||
hs._parameters = {}
|
||||
hs._applied = False
|
||||
hs.push()
|
||||
|
||||
expected_template = {
|
||||
'heat_template_version': '2013-05-23',
|
||||
'resources': {'test': 1}
|
||||
}
|
||||
self.heat_client_mock.stacks.create.assert_called_with(
|
||||
stack_name='test-stack',
|
||||
disable_rollback=True,
|
||||
parameters={},
|
||||
template=expected_template,
|
||||
files={"heatFile": "file"}
|
||||
)
|
||||
self.assertTrue(hs._applied)
|
||||
expected_template = {
|
||||
'heat_template_version': '2013-05-23',
|
||||
'resources': {'test': 1}
|
||||
}
|
||||
self.heat_client_mock.stacks.create.assert_called_with(
|
||||
stack_name='test-stack',
|
||||
disable_rollback=True,
|
||||
parameters={},
|
||||
template=expected_template,
|
||||
files={"heatFile": "file"},
|
||||
environment=''
|
||||
)
|
||||
self.assertTrue(hs._applied)
|
||||
|
||||
def test_update_wrong_template_version(self):
|
||||
@mock.patch(MOD_NAME + '.HeatStack._wait_state')
|
||||
@mock.patch(MOD_NAME + '.HeatStack._get_status')
|
||||
def test_heat_environments_are_sent(self, status_get, wait_st):
|
||||
"""Assert that if heat_template_version is omitted, it's added."""
|
||||
|
||||
status_get.return_value = 'NOT_FOUND'
|
||||
wait_st.return_value = {}
|
||||
context = {constants.CTX_ENVIRONMENT: self.environment_mock}
|
||||
|
||||
with helpers.contextual(context):
|
||||
hs = heat_stack.HeatStack('test-stack', None)
|
||||
hs._description = None
|
||||
hs._template = {'resources': {'test': 1}}
|
||||
hs._files = {"heatFile": "file"}
|
||||
hs._hot_environment = 'environments'
|
||||
hs._parameters = {}
|
||||
hs._applied = False
|
||||
hs.push()
|
||||
|
||||
expected_template = {
|
||||
'heat_template_version': '2013-05-23',
|
||||
'resources': {'test': 1}
|
||||
}
|
||||
self.heat_client_mock.stacks.create.assert_called_with(
|
||||
stack_name='test-stack',
|
||||
disable_rollback=True,
|
||||
parameters={},
|
||||
template=expected_template,
|
||||
files={"heatFile": "file"},
|
||||
environment='environments',
|
||||
)
|
||||
self.assertTrue(hs._applied)
|
||||
|
||||
@mock.patch(MOD_NAME + '.HeatStack.current')
|
||||
def test_update_wrong_template_version(self, current):
|
||||
"""Template version other than expected should cause error."""
|
||||
|
||||
context = {constants.CTX_ENVIRONMENT: self.environment_mock}
|
||||
|
@ -161,22 +203,21 @@ class TestHeatStack(base.MuranoTestCase):
|
|||
'heat_template_version': 'something else'
|
||||
}
|
||||
|
||||
with mock.patch(MOD_NAME + '.HeatStack.current') as current:
|
||||
current.return_value = {}
|
||||
current.return_value = {}
|
||||
|
||||
e = self.assertRaises(heat_stack.HeatStackError,
|
||||
hs.update_template,
|
||||
invalid_template)
|
||||
err_msg = "Currently only heat_template_version 2013-05-23 "\
|
||||
"is supported."
|
||||
self.assertEqual(err_msg, str(e))
|
||||
e = self.assertRaises(heat_stack.HeatStackError,
|
||||
hs.update_template,
|
||||
invalid_template)
|
||||
err_msg = "Currently only heat_template_version 2013-05-23 "\
|
||||
"is supported."
|
||||
self.assertEqual(err_msg, str(e))
|
||||
|
||||
# Check it's ok without a version
|
||||
hs.update_template({})
|
||||
expected = {'resources': {'test': 1}}
|
||||
self.assertEqual(expected, hs._template)
|
||||
# Check it's ok without a version
|
||||
hs.update_template({})
|
||||
expected = {'resources': {'test': 1}}
|
||||
self.assertEqual(expected, hs._template)
|
||||
|
||||
# .. or with a good version
|
||||
hs.update_template({'heat_template_version': '2013-05-23'})
|
||||
expected['heat_template_version'] = '2013-05-23'
|
||||
self.assertEqual(expected, hs._template)
|
||||
# .. or with a good version
|
||||
hs.update_template({'heat_template_version': '2013-05-23'})
|
||||
expected['heat_template_version'] = '2013-05-23'
|
||||
self.assertEqual(expected, hs._template)
|
||||
|
|
Loading…
Reference in New Issue