From eb793b613cc5da05a9ca777c55ba3d47143dd58e Mon Sep 17 00:00:00 2001 From: Jason Dunsmore Date: Tue, 14 Mar 2017 16:07:57 -0500 Subject: [PATCH] Only recreate CHECK FAILED resources in ResourceGroup Previously, ResourceGroup._needs_update() would raise UpdateReplace when the ResourceGroup was in CHECK_FAILED status, resulting in delete/replacement of every resource in the group. Now, it will return True so that only resources in the CHECK_FAILED status. Change-Id: I800c4f58feec7c1aaa4897c2ba056e5a74200e5d Closes-Bug: #1671592 (cherry picked from commit 362069cd1f8541a4a5511d92e5dbbe38bb78701f) --- heat/engine/resources/stack_resource.py | 20 +++++++++---- heat/tests/test_stack_resource.py | 40 +++++++++++++++++++++---- 2 files changed, 49 insertions(+), 11 deletions(-) diff --git a/heat/engine/resources/stack_resource.py b/heat/engine/resources/stack_resource.py index 9b27f1fe1..6d9f1aa60 100644 --- a/heat/engine/resources/stack_resource.py +++ b/heat/engine/resources/stack_resource.py @@ -94,11 +94,6 @@ class StackResource(resource.Resource): def _needs_update(self, after, before, after_props, before_props, prev_resource, check_init_complete=True): - # If stack resource is in CHECK_FAILED state, raise UpdateReplace - # to replace the failed stack. - if self.state == (self.CHECK, self.FAILED): - raise resource.UpdateReplace(self) - # If the nested stack has not been created, use the default # implementation to determine if we need to replace the resource. Note # that we do *not* return the result. @@ -107,6 +102,21 @@ class StackResource(resource.Resource): after_props, before_props, prev_resource, check_init_complete) + else: + if self.state == (self.CHECK, self.FAILED): + nested_stack = self.rpc_client().show_stack( + self.context, self.nested_identifier())[0] + nested_stack_state = (nested_stack[rpc_api.STACK_ACTION], + nested_stack[rpc_api.STACK_STATUS]) + if nested_stack_state == (self.stack.CHECK, self.stack.FAILED): + # The stack-check action marked the stack resource + # CHECK_FAILED, so return True to allow the individual + # CHECK_FAILED resources decide if they need updating. + return True + # The mark-unhealthy action marked the stack resource + # CHECK_FAILED, so raise UpdateReplace to replace the + # entire failed stack. + raise resource.UpdateReplace(self) # Always issue an update to the nested stack and let the individual # resources in it decide if they need updating. diff --git a/heat/tests/test_stack_resource.py b/heat/tests/test_stack_resource.py index cb194720a..8bcc2772b 100644 --- a/heat/tests/test_stack_resource.py +++ b/heat/tests/test_stack_resource.py @@ -523,6 +523,12 @@ class StackResourceTest(StackResourceBaseTest): need update. """ self.parent_resource.action = self.parent_resource.CREATE + + self.parent_resource._rpc_client = mock.MagicMock() + self.parent_resource._rpc_client.show_stack.return_value = [ + {'stack_action': self.parent_resource.CREATE, + 'stack_status': self.parent_resource.COMPLETE}] + need_update = self.parent_resource._needs_update( self.parent_resource.t, self.parent_resource.t, @@ -566,12 +572,8 @@ class StackResourceTest(StackResourceBaseTest): self.parent_resource.properties, self.parent_resource) - def test_need_update_in_check_failed_state_for_nested_resource(self): - """Test the resource should need replacement. - - The resource in check_failed state should need update with - UpdateReplace. - """ + def test_need_update_in_check_failed_state_after_stack_check(self): + self.parent_resource.resource_id = 'fake_id' self.parent_resource.state_set(self.parent_resource.CHECK, self.parent_resource.FAILED) self.nested = mock.MagicMock() @@ -579,6 +581,32 @@ class StackResourceTest(StackResourceBaseTest): self.parent_resource.nested = mock.MagicMock(return_value=self.nested) self.parent_resource._nested = self.nested + self.parent_resource._rpc_client = mock.MagicMock() + self.parent_resource._rpc_client.show_stack.return_value = [ + {'stack_action': self.parent_resource.CHECK, + 'stack_status': self.parent_resource.FAILED}] + + self.assertTrue( + self.parent_resource._needs_update(self.parent_resource.t, + self.parent_resource.t, + self.parent_resource.properties, + self.parent_resource.properties, + self.parent_resource)) + + def test_need_update_check_failed_state_after_mark_unhealthy(self): + self.parent_resource.resource_id = 'fake_id' + self.parent_resource.state_set(self.parent_resource.CHECK, + self.parent_resource.FAILED) + self.nested = mock.MagicMock() + self.nested.name = 'nested-stack' + self.parent_resource.nested = mock.MagicMock(return_value=self.nested) + self.parent_resource._nested = self.nested + + self.parent_resource._rpc_client = mock.MagicMock() + self.parent_resource._rpc_client.show_stack.return_value = [ + {'stack_action': self.parent_resource.CREATE, + 'stack_status': self.parent_resource.COMPLETE}] + self.assertRaises(resource.UpdateReplace, self.parent_resource._needs_update, self.parent_resource.t,