Avoid error on double-delete of nested stack

When deleting a stack that has a nested stack previously deleted, the
process fails because the nested stack cannot be retrieved from the
database.
To solve the bug, the nested stack is now retrieved from the database
ignoring the deleted stacks before the deletion process begins. Then
a NotFound exception is raised, and then when it's catched the nested
stack will be ignored.

Change-Id: Ib863527dbfb0200b833a4e90b5a4fd50f469c1ce
Closes-Bug: #1261436
This commit is contained in:
Pablo Andres Fuente 2013-12-17 17:01:04 -03:00
parent e3688f5f1a
commit 7233ba1b14
3 changed files with 56 additions and 3 deletions

View File

@ -65,7 +65,8 @@ class StackResource(resource.Resource):
if self._nested is None and self.resource_id is not None:
self._nested = parser.Stack.load(self.context,
self.resource_id,
parent_resource=self)
parent_resource=self,
show_deleted=False)
if self._nested is None:
raise exception.NotFound(_('Nested stack not found in DB'))

View File

@ -480,6 +480,41 @@ Resources:
self.assertIn('Recursion depth exceeds', stack.status_reason)
self.m.VerifyAll()
def test_nested_stack_delete(self):
urlfetch.get('https://server.test/the.template').MultipleTimes().\
AndReturn(self.nested_template)
self.m.ReplayAll()
stack = self.create_stack(self.test_template)
rsrc = stack['the_nested']
scheduler.TaskRunner(rsrc.delete)()
self.assertEqual((stack.DELETE, stack.COMPLETE), rsrc.state)
nested_stack = parser.Stack.load(utils.dummy_context(
'test_username', 'aaaa', 'password'), rsrc.resource_id)
self.assertEqual((stack.DELETE, stack.COMPLETE), nested_stack.state)
self.m.VerifyAll()
def test_nested_stack_delete_then_delete_parent_stack(self):
urlfetch.get('https://server.test/the.template').MultipleTimes().\
AndReturn(self.nested_template)
self.m.ReplayAll()
stack = self.create_stack(self.test_template)
rsrc = stack['the_nested']
nested_stack = parser.Stack.load(utils.dummy_context(
'test_username', 'aaaa', 'password'), rsrc.resource_id)
nested_stack.delete()
stack = parser.Stack.load(utils.dummy_context(
'test_username', 'aaaa', 'password'), stack.id)
stack.delete()
self.assertEqual((stack.DELETE, stack.COMPLETE), stack.state)
self.m.VerifyAll()
class ResDataResource(generic_rsrc.GenericResource):
def handle_create(self):

View File

@ -241,7 +241,8 @@ class StackResourceTest(HeatTestCase):
self.m.StubOutWithMock(parser.Stack, 'load')
parser.Stack.load(self.parent_resource.context,
self.parent_resource.resource_id,
parent_resource=self.parent_resource).AndReturn('s')
parent_resource=self.parent_resource,
show_deleted=False).AndReturn('s')
self.m.ReplayAll()
self.parent_resource.nested()
@ -257,7 +258,8 @@ class StackResourceTest(HeatTestCase):
self.m.StubOutWithMock(parser.Stack, 'load')
parser.Stack.load(self.parent_resource.context,
self.parent_resource.resource_id,
parent_resource=self.parent_resource)
parent_resource=self.parent_resource,
show_deleted=False)
self.m.ReplayAll()
self.assertRaises(exception.NotFound, self.parent_resource.nested)
@ -273,6 +275,21 @@ class StackResourceTest(HeatTestCase):
self.parent_resource.delete_nested()
self.m.VerifyAll()
def test_delete_nested_not_found_nested_stack(self):
self.parent_resource.create_with_template(self.templ,
{"KeyName": "key"})
self.stack = self.parent_resource.nested()
self.parent_resource._nested = None
self.m.StubOutWithMock(parser.Stack, 'load')
parser.Stack.load(self.parent_resource.context,
self.parent_resource.resource_id,
parent_resource=self.parent_resource,
show_deleted=False).AndRaise(exception.NotFound)
self.m.ReplayAll()
self.assertIsNone(self.parent_resource.delete_nested())
def test_get_output_ok(self):
nested = self.m.CreateMockAnything()
self.m.StubOutWithMock(stack_resource.StackResource, 'nested')