From 11c6c3b7d77fb5bef6cdefb3b21dc966314c3692 Mon Sep 17 00:00:00 2001 From: Davanum Srinivas Date: Tue, 23 Jul 2013 23:14:16 -0400 Subject: [PATCH] Fix for bad content inside Resources element In validate_template check if we have a dict before we try to get 'Type'. We also need to check the response back from validate_template to see if there are any 'Error'(s) and present that back to the user. Fixes LP# 1204157 Change-Id: I0f636a6a84f6afc9a0aff3089e2bd13a8f88a470 --- heat/api/cfn/v1/stacks.py | 4 ++++ heat/engine/service.py | 14 +++++++++++-- heat/tests/test_api_cfn_v1.py | 34 ++++++++++++++++++++++++++++++++ heat/tests/test_validate.py | 37 +++++++++++++++++++++++++++++++++++ 4 files changed, 87 insertions(+), 2 deletions(-) diff --git a/heat/api/cfn/v1/stacks.py b/heat/api/cfn/v1/stacks.py index 764e6b0b..4d89ecaf 100644 --- a/heat/api/cfn/v1/stacks.py +++ b/heat/api/cfn/v1/stacks.py @@ -438,6 +438,10 @@ class StackController(object): try: res = self.engine_rpcapi.validate_template(con, template) + if 'Error' in res: + return api_utils.format_response('ValidateTemplate', + res['Error']) + res['Parameters'] = [format_validate_parameter(k, v) for k, v in res['Parameters'].items()] return api_utils.format_response('ValidateTemplate', res) diff --git a/heat/engine/service.py b/heat/engine/service.py index add8e1a8..0c438eef 100644 --- a/heat/engine/service.py +++ b/heat/engine/service.py @@ -312,9 +312,19 @@ class EngineService(service.Service): return {'Error': 'At least one Resources member must be defined.'} for res in tmpl_resources.values(): - if not res.get('Type'): + try: + if not res.get('Type'): + return {'Error': + 'Every Resource object must ' + 'contain a Type member.'} + except AttributeError: + type_res = type(res) + if isinstance(res, unicode): + type_res = "string" return {'Error': - 'Every Resources object must contain a Type member.'} + 'Resources must contain Resource. ' + 'Found a [%s] instead' % type_res} + ResourceClass = resource.get_class(res['Type']) props = properties.Properties(ResourceClass.properties_schema, res.get('Properties', {})) diff --git a/heat/tests/test_api_cfn_v1.py b/heat/tests/test_api_cfn_v1.py index bb663efe..b302770e 100644 --- a/heat/tests/test_api_cfn_v1.py +++ b/heat/tests/test_api_cfn_v1.py @@ -1076,6 +1076,40 @@ class CfnStackControllerTest(HeatTestCase): self.assertEqual(type(result), exception.HeatInvalidParameterValueError) + def test_bad_resources_in_template(self): + # Format a dummy request + json_template = { + 'template': { + 'AWSTemplateFormatVersion': '2010-09-09', + 'Resources': { + 'Type': 'AWS: : EC2: : Instance', + }, + } + } + params = {'Action': 'ValidateTemplate', + 'TemplateBody': '%s' % json.dumps(json_template)} + response = {'Error': 'Resources must contain Resource. ' + 'Found a [string] instead'} + dummy_req = self._dummy_GET_request(params) + + # Stub out the RPC call to the engine with a pre-canned response + self.m.StubOutWithMock(rpc, 'call') + rpc.call(dummy_req.context, self.topic, + {'namespace': None, + 'method': 'validate_template', + 'args': {'template': json_template}, + 'version': self.api_version}, None).AndReturn(response) + self.m.ReplayAll() + + response = self.controller.validate_template(dummy_req) + + expected = {'ValidateTemplateResponse': + {'ValidateTemplateResult': + 'Resources must contain Resource. ' + 'Found a [string] instead'}} + self.assertEqual(expected, response) + self.m.VerifyAll() + def test_delete(self): # Format a dummy request stack_name = "wordpress" diff --git a/heat/tests/test_validate.py b/heat/tests/test_validate.py index 4c971f30..c815d043 100644 --- a/heat/tests/test_validate.py +++ b/heat/tests/test_validate.py @@ -211,6 +211,31 @@ test_template_findinmap_invalid = ''' } ''' +test_template_invalid_resources = ''' +{ + "AWSTemplateFormatVersion" : "2010-09-09", + "Description" : "AWS CloudFormation Sample Template for xyz.", + "Parameters" : { + "InstanceType" : { + "Description" : "Defined instance type", + "Type" : "String", + "Default" : "node.ee", + "AllowedValues" : ["node.ee", "node.apache", "node.api"], + "ConstraintDescription" : "must be a valid instance type." + } + }, + "Resources" : { + "Type" : "AWS::EC2::Instance", + "Metadata" : { + }, + "Properties" : { + "ImageId" : { "Ref" : "centos-6.4-20130701-0" }, + "InstanceType" : { "Ref" : "InstanceType" } + } + } +} +''' + test_template_invalid_property = ''' { "AWSTemplateFormatVersion" : "2010-09-09", @@ -605,6 +630,18 @@ class validateTest(HeatTestCase): res = dict(engine.validate_template(None, t)) self.assertEqual(res, {'Error': 'Unknown Property UnknownProperty'}) + def test_invalid_resources(self): + t = template_format.parse(test_template_invalid_resources) + self.m.StubOutWithMock(instances.Instance, 'nova') + instances.Instance.nova().AndReturn(self.fc) + self.m.ReplayAll() + + engine = service.EngineService('a', 't') + res = dict(engine.validate_template(None, t)) + self.assertEqual({'Error': 'Resources must contain Resource. ' + 'Found a [string] instead'}, + res) + def test_unimplemented_property(self): t = template_format.parse(test_template_unimplemented_property) self.m.StubOutWithMock(instances.Instance, 'nova') -- 2.45.2