"version" section should be required in template
"HeatTemplateFormatVersion" as default while a template missing version section now in heat. But then the server can't parse the template in right way, not translate hot format to cfn. Some errors raised such as: Error : At least one Resources member must be defined. but "resources" section has one resource. Error: The Parameter (image_id) was not defined in template. but "image_id" was defined in "parameter" section. So if we cannot determine the template version, we should raise an exception. This patch will check whether version section is in template, if not we should raise exception. Change-Id: Idc2ba2b9ae099d4509dfd81914aa9bb8bd3bfefb Closes-Bug: #1267735
This commit is contained in:
parent
f4337bc58c
commit
ae5647f8d5
@ -28,7 +28,7 @@ from ..plugin import docker_container # noqa
|
||||
|
||||
template = '''
|
||||
{
|
||||
"AWSTemplateFormatVersion" : "2010-09-09",
|
||||
"AWSTemplateFormatVersion": "2010-09-09",
|
||||
"Description": "Test template",
|
||||
"Parameters": {},
|
||||
"Resources": {
|
||||
|
@ -169,6 +169,7 @@ class FakeAutoScale(object):
|
||||
class ScalingGroupTest(HeatTestCase):
|
||||
|
||||
group_template = template_format.parse('''
|
||||
HeatTemplateFormatVersion: "2012-12-12"
|
||||
Description: "Rackspace Auto Scale"
|
||||
Parameters: {}
|
||||
Resources:
|
||||
@ -330,6 +331,7 @@ class ScalingGroupTest(HeatTestCase):
|
||||
|
||||
class PolicyTest(HeatTestCase):
|
||||
policy_template = template_format.parse('''
|
||||
HeatTemplateFormatVersion: "2012-12-12"
|
||||
Description: "Rackspace Auto Scale"
|
||||
Parameters: {}
|
||||
Resources:
|
||||
@ -472,6 +474,7 @@ class PolicyTest(HeatTestCase):
|
||||
|
||||
class WebHookTest(HeatTestCase):
|
||||
webhook_template = template_format.parse('''
|
||||
HeatTemplateFormatVersion: "2012-12-12"
|
||||
Description: "Rackspace Auto Scale"
|
||||
Parameters: {}
|
||||
Resources:
|
||||
|
@ -69,28 +69,14 @@ def parse(tmpl_str):
|
||||
else:
|
||||
if tpl is None:
|
||||
tpl = {}
|
||||
if u'heat_template_version' not in tpl:
|
||||
default_for_missing(tpl, u'HeatTemplateFormatVersion',
|
||||
HEAT_VERSIONS)
|
||||
# Looking for supported version keys in the loaded template
|
||||
if not ('HeatTemplateFormatVersion' in tpl
|
||||
or 'heat_template_version' in tpl
|
||||
or 'AWSTemplateFormatVersion' in tpl):
|
||||
raise ValueError(_("Template format version not found."))
|
||||
return tpl
|
||||
|
||||
|
||||
def default_for_missing(tpl, version_param, versions):
|
||||
'''
|
||||
Checks a parsed template for missing version and sections.
|
||||
|
||||
This is currently only applied to YAML templates.
|
||||
'''
|
||||
# if version is missing, implicitly use the lastest one
|
||||
if version_param not in tpl:
|
||||
tpl[version_param] = versions[-1]
|
||||
|
||||
# create empty placeholders for any of the main dict sections
|
||||
for param in (u'Parameters', u'Mappings', u'Resources', u'Outputs'):
|
||||
if param not in tpl:
|
||||
tpl[param] = {}
|
||||
|
||||
|
||||
def convert_json_to_yaml(json_str):
|
||||
'''Convert a string containing the AWS JSON template format
|
||||
to an equivalent string containing the Heat YAML format.
|
||||
|
@ -3,10 +3,6 @@
|
||||
|
||||
"Description" : "Template to test Neutron resources",
|
||||
|
||||
"Parameters" : {
|
||||
|
||||
},
|
||||
|
||||
"Resources" : {
|
||||
"network": {
|
||||
"Type": "OS::Neutron::Net",
|
||||
|
@ -30,6 +30,7 @@ from heat.tests.common import HeatTestCase
|
||||
from heat.tests import utils
|
||||
|
||||
policy_path = os.path.dirname(os.path.realpath(__file__)) + "/policy/"
|
||||
template = {u'AWSTemplateFormatVersion': u'2010-09-09', u'Foo': u'bar'}
|
||||
|
||||
|
||||
class CfnStackControllerTest(HeatTestCase):
|
||||
@ -464,7 +465,6 @@ class CfnStackControllerTest(HeatTestCase):
|
||||
def test_create(self):
|
||||
# Format a dummy request
|
||||
stack_name = "wordpress"
|
||||
template = {u'Foo': u'bar'}
|
||||
json_template = json.dumps(template)
|
||||
params = {'Action': 'CreateStack', 'StackName': stack_name,
|
||||
'TemplateBody': '%s' % json_template,
|
||||
@ -511,7 +511,6 @@ class CfnStackControllerTest(HeatTestCase):
|
||||
def test_create_rollback(self):
|
||||
# Format a dummy request
|
||||
stack_name = "wordpress"
|
||||
template = {u'Foo': u'bar'}
|
||||
json_template = json.dumps(template)
|
||||
params = {'Action': 'CreateStack', 'StackName': stack_name,
|
||||
'TemplateBody': '%s' % json_template,
|
||||
@ -558,7 +557,6 @@ class CfnStackControllerTest(HeatTestCase):
|
||||
def test_create_onfailure_true(self):
|
||||
# Format a dummy request
|
||||
stack_name = "wordpress"
|
||||
template = {u'Foo': u'bar'}
|
||||
json_template = json.dumps(template)
|
||||
params = {'Action': 'CreateStack', 'StackName': stack_name,
|
||||
'TemplateBody': '%s' % json_template,
|
||||
@ -605,7 +603,6 @@ class CfnStackControllerTest(HeatTestCase):
|
||||
def test_create_onfailure_false_delete(self):
|
||||
# Format a dummy request
|
||||
stack_name = "wordpress"
|
||||
template = {u'Foo': u'bar'}
|
||||
json_template = json.dumps(template)
|
||||
params = {'Action': 'CreateStack', 'StackName': stack_name,
|
||||
'TemplateBody': '%s' % json_template,
|
||||
@ -652,7 +649,6 @@ class CfnStackControllerTest(HeatTestCase):
|
||||
def test_create_onfailure_false_rollback(self):
|
||||
# Format a dummy request
|
||||
stack_name = "wordpress"
|
||||
template = {u'Foo': u'bar'}
|
||||
json_template = json.dumps(template)
|
||||
params = {'Action': 'CreateStack', 'StackName': stack_name,
|
||||
'TemplateBody': '%s' % json_template,
|
||||
@ -699,7 +695,6 @@ class CfnStackControllerTest(HeatTestCase):
|
||||
def test_create_onfailure_err(self):
|
||||
# Format a dummy request
|
||||
stack_name = "wordpress"
|
||||
template = {u'Foo': u'bar'}
|
||||
json_template = json.dumps(template)
|
||||
params = {'Action': 'CreateStack', 'StackName': stack_name,
|
||||
'TemplateBody': '%s' % json_template,
|
||||
@ -739,7 +734,6 @@ class CfnStackControllerTest(HeatTestCase):
|
||||
def test_create_err_rpcerr(self):
|
||||
# Format a dummy request
|
||||
stack_name = "wordpress"
|
||||
template = {u'Foo': u'bar'}
|
||||
json_template = json.dumps(template)
|
||||
params = {'Action': 'CreateStack', 'StackName': stack_name,
|
||||
'TemplateBody': '%s' % json_template,
|
||||
@ -809,7 +803,6 @@ class CfnStackControllerTest(HeatTestCase):
|
||||
def test_create_err_exists(self):
|
||||
# Format a dummy request
|
||||
stack_name = "wordpress"
|
||||
template = {u'Foo': u'bar'}
|
||||
json_template = json.dumps(template)
|
||||
params = {'Action': 'CreateStack', 'StackName': stack_name,
|
||||
'TemplateBody': '%s' % json_template,
|
||||
@ -845,7 +838,6 @@ class CfnStackControllerTest(HeatTestCase):
|
||||
def test_create_err_engine(self):
|
||||
# Format a dummy request
|
||||
stack_name = "wordpress"
|
||||
template = {u'Foo': u'bar'}
|
||||
json_template = json.dumps(template)
|
||||
params = {'Action': 'CreateStack', 'StackName': stack_name,
|
||||
'TemplateBody': '%s' % json_template,
|
||||
@ -881,7 +873,6 @@ class CfnStackControllerTest(HeatTestCase):
|
||||
def test_update(self):
|
||||
# Format a dummy request
|
||||
stack_name = "wordpress"
|
||||
template = {u'Foo': u'bar'}
|
||||
json_template = json.dumps(template)
|
||||
params = {'Action': 'UpdateStack', 'StackName': stack_name,
|
||||
'TemplateBody': '%s' % json_template,
|
||||
@ -929,7 +920,6 @@ class CfnStackControllerTest(HeatTestCase):
|
||||
|
||||
def test_update_bad_name(self):
|
||||
stack_name = "wibble"
|
||||
template = {u'Foo': u'bar'}
|
||||
json_template = json.dumps(template)
|
||||
params = {'Action': 'UpdateStack', 'StackName': stack_name,
|
||||
'TemplateBody': '%s' % json_template,
|
||||
@ -961,7 +951,6 @@ class CfnStackControllerTest(HeatTestCase):
|
||||
# Format a dummy request
|
||||
stack_name = "wordpress"
|
||||
identity = dict(identifier.HeatIdentifier('t', stack_name, '6'))
|
||||
template = {u'Foo': u'bar'}
|
||||
params = {'Action': 'GetTemplate', 'StackName': stack_name}
|
||||
dummy_req = self._dummy_GET_request(params)
|
||||
self._stub_enforce(dummy_req, 'GetTemplate')
|
||||
@ -1092,12 +1081,10 @@ class CfnStackControllerTest(HeatTestCase):
|
||||
def test_bad_resources_in_template(self):
|
||||
# Format a dummy request
|
||||
json_template = {
|
||||
'template': {
|
||||
'AWSTemplateFormatVersion': '2010-09-09',
|
||||
'Resources': {
|
||||
'Type': 'AWS: : EC2: : Instance',
|
||||
},
|
||||
}
|
||||
'AWSTemplateFormatVersion': '2010-09-09',
|
||||
'Resources': {
|
||||
'Type': 'AWS: : EC2: : Instance',
|
||||
},
|
||||
}
|
||||
params = {'Action': 'ValidateTemplate',
|
||||
'TemplateBody': '%s' % json.dumps(json_template)}
|
||||
|
@ -64,8 +64,12 @@ def to_remote_error(error):
|
||||
class InstantiationDataTest(HeatTestCase):
|
||||
|
||||
def test_format_parse(self):
|
||||
data = {"key1": ["val1[0]", "val1[1]"], "key2": "val2"}
|
||||
json_repr = '{ "key1": [ "val1[0]", "val1[1]" ], "key2": "val2" }'
|
||||
data = {"AWSTemplateFormatVersion": "2010-09-09",
|
||||
"key1": ["val1[0]", "val1[1]"],
|
||||
"key2": "val2"}
|
||||
json_repr = '{"AWSTemplateFormatVersion" : "2010-09-09",' \
|
||||
'"key1": [ "val1[0]", "val1[1]" ], ' \
|
||||
'"key2": "val2" }'
|
||||
parsed = stacks.InstantiationData.format_parse(json_repr, 'foo')
|
||||
self.assertEqual(parsed, data)
|
||||
|
||||
@ -77,6 +81,7 @@ class InstantiationDataTest(HeatTestCase):
|
||||
def test_format_parse_invalid_message(self):
|
||||
# make sure the parser error gets through to the caller.
|
||||
bad_temp = '''
|
||||
heat_template_version: '2012-12-12'
|
||||
parameters:
|
||||
KeyName:
|
||||
type: string
|
||||
@ -86,7 +91,7 @@ parameters:
|
||||
parse_ex = self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
stacks.InstantiationData.format_parse,
|
||||
bad_temp, 'foo')
|
||||
self.assertIn('line 3, column 3', str(parse_ex))
|
||||
self.assertIn('line 4, column 3', str(parse_ex))
|
||||
|
||||
def test_stack_name(self):
|
||||
body = {'stack_name': 'wibble'}
|
||||
@ -105,20 +110,18 @@ parameters:
|
||||
self.assertEqual(data.template(), template)
|
||||
|
||||
def test_template_string_json(self):
|
||||
template = '{"foo": "bar", "blarg": "wibble"}'
|
||||
template = '{"heat_template_version": "2012-12-12",' \
|
||||
'"foo": "bar", "blarg": "wibble"}'
|
||||
body = {'template': template}
|
||||
data = stacks.InstantiationData(body)
|
||||
self.assertEqual(data.template(), json.loads(template))
|
||||
|
||||
def test_template_string_yaml(self):
|
||||
template = '''foo: bar
|
||||
template = '''HeatTemplateFormatVersion: 2012-12-12
|
||||
foo: bar
|
||||
blarg: wibble
|
||||
'''
|
||||
parsed = {u'HeatTemplateFormatVersion': u'2012-12-12',
|
||||
u'Mappings': {},
|
||||
u'Outputs': {},
|
||||
u'Parameters': {},
|
||||
u'Resources': {},
|
||||
u'blarg': u'wibble',
|
||||
u'foo': u'bar'}
|
||||
|
||||
@ -127,7 +130,9 @@ blarg: wibble
|
||||
self.assertEqual(data.template(), parsed)
|
||||
|
||||
def test_template_url(self):
|
||||
template = {'foo': 'bar', 'blarg': 'wibble'}
|
||||
template = {'heat_template_version': '2013-05-23',
|
||||
'foo': 'bar',
|
||||
'blarg': 'wibble'}
|
||||
url = 'http://example.com/template'
|
||||
body = {'template_url': url}
|
||||
data = stacks.InstantiationData(body)
|
||||
|
@ -320,7 +320,7 @@ Outputs:
|
||||
|
||||
def test_nested_stack_three_deep(self):
|
||||
root_template = '''
|
||||
HeatTemplateFormat: 2012-12-12
|
||||
HeatTemplateFormatVersion: 2012-12-12
|
||||
Resources:
|
||||
Nested:
|
||||
Type: AWS::CloudFormation::Stack
|
||||
@ -328,7 +328,7 @@ Resources:
|
||||
TemplateURL: 'https://server.test/depth1.template'
|
||||
'''
|
||||
depth1_template = '''
|
||||
HeatTemplateFormat: 2012-12-12
|
||||
HeatTemplateFormatVersion: 2012-12-12
|
||||
Resources:
|
||||
Nested:
|
||||
Type: AWS::CloudFormation::Stack
|
||||
@ -336,7 +336,7 @@ Resources:
|
||||
TemplateURL: 'https://server.test/depth2.template'
|
||||
'''
|
||||
depth2_template = '''
|
||||
HeatTemplateFormat: 2012-12-12
|
||||
HeatTemplateFormatVersion: 2012-12-12
|
||||
Resources:
|
||||
Nested:
|
||||
Type: AWS::CloudFormation::Stack
|
||||
@ -360,7 +360,7 @@ Resources:
|
||||
|
||||
def test_nested_stack_four_deep(self):
|
||||
root_template = '''
|
||||
HeatTemplateFormat: 2012-12-12
|
||||
HeatTemplateFormatVersion: 2012-12-12
|
||||
Resources:
|
||||
Nested:
|
||||
Type: AWS::CloudFormation::Stack
|
||||
@ -368,7 +368,7 @@ Resources:
|
||||
TemplateURL: 'https://server.test/depth1.template'
|
||||
'''
|
||||
depth1_template = '''
|
||||
HeatTemplateFormat: 2012-12-12
|
||||
HeatTemplateFormatVersion: 2012-12-12
|
||||
Resources:
|
||||
Nested:
|
||||
Type: AWS::CloudFormation::Stack
|
||||
@ -376,7 +376,7 @@ Resources:
|
||||
TemplateURL: 'https://server.test/depth2.template'
|
||||
'''
|
||||
depth2_template = '''
|
||||
HeatTemplateFormat: 2012-12-12
|
||||
HeatTemplateFormatVersion: 2012-12-12
|
||||
Resources:
|
||||
Nested:
|
||||
Type: AWS::CloudFormation::Stack
|
||||
@ -384,7 +384,7 @@ Resources:
|
||||
TemplateURL: 'https://server.test/depth3.template'
|
||||
'''
|
||||
depth3_template = '''
|
||||
HeatTemplateFormat: 2012-12-12
|
||||
HeatTemplateFormatVersion: 2012-12-12
|
||||
Resources:
|
||||
Nested:
|
||||
Type: AWS::CloudFormation::Stack
|
||||
@ -415,7 +415,7 @@ Resources:
|
||||
|
||||
def test_nested_stack_four_wide(self):
|
||||
root_template = '''
|
||||
HeatTemplateFormat: 2012-12-12
|
||||
HeatTemplateFormatVersion: 2012-12-12
|
||||
Resources:
|
||||
Nested:
|
||||
Type: AWS::CloudFormation::Stack
|
||||
@ -460,7 +460,7 @@ Resources:
|
||||
|
||||
def test_nested_stack_infinite_recursion(self):
|
||||
template = '''
|
||||
HeatTemplateFormat: 2012-12-12
|
||||
HeatTemplateFormatVersion: 2012-12-12
|
||||
Resources:
|
||||
Nested:
|
||||
Type: AWS::CloudFormation::Stack
|
||||
|
@ -104,6 +104,7 @@ class ParserTest(HeatTestCase):
|
||||
|
||||
|
||||
mapping_template = template_format.parse('''{
|
||||
"AWSTemplateFormatVersion" : "2010-09-09",
|
||||
"Mappings" : {
|
||||
"ValidMapping" : {
|
||||
"TestKey" : { "TestValue" : "wibble" }
|
||||
@ -787,10 +788,12 @@ class StackTest(HeatTestCase):
|
||||
self.assertEqual(1, stack.total_resources())
|
||||
|
||||
def _setup_nested(self, name):
|
||||
nested_tpl = ('{"Resources":{'
|
||||
nested_tpl = ('{"HeatTemplateFormatVersion" : "2012-12-12",'
|
||||
'"Resources":{'
|
||||
'"A": {"Type": "GenericResourceType"},'
|
||||
'"B": {"Type": "GenericResourceType"}}}')
|
||||
tpl = {'Resources':
|
||||
tpl = {'HeatTemplateFormatVersion': "2012-12-12",
|
||||
'Resources':
|
||||
{'A': {'Type': 'AWS::CloudFormation::Stack',
|
||||
'Properties':
|
||||
{'TemplateURL': 'http://server.test/nested.json'}},
|
||||
|
@ -92,6 +92,7 @@ class ProviderTemplateTest(HeatTestCase):
|
||||
def test_to_parameters(self):
|
||||
"""Tests property conversion to parameter values."""
|
||||
provider = {
|
||||
'HeatTemplateFormatVersion': '2012-12-12',
|
||||
'Parameters': {
|
||||
'Foo': {'Type': 'String'},
|
||||
'AList': {'Type': 'CommaDelimitedList'},
|
||||
@ -163,6 +164,7 @@ class ProviderTemplateTest(HeatTestCase):
|
||||
|
||||
def test_attributes_extra(self):
|
||||
provider = {
|
||||
'HeatTemplateFormatVersion': '2012-12-12',
|
||||
'Outputs': {
|
||||
'Foo': {'Value': 'bar'},
|
||||
'Blarg': {'Value': 'wibble'},
|
||||
@ -221,6 +223,7 @@ class ProviderTemplateTest(HeatTestCase):
|
||||
|
||||
def test_properties_normal(self):
|
||||
provider = {
|
||||
'HeatTemplateFormatVersion': '2012-12-12',
|
||||
'Parameters': {
|
||||
'Foo': {'Type': 'String'},
|
||||
'Blarg': {'Type': 'String', 'Default': 'wibble'},
|
||||
@ -417,7 +420,9 @@ class ProviderTemplateTest(HeatTestCase):
|
||||
parser.Template({}),
|
||||
stack_id=str(uuid.uuid4()))
|
||||
|
||||
minimal_temp = json.dumps({'Parameters': {}, 'Resources': {}})
|
||||
minimal_temp = json.dumps({'HeatTemplateFormatVersion': '2012-12-12',
|
||||
'Parameters': {},
|
||||
'Resources': {}})
|
||||
self.m.StubOutWithMock(urlfetch, "get")
|
||||
urlfetch.get(test_templ_name,
|
||||
allowed_schemes=('http', 'https',
|
||||
|
@ -35,6 +35,7 @@ ws_res_snippet = {"Type": "some_magic_type",
|
||||
|
||||
param_template = '''
|
||||
{
|
||||
"AWSTemplateFormatVersion" : "2010-09-09",
|
||||
"Parameters" : {
|
||||
"KeyName" : {
|
||||
"Description" : "KeyName",
|
||||
@ -54,6 +55,7 @@ param_template = '''
|
||||
|
||||
simple_template = '''
|
||||
{
|
||||
"AWSTemplateFormatVersion" : "2010-09-09",
|
||||
"Parameters" : {},
|
||||
"Resources" : {
|
||||
"WebServer": {
|
||||
|
@ -60,8 +60,6 @@ class JsonToYamlTest(HeatTestCase):
|
||||
del(yml[u'HeatTemplateFormatVersion'])
|
||||
|
||||
jsn = template_format.parse(json_str)
|
||||
template_format.default_for_missing(jsn, 'AWSTemplateFormatVersion',
|
||||
template_format.CFN_VERSIONS)
|
||||
|
||||
if u'AWSTemplateFormatVersion' in jsn:
|
||||
del(jsn[u'AWSTemplateFormatVersion'])
|
||||
@ -81,18 +79,6 @@ class JsonToYamlTest(HeatTestCase):
|
||||
|
||||
class YamlMinimalTest(HeatTestCase):
|
||||
|
||||
def test_minimal_yaml(self):
|
||||
yaml1 = ''
|
||||
yaml2 = '''HeatTemplateFormatVersion: '2012-12-12'
|
||||
Parameters: {}
|
||||
Mappings: {}
|
||||
Resources: {}
|
||||
Outputs: {}
|
||||
'''
|
||||
tpl1 = template_format.parse(yaml1)
|
||||
tpl2 = template_format.parse(yaml2)
|
||||
self.assertEqual(tpl1, tpl2)
|
||||
|
||||
def test_long_yaml(self):
|
||||
template = {'HeatTemplateVersion': '2012-12-12'}
|
||||
config.cfg.CONF.set_override('max_template_size', 1024)
|
||||
@ -105,6 +91,16 @@ Outputs: {}
|
||||
msg = 'Request limit exceeded: Template exceeds maximum allowed size.'
|
||||
self.assertEqual(msg, str(ex))
|
||||
|
||||
def test_parse_no_version_format(self):
|
||||
yaml = ''
|
||||
self.assertRaises(ValueError, template_format.parse, yaml)
|
||||
yaml2 = '''Parameters: {}
|
||||
Mappings: {}
|
||||
Resources: {}
|
||||
Outputs: {}
|
||||
'''
|
||||
self.assertRaises(ValueError, template_format.parse, yaml2)
|
||||
|
||||
|
||||
class YamlParseExceptions(HeatTestCase):
|
||||
|
||||
@ -143,13 +139,9 @@ class JsonYamlResolvedCompareTest(HeatTestCase):
|
||||
|
||||
def compare_stacks(self, json_file, yaml_file, parameters):
|
||||
t1 = self.load_template(json_file)
|
||||
template_format.default_for_missing(t1, 'AWSTemplateFormatVersion',
|
||||
template_format.CFN_VERSIONS)
|
||||
del(t1[u'AWSTemplateFormatVersion'])
|
||||
|
||||
t2 = self.load_template(yaml_file)
|
||||
del(t2[u'HeatTemplateFormatVersion'])
|
||||
|
||||
del(t1[u'AWSTemplateFormatVersion'])
|
||||
stack1 = utils.parse_stack(t1, parameters)
|
||||
stack2 = utils.parse_stack(t2, parameters)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user