From 6ab4551fc78975dd4c94482cd3626f9c9c7a65de Mon Sep 17 00:00:00 2001 From: Alex Harvey Date: Sat, 3 Aug 2024 17:20:23 +1000 Subject: [PATCH] [Resolve #1493] Complete DisableRollback implementation This adds missing code to complete the implementation of the DisableRollback feature. The implementation in 99c839a2b is clear that it was intended that this feature would cover both cases of creating and updating stacks, and this code and unit test was missed. --- sceptre/cli/update.py | 1 + sceptre/plan/actions.py | 4 ++++ tests/test_actions.py | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 41 insertions(+) diff --git a/sceptre/cli/update.py b/sceptre/cli/update.py index 35d4239ae..150242299 100644 --- a/sceptre/cli/update.py +++ b/sceptre/cli/update.py @@ -41,6 +41,7 @@ def update_command( :type verbose: bool :param yes: A flag to answer 'yes' to all CLI questions. :type yes: bool + :param disable_rollback: A flag to disable cloudformation rollback. """ context = SceptreContext( diff --git a/sceptre/plan/actions.py b/sceptre/plan/actions.py index 7b7fd5718..b44af91af 100644 --- a/sceptre/plan/actions.py +++ b/sceptre/plan/actions.py @@ -139,6 +139,10 @@ def update(self): {"Key": str(k), "Value": str(v)} for k, v in self.stack.tags.items() ], } + + if self.stack.disable_rollback: + update_stack_kwargs.update({"DisableRollback": self.stack.disable_rollback}) + update_stack_kwargs.update(self.stack.template.get_boto_call_parameter()) update_stack_kwargs.update(self._get_role_arn()) response = self.connection_manager.call( diff --git a/tests/test_actions.py b/tests/test_actions.py index 9e66b7601..b8688d09c 100644 --- a/tests/test_actions.py +++ b/tests/test_actions.py @@ -275,6 +275,42 @@ def test_update_sends_correct_request(self, mock_wait_for_completion): sentinel.stack_timeout, boto_response=ANY ) + @patch("sceptre.plan.actions.StackActions._wait_for_completion") + @patch("sceptre.plan.actions.StackActions._get_stack_timeout") + def test_update_disable_rollback_overrides_on_failure( + self, mock_get_stack_timeout, mock_wait_for_completion + ): + self.actions.stack._template = Mock(spec=Template) + self.actions.stack._template.get_boto_call_parameter.return_value = { + "Template": sentinel.template + } + + self.actions.stack.on_failure = "ROLLBACK" + self.actions.stack.disable_rollback = True + + mock_get_stack_timeout.return_value = {"TimeoutInMinutes": sentinel.timeout} + + self.actions.update() + self.actions.connection_manager.call.assert_called_with( + service="cloudformation", + command="update_stack", + kwargs={ + "StackName": sentinel.external_name, + "Template": sentinel.template, + "Parameters": [{"ParameterKey": "key1", "ParameterValue": "val1"}], + "Capabilities": [ + "CAPABILITY_IAM", + "CAPABILITY_NAMED_IAM", + "CAPABILITY_AUTO_EXPAND", + ], + "RoleARN": sentinel.cloudformation_service_role, + "NotificationARNs": [sentinel.notification], + "Tags": [{"Key": "tag1", "Value": "val1"}], + "DisableRollback": True, + }, + ) + mock_wait_for_completion.assert_called_once_with(sentinel.stack_timeout, boto_response=ANY) + @patch("sceptre.plan.actions.StackActions._wait_for_completion") def test_update_cancels_after_timeout(self, mock_wait_for_completion): self.actions.stack._template = Mock(spec=Template)