Use handle_update_cancel() to cancel nested stack updates

The code designed to deal with the case where an in-progress StackResource
update is cancelled by stopping the update of the nested stack has never
worked because it was erroneously trying to catch StopIteration exceptions
instead of the actual exception raised when a generator is closed,
GeneratorExit.

This change moves the handling code to the new handle_update_cancel()
mechanism instead. Using this mechanism also means that StackResources will
be cancelled immediately, rather than waiting for up to error_wait_time
before cancelling the nested stack update. The previous patch adds a grace
period for IN_PROGRESS resources within the nested stack to complete, so we
want to notify it straight away to begin that period (and to not start any
new work).

Since the grace period on the child stack means that the child is likely to
still be IN_PROGRESS at the time we want to roll back, handle rollback by
sending an update-cancel with rollback message to the child. Only if the
child is no longer IN_PROGRESS will we start an update with the previous
template. If it is still IN_PROGRESS then just wait for it to reach
ROLLBACK_COMPLETE.

Change-Id: Ide210f695446fe8de2057b3b74c276165a6f9f3f
Closes-Bug: #1591337
Closes-Bug: #1446252
This commit is contained in:
Zane Bitter 2016-07-22 11:14:54 -04:00
parent e7a9e03132
commit 97a3670462
1 changed files with 44 additions and 14 deletions

View File

@ -22,6 +22,7 @@ import six
from heat.common import exception
from heat.common.i18n import _
from heat.common.i18n import _LI
from heat.common.i18n import _LW
from heat.common import identifier
from heat.common import template_format
@ -110,19 +111,6 @@ class StackResource(resource.Resource):
return True
@scheduler.wrappertask
def update(self, after, before=None, prev_resource=None):
try:
yield super(StackResource, self).update(after, before,
prev_resource)
except StopIteration:
with excutils.save_and_reraise_exception():
stack_identity = self.nested_identifier()
self.rpc_client().stack_cancel_update(
self.context,
dict(stack_identity),
cancel_with_rollback=False)
def nested_identifier(self):
if self.resource_id is None:
return None
@ -423,12 +411,41 @@ class StackResource(resource.Resource):
def check_adopt_complete(self, cookie=None):
return self._check_status_complete(self.ADOPT)
def _try_rollback(self):
stack_identity = self.nested_identifier()
if stack_identity is None:
return False
self.rpc_client().stack_cancel_update(
self.context,
dict(stack_identity),
cancel_with_rollback=True)
try:
data = stack_object.Stack.get_status(self.context,
self.resource_id)
except exception.NotFound:
return False
action, status, status_reason, updated_time = data
# If nested stack is still in progress, it should eventually roll
# itself back due to stack_cancel_update(), so we just need to wait
# for that to complete
return status == self.stack.IN_PROGRESS
def update_with_template(self, child_template, user_params=None,
timeout_mins=None):
"""Update the nested stack with the new template."""
if self.id is None:
self._store()
if self.stack.action == self.stack.ROLLBACK:
if self._try_rollback():
LOG.info(_LI('Triggered nested stack %s rollback'),
self.physical_resource_name())
return {'target_action': self.stack.ROLLBACK}
nested_stack = self.nested()
if nested_stack is None:
# if the create failed for some reason and the nested
@ -469,9 +486,22 @@ class StackResource(resource.Resource):
return cookie
def check_update_complete(self, cookie=None):
return self._check_status_complete(self.UPDATE,
if cookie is not None and 'target_action' in cookie:
target_action = cookie['target_action']
cookie = None
else:
target_action = self.stack.UPDATE
return self._check_status_complete(target_action,
cookie=cookie)
def handle_update_cancel(self, cookie):
stack_identity = self.nested_identifier()
if stack_identity is not None:
self.rpc_client().stack_cancel_update(
self.context,
dict(stack_identity),
cancel_with_rollback=False)
def delete_nested(self):
"""Delete the nested stack."""
stack = self.nested()