From 6928501973dff823540a3cfe8b4b0880722e8e9c Mon Sep 17 00:00:00 2001 From: Jesse Szwedko Date: Mon, 11 Apr 2016 21:52:03 +0000 Subject: [PATCH] Throw exception if an update is attempted on a stack in ROLLBACK_COMPLETE If a stack has a status of ROLLBACK_COMPLETE and you attempt to update it, the AWS API throws a validation error. This updates moto to have the same behvaior. We also uncommented a test which tests updating a stack which passed without any additional modification -- it is unclear why this test was commented. Signed-off-by: Jack Lund --- moto/cloudformation/exceptions.py | 7 +++++-- moto/cloudformation/responses.py | 4 ++++ .../test_cloudformation_stack_crud.py | 19 +++++++++++++++++++ 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/moto/cloudformation/exceptions.py b/moto/cloudformation/exceptions.py index 8e34b84a8..de672d8b8 100644 --- a/moto/cloudformation/exceptions.py +++ b/moto/cloudformation/exceptions.py @@ -9,12 +9,15 @@ class UnformattedGetAttTemplateException(Exception): class ValidationError(BadRequest): - def __init__(self, name_or_id): + def __init__(self, name_or_id, message=None): + if message is None: + messgae="Stack:{0} does not exist".format(name_or_id), + template = Template(ERROR_RESPONSE) super(ValidationError, self).__init__() self.description = template.render( code="ValidationError", - message="Stack:{0} does not exist".format(name_or_id), + message=message, ) diff --git a/moto/cloudformation/responses.py b/moto/cloudformation/responses.py index 711d6f82e..e407be9d2 100644 --- a/moto/cloudformation/responses.py +++ b/moto/cloudformation/responses.py @@ -129,6 +129,10 @@ class CloudFormationResponse(BaseResponse): else: stack_body = self._get_param('TemplateBody') + stack = self.cloudformation_backend.get_stack(stack_name) + if stack.status == 'ROLLBACK_COMPLETE': + raise ValidationError(stack.stack_id, message="Stack:{0} is in ROLLBACK_COMPLETE state and can not be updated.".format(stack.stack_id)) + stack = self.cloudformation_backend.update_stack( name=stack_name, template=stack_body, diff --git a/tests/test_cloudformation/test_cloudformation_stack_crud.py b/tests/test_cloudformation/test_cloudformation_stack_crud.py index 26084c7b4..e516d92dc 100644 --- a/tests/test_cloudformation/test_cloudformation_stack_crud.py +++ b/tests/test_cloudformation/test_cloudformation_stack_crud.py @@ -13,6 +13,7 @@ import tests.backport_assert_raises # noqa from nose.tools import assert_raises from moto import mock_cloudformation, mock_s3 +from moto.cloudformation import cloudformation_backends dummy_template = { "AWSTemplateFormatVersion": "2010-09-09", @@ -286,6 +287,7 @@ def test_update_stack(): } }) + @mock_cloudformation def test_update_stack(): conn = boto.connect_cloudformation() @@ -307,3 +309,20 @@ def test_update_stack(): } } }) + + +@mock_cloudformation +def test_update_stack_when_rolled_back(): + conn = boto.connect_cloudformation() + stack_id = conn.create_stack("test_stack", template_body=dummy_template_json) + + cloudformation_backends[conn.region.name].stacks[stack_id].status = 'ROLLBACK_COMPLETE' + + with assert_raises(BotoServerError) as err: + conn.update_stack("test_stack", dummy_template_json) + + ex = err.exception + ex.body.should.match(r'is in ROLLBACK_COMPLETE state and can not be updated') + ex.error_code.should.equal('ValidationError') + ex.reason.should.equal('Bad Request') + ex.status.should.equal(400)