From ed6715178c6dd30c3af17c66cb0f590964fb4600 Mon Sep 17 00:00:00 2001 From: Alex Harvey Date: Mon, 26 Aug 2024 00:18:21 +1000 Subject: [PATCH] [Resolve #1493] Complete DisableRollback implementation (#1494) * [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. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- sceptre/cli/update.py | 1 + sceptre/plan/actions.py | 6 ++++++ tests/test_actions.py | 38 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 45 insertions(+) diff --git a/sceptre/cli/update.py b/sceptre/cli/update.py index f685ecc68..4ea716780 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 8cc67ef5f..d8022b2ba 100644 --- a/sceptre/plan/actions.py +++ b/sceptre/plan/actions.py @@ -139,6 +139,12 @@ 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 113ccf6c8..77933e298 100644 --- a/tests/test_actions.py +++ b/tests/test_actions.py @@ -275,6 +275,44 @@ 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)