From b8aad78cc5389874056de547dcd26a198a3bbd03 Mon Sep 17 00:00:00 2001 From: Steven Hardy Date: Fri, 8 Mar 2013 17:23:17 +0000 Subject: [PATCH] heat engine : store stack on failed update Store the stack even when the update fails, otherwise we lose track of the resources which failed on create so they are not removed on stack delete fixes bug 1151989 Change-Id: Ic8aa5ef92e188fb704ed25563aa9b86aa69232b4 --- heat/engine/parser.py | 17 ++++++++++------- heat/tests/test_parser.py | 31 +++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 7 deletions(-) diff --git a/heat/engine/parser.py b/heat/engine/parser.py index f4ff716e..16dbec51 100644 --- a/heat/engine/parser.py +++ b/heat/engine/parser.py @@ -426,13 +426,6 @@ class Stack(object): raise exception.ResourceUpdateFailed( resource_name=res.name) - # flip the template & parameters to the newstack values - self.t = newstack.t - self.parameters = newstack.parameters - template_outputs = self.t[template.OUTPUTS] - self.outputs = self.resolve_static_data(template_outputs) - self.store() - if action == self.UPDATE: stack_status = self.UPDATE_COMPLETE reason = 'Stack successfully updated' @@ -469,6 +462,16 @@ class Stack(object): self.state_set(stack_status, reason) + # flip the template & parameters to the newstack values + # Note we do this on success and failure, so the current + # stack resources are stored, even if one is in a failed + # state (otherwise we won't remove them on delete) + self.t = newstack.t + self.parameters = newstack.parameters + template_outputs = self.t[template.OUTPUTS] + self.outputs = self.resolve_static_data(template_outputs) + self.store() + def delete(self, action=DELETE): ''' Delete all of the resources, and then the stack itself. diff --git a/heat/tests/test_parser.py b/heat/tests/test_parser.py index 7b0186bf..1f2b9091 100644 --- a/heat/tests/test_parser.py +++ b/heat/tests/test_parser.py @@ -674,6 +674,37 @@ class StackTest(unittest.TestCase): self.assertEqual(self.stack.state, parser.Stack.UPDATE_FAILED) self.m.VerifyAll() + @stack_delete_after + def test_update_add_failed_create(self): + tmpl = {'Resources': {'AResource': {'Type': 'GenericResourceType'}}} + + self.stack = parser.Stack(self.ctx, 'update_test_stack', + template.Template(tmpl)) + self.stack.store() + self.stack.create() + self.assertEqual(self.stack.state, parser.Stack.CREATE_COMPLETE) + + tmpl2 = {'Resources': { + 'AResource': {'Type': 'GenericResourceType'}, + 'BResource': {'Type': 'GenericResourceType'}}} + updated_stack = parser.Stack(self.ctx, 'updated_stack', + template.Template(tmpl2)) + + # patch in a dummy handle_create making BResource fail creating + self.m.StubOutWithMock(generic_rsrc.GenericResource, 'handle_create') + generic_rsrc.GenericResource.handle_create().AndRaise(Exception) + self.m.ReplayAll() + + self.stack.update(updated_stack) + self.assertEqual(self.stack.state, parser.Stack.UPDATE_FAILED) + self.assertTrue('BResource' in self.stack) + + # Reload the stack from the DB and prove that it contains the failed + # resource (to ensure it will be deleted on stack delete) + re_stack = parser.Stack.load(self.ctx, stack_id=self.stack.id) + self.assertTrue('BResource' in re_stack) + self.m.VerifyAll() + @stack_delete_after def test_update_rollback(self): # patch in a dummy property schema for GenericResource -- 2.45.2