Don't delete stack domain project on stack-abandon

Currently we delete the stack domain project on abandon, which will
break any resources being abandoned that depend on users created in
the stack user domain.  So add a flag to skip deletion on abandon
and some tests to prove it works.

Change-Id: I883831a33d5bd326523836247683c161f797ee2a
Closes-Bug: #1300734
This commit is contained in:
Steven Hardy 2014-09-08 18:58:47 +01:00
parent 0c2ee06320
commit 6e95e28c4d
3 changed files with 59 additions and 5 deletions

View File

@ -814,7 +814,8 @@ class EngineService(service.Service):
stack_info = stack.prepare_abandon()
self.thread_group_mgr.start_with_acquired_lock(stack,
lock,
stack.delete)
stack.delete,
abandon=True)
return stack_info
def list_resource_types(self, cnxt, support_status=None):

View File

@ -770,13 +770,17 @@ class Stack(collections.Mapping):
notification.send(self)
def delete(self, action=DELETE, backup=False):
def delete(self, action=DELETE, backup=False, abandon=False):
'''
Delete all of the resources, and then the stack itself.
The action parameter is used to differentiate between a user
initiated delete and an automatic stack rollback after a failed
create, which amount to the same thing, but the states are recorded
differently.
Note abandon is a delete where all resources have been set to a
RETAIN deletion policy, but we also don't want to delete anything
required for those resources, e.g the stack_user_project.
'''
if action not in (self.DELETE, self.ROLLBACK):
LOG.error(_("Unexpected action %s passed to delete!") % action)
@ -784,8 +788,6 @@ class Stack(collections.Mapping):
"Invalid action %s" % action)
return
# Note abandon is a delete with
# stack.set_deletion_policy(resource.RETAIN)
stack_status = self.COMPLETE
reason = 'Stack %s completed successfully' % action
self.state_set(action, self.IN_PROGRESS, 'Stack %s started' %
@ -897,7 +899,7 @@ class Stack(collections.Mapping):
"%s ") % self.id)
# If the stack has a domain project, delete it
if self.stack_user_project_id:
if self.stack_user_project_id and not abandon:
try:
keystone = self.clients.client('keystone')
keystone.delete_stack_domain_project(

View File

@ -1522,6 +1522,57 @@ class StackTest(HeatTestCase):
self.stack.state)
self.assertIn('Error deleting trust', self.stack.status_reason)
def test_delete_deletes_project(self):
fkc = FakeKeystoneClient()
fkc.delete_stack_domain_project = mock.Mock()
self.m.StubOutWithMock(keystone.KeystoneClientPlugin, '_create')
keystone.KeystoneClientPlugin._create().AndReturn(fkc)
self.m.ReplayAll()
self.stack = parser.Stack(
self.ctx, 'delete_trust', self.tmpl)
stack_id = self.stack.store()
self.stack.set_stack_user_project_id(project_id='aproject456')
db_s = db_api.stack_get(self.ctx, stack_id)
self.assertIsNotNone(db_s)
self.stack.delete()
db_s = db_api.stack_get(self.ctx, stack_id)
self.assertIsNone(db_s)
self.assertEqual((parser.Stack.DELETE, parser.Stack.COMPLETE),
self.stack.state)
fkc.delete_stack_domain_project.assert_called_once_with(
project_id='aproject456')
def test_abandon_nodelete_project(self):
fkc = FakeKeystoneClient()
fkc.delete_stack_domain_project = mock.Mock()
self.m.StubOutWithMock(keystone.KeystoneClientPlugin, '_create')
keystone.KeystoneClientPlugin._create().AndReturn(fkc)
self.m.ReplayAll()
self.stack = parser.Stack(
self.ctx, 'delete_trust', self.tmpl)
stack_id = self.stack.store()
self.stack.set_stack_user_project_id(project_id='aproject456')
db_s = db_api.stack_get(self.ctx, stack_id)
self.assertIsNotNone(db_s)
self.stack.delete(abandon=True)
db_s = db_api.stack_get(self.ctx, stack_id)
self.assertIsNone(db_s)
self.assertEqual((parser.Stack.DELETE, parser.Stack.COMPLETE),
self.stack.state)
self.assertFalse(fkc.delete_stack_domain_project.called)
def test_suspend_resume(self):
self.m.ReplayAll()
tmpl = {'HeatTemplateFormatVersion': '2012-12-12',