From c6b1c61376de61445612370ae5eb8f97e2bcfc43 Mon Sep 17 00:00:00 2001 From: Steven Hardy Date: Thu, 19 Sep 2013 22:31:16 +0100 Subject: [PATCH] Tolerate bad environment until validation Currently if we get a bad environment for a template_resource, it causes an exception in the resource constructor, which does cause the stack create to fail, but also all subsequent operations too. By tolerating the error in the constructor, we can catch it instead at validation time. Change-Id: Ia971d8f1c50ca6f265ec36ea564aeba1638de541 Closes-Bug: 1227816 --- heat/engine/resources/template_resource.py | 14 ++++- heat/tests/test_provider_template.py | 68 ++++++++++++++++++++-- 2 files changed, 75 insertions(+), 7 deletions(-) diff --git a/heat/engine/resources/template_resource.py b/heat/engine/resources/template_resource.py index 50bb37f7..0a5cf7ec 100644 --- a/heat/engine/resources/template_resource.py +++ b/heat/engine/resources/template_resource.py @@ -50,7 +50,14 @@ class TemplateResource(stack_resource.StackResource): else: self.allowed_schemes = ('http', 'https', 'file') - tmpl = template.Template(self.parsed_nested) + # parse_nested can fail if the URL in the environment is bad + # or otherwise inaccessible. Suppress the error here so the + # stack can be deleted, and detect it at validate/create time + try: + tmpl = template.Template(self.parsed_nested) + except ValueError: + tmpl = template.Template({}) + self.properties_schema = (properties.Properties .schema_from_params(tmpl.param_schemata())) self.attributes_schema = (attributes.Attributes @@ -146,6 +153,11 @@ class TemplateResource(stack_resource.StackResource): raise exception.StackValidationFailed(message=msg) def validate(self): + try: + td = self.template_data + except ValueError as ex: + msg = _("Failed to retrieve template data: %s") % str(ex) + raise exception.StackValidationFailed(message=msg) cri = self.stack.env.get_resource_info( self.type(), registry_type=environment.ClassResourceInfo) diff --git a/heat/tests/test_provider_template.py b/heat/tests/test_provider_template.py index 828f2f08..08c96af7 100644 --- a/heat/tests/test_provider_template.py +++ b/heat/tests/test_provider_template.py @@ -425,7 +425,7 @@ class ProviderTemplateTest(HeatTestCase): def test_user_template_not_retrieved_by_file(self): # make sure that a TemplateResource defined in the user environment - # can NOT be retrieved using the "file:" scheme. + # can NOT be retrieved using the "file:" scheme, validation should fail env = environment.Environment() test_templ_name = 'file:///etc/heatr/flippy.yaml' env.load({'resource_registry': @@ -434,8 +434,64 @@ class ProviderTemplateTest(HeatTestCase): parser.Template({}), env=env, stack_id=uuidutils.generate_uuid()) - self.assertRaises(ValueError, - template_resource.TemplateResource, - 'test_t_res', - {"Type": 'Test::Flippy'}, - stack) + temp_res = template_resource.TemplateResource('test_t_res', + {"Type": 'Test::Flippy'}, + stack) + + self.assertRaises(exception.StackValidationFailed, temp_res.validate) + + def test_system_template_retrieve_fail(self): + # make sure that a TemplateResource defined in the global environment + # fails gracefully if the template file specified is inaccessible + # we should be able to create the TemplateResource object, but + # validation should fail, when the second attempt to access it is + # made in validate() + g_env = resources.global_env() + test_templ_name = 'file:///etc/heatr/frodo.yaml' + g_env.load({'resource_registry': + {'Test::Frodo': test_templ_name}}) + stack = parser.Stack(utils.dummy_context(), 'test_stack', + parser.Template({}), + stack_id=uuidutils.generate_uuid()) + + self.m.StubOutWithMock(urlfetch, "get") + urlfetch.get(test_templ_name, + allowed_schemes=('http', 'https', + 'file')).AndRaise(IOError) + urlfetch.get(test_templ_name, + allowed_schemes=('http', 'https', + 'file')).AndRaise(IOError) + self.m.ReplayAll() + + temp_res = template_resource.TemplateResource('test_t_res', + {"Type": 'Test::Frodo'}, + stack) + self.assertRaises(exception.StackValidationFailed, temp_res.validate) + self.m.VerifyAll() + + def test_user_template_retrieve_fail(self): + # make sure that a TemplateResource defined in the user environment + # fails gracefully if the template file specified is inaccessible + # we should be able to create the TemplateResource object, but + # validation should fail, when the second attempt to access it is + # made in validate() + env = environment.Environment() + test_templ_name = 'http://heatr/noexist.yaml' + env.load({'resource_registry': + {'Test::Flippy': test_templ_name}}) + stack = parser.Stack(utils.dummy_context(), 'test_stack', + parser.Template({}), env=env, + stack_id=uuidutils.generate_uuid()) + + self.m.StubOutWithMock(urlfetch, "get") + urlfetch.get(test_templ_name, + allowed_schemes=('http', 'https')).AndRaise(IOError) + urlfetch.get(test_templ_name, + allowed_schemes=('http', 'https')).AndRaise(IOError) + self.m.ReplayAll() + + temp_res = template_resource.TemplateResource('test_t_res', + {"Type": 'Test::Flippy'}, + stack) + self.assertRaises(exception.StackValidationFailed, temp_res.validate) + self.m.VerifyAll() -- 2.45.2