Split abandon into pre-abandon(export) and abandon

Splits the existing abandon action into two actions as
below:
1. export: User is recommended to run export successfully
   before doing the abandon action. It returns the stack
   data, which can be used for adopting stack.
2. abandon: Abandons the given stack

Partial-Bug: #1353670

Co-Authored-By: Jason Dunsmore <jasondunsmore@gmail.com>
Change-Id: I65264d91b1378df9cb3e492bc6ccaa778940dd6b
This commit is contained in:
Kanagaraj Manickam 2015-05-11 17:34:43 +05:30 committed by Jason Dunsmore
parent 17851c88c9
commit bbb770841a
3 changed files with 43 additions and 10 deletions

View File

@ -1139,26 +1139,42 @@ class EngineService(service.Service):
return None
@context.request_context
def abandon_stack(self, cnxt, stack_identity):
def export_stack(self, cnxt, stack_identity):
"""Exports the stack data json.
Intended to be used to safely retrieve the stack data before
performing the abandon action.
:param cnxt: RPC context.
:param stack_identity: Name of the stack you want to export.
"""
return self.abandon_stack(cnxt, stack_identity, abandon=False)
@context.request_context
def abandon_stack(self, cnxt, stack_identity, abandon=True):
"""Abandon a given stack.
:param cnxt: RPC context.
:param stack_identity: Name of the stack you want to abandon.
:param abandon: Delete Heat stack but not physical resources.
"""
if not cfg.CONF.enable_stack_abandon:
raise exception.NotSupported(feature='Stack Abandon')
st = self._get_stack(cnxt, stack_identity)
LOG.info(_LI('abandoning stack %s'), st.name)
stack = parser.Stack.load(cnxt, stack=st)
lock = stack_lock.StackLock(cnxt, stack.id, self.engine_id)
with lock.thread_lock():
# Get stack details before deleting it.
stack_info = stack.prepare_abandon()
self.thread_group_mgr.start_with_acquired_lock(stack,
lock,
stack.delete,
abandon=True)
if abandon:
LOG.info(_LI('abandoning stack %s'), st.name)
self.thread_group_mgr.start_with_acquired_lock(stack,
lock,
stack.delete,
abandon=True)
else:
LOG.info(_LI('exporting stack %s'), st.name)
return stack_info
def list_resource_types(self,

View File

@ -815,8 +815,8 @@ class StackServiceTest(common.HeatTestCase):
not_tags=None,
not_tags_any=None)
@tools.stack_context('service_abandon_stack')
def test_abandon_stack(self):
@tools.stack_context('service_export_stack')
def test_export_stack(self):
cfg.CONF.set_override('enable_stack_abandon', True)
self.m.StubOutWithMock(parser.Stack, 'load')
parser.Stack.load(self.ctx,
@ -832,11 +832,11 @@ class StackServiceTest(common.HeatTestCase):
'type': u'AWS::EC2::Instance'}}
self.stack.tags = ['tag1', 'tag2']
self.m.ReplayAll()
ret = self.eng.abandon_stack(self.ctx, self.stack.identifier())
ret = self.eng.export_stack(self.ctx, self.stack.identifier())
self.assertEqual(11, len(ret))
self.assertEqual('CREATE', ret['action'])
self.assertEqual('COMPLETE', ret['status'])
self.assertEqual('service_abandon_stack', ret['name'])
self.assertEqual('service_export_stack', ret['name'])
self.assertEqual({}, ret['files'])
self.assertIn('id', ret)
self.assertEqual(expected_res, ret['resources'])
@ -848,6 +848,20 @@ class StackServiceTest(common.HeatTestCase):
self.assertEqual(['tag1', 'tag2'], ret['tags'])
self.m.VerifyAll()
@tools.stack_context('service_abandon_stack')
def test_abandon_stack(self):
cfg.CONF.set_override('enable_stack_abandon', True)
self.m.StubOutWithMock(parser.Stack, 'load')
parser.Stack.load(self.ctx,
stack=mox.IgnoreArg()).AndReturn(self.stack)
self.m.ReplayAll()
self.eng.abandon_stack(self.ctx, self.stack.identifier())
ex = self.assertRaises(dispatcher.ExpectedException,
self.eng.show_stack,
self.ctx, self.stack.identifier())
self.assertEqual(exception.EntityNotFound, ex.exc_info[0])
self.m.VerifyAll()
def test_stack_describe_nonexistent(self):
non_exist_identifier = identifier.HeatIdentifier(
self.ctx.tenant_id, 'wibble',

View File

@ -616,6 +616,9 @@ Outputs:
info['template'])
self.assertEqual(self._yaml_to_json(self.nested_templ),
info['resources']['the_nested']['template'])
# TODO(james combs): Implement separate test cases for export
# once export REST API is available. Also test reverse order
# of invocation: export -> abandon AND abandon -> export
def test_adopt(self):
data = {