From e1f161a19a9eef3edb05612aad905e2e7ae7c674 Mon Sep 17 00:00:00 2001 From: Crag Wolfe Date: Wed, 21 Sep 2016 19:06:36 -0400 Subject: [PATCH] Do not attempt deletion of a DELETE_COMPLETE stack in service api A stack may be in transient state where it is DELETE_COMPLETE, but has has not actually been soft-deleted yet. For the purposes of delete_stack in service.py, consider a DELETE_COMPLETE stack as equivalent to a soft-deleted one (it soon will be), thereby avoiding a race where we would have attempted to update the stack, running into a foreign-key constraint issue for a non-existing user_cred. Change-Id: Iec021e6a0df262d447fdf9ee1789603c7a1c55f8 Closes-Bug: #1626173 Closes-Bug: #1626107 --- heat/engine/service.py | 4 ++++ heat/tests/test_engine_service.py | 11 +++++++++++ 2 files changed, 15 insertions(+) diff --git a/heat/engine/service.py b/heat/engine/service.py index 8bee77aeca..bd3c807c6f 100644 --- a/heat/engine/service.py +++ b/heat/engine/service.py @@ -1358,6 +1358,10 @@ class EngineService(service.Service): """ st = self._get_stack(cnxt, stack_identity) + if (st.status == parser.Stack.COMPLETE and + st.action == parser.Stack.DELETE): + raise exception.EntityNotFound(entity='Stack', name=st.name) + LOG.info(_LI('Deleting stack %s'), st.name) stack = parser.Stack.load(cnxt, stack=st) self.resource_enforcer.enforce_stack(stack) diff --git a/heat/tests/test_engine_service.py b/heat/tests/test_engine_service.py index 118683baec..e58d03dad5 100644 --- a/heat/tests/test_engine_service.py +++ b/heat/tests/test_engine_service.py @@ -975,6 +975,17 @@ class StackServiceTest(common.HeatTestCase): outputs = self.eng.list_outputs(self.ctx, mock.ANY) self.assertEqual([], outputs) + def test_stack_delete_complete_is_not_found(self): + mock_get_stack = self.patchobject(self.eng, '_get_stack') + mock_get_stack.return_value = mock.MagicMock() + mock_get_stack.return_value.status = parser.Stack.COMPLETE + mock_get_stack.return_value.action = parser.Stack.DELETE + ex = self.assertRaises(dispatcher.ExpectedException, + self.eng.delete_stack, + 'irrelevant', + 'irrelevant') + self.assertEqual(exception.EntityNotFound, ex.exc_info[0]) + def test_get_environment(self): # Setup t = template_format.parse(tools.wp_template)