diff --git a/moto/cloudformation/models.py b/moto/cloudformation/models.py index d3fb2870d..d3f0976a1 100644 --- a/moto/cloudformation/models.py +++ b/moto/cloudformation/models.py @@ -707,6 +707,7 @@ class CloudFormationBackend(BaseBackend): for stack in self.stacks.values(): if stack.name == name_or_stack_id: return stack + raise ValidationError(name_or_stack_id) def update_stack(self, name, template, role_arn=None, parameters=None, tags=None): stack = self.get_stack(name) @@ -715,8 +716,6 @@ class CloudFormationBackend(BaseBackend): def list_stack_resources(self, stack_name_or_id): stack = self.get_stack(stack_name_or_id) - if stack is None: - return None return stack.stack_resources def delete_stack(self, name_or_stack_id): diff --git a/moto/cloudformation/responses.py b/moto/cloudformation/responses.py index 4c8ad36d0..6d2af655d 100644 --- a/moto/cloudformation/responses.py +++ b/moto/cloudformation/responses.py @@ -232,8 +232,6 @@ class CloudFormationResponse(BaseResponse): if stack_resource.logical_resource_id == logical_resource_id: resource = stack_resource break - else: - raise ValidationError(logical_resource_id) template = self.response_template(DESCRIBE_STACK_RESOURCE_RESPONSE_TEMPLATE) return template.render(stack=stack, resource=resource) @@ -267,9 +265,6 @@ class CloudFormationResponse(BaseResponse): stack_name_or_id = self._get_param("StackName") resources = self.cloudformation_backend.list_stack_resources(stack_name_or_id) - if resources is None: - raise ValidationError(stack_name_or_id) - template = self.response_template(LIST_STACKS_RESOURCES_RESPONSE) return template.render(resources=resources) diff --git a/tests/test_cloudformation/test_cloudformation_stack_crud.py b/tests/test_cloudformation/test_cloudformation_stack_crud.py index 40004f805..1725be0f6 100644 --- a/tests/test_cloudformation/test_cloudformation_stack_crud.py +++ b/tests/test_cloudformation/test_cloudformation_stack_crud.py @@ -570,6 +570,15 @@ def test_describe_stack_events_shows_create_update_and_delete(): list(stack_events_to_look_for).should.be.empty + with pytest.raises(BotoServerError) as exp: + conn.describe_stack_events("non_existing_stack") + err = exp.value + err.message.should.equal("Stack with id non_existing_stack does not exist") + err.body.should.match(r"Stack with id non_existing_stack does not exist") + err.error_code.should.equal("ValidationError") + err.reason.should.equal("Bad Request") + err.status.should.equal(400) + @mock_cloudformation_deprecated def test_create_stack_lambda_and_dynamodb(): diff --git a/tests/test_cloudformation/test_cloudformation_stack_crud_boto3.py b/tests/test_cloudformation/test_cloudformation_stack_crud_boto3.py index 3eb2c7291..0fa45e1b8 100644 --- a/tests/test_cloudformation/test_cloudformation_stack_crud_boto3.py +++ b/tests/test_cloudformation/test_cloudformation_stack_crud_boto3.py @@ -255,6 +255,7 @@ def test_boto3_filter_stacks(): conn.create_stack(StackName="test_stack", TemplateBody=dummy_template_json) conn.create_stack(StackName="test_stack2", TemplateBody=dummy_template_json) conn.update_stack(StackName="test_stack", TemplateBody=dummy_template_json2) + stacks = conn.list_stacks(StackStatusFilter=["CREATE_COMPLETE"]) stacks.get("StackSummaries").should.have.length_of(1) stacks = conn.list_stacks(StackStatusFilter=["UPDATE_COMPLETE"]) @@ -318,6 +319,18 @@ def test_boto3_describe_stack_set_operation(): response["StackSetOperation"]["Status"].should.equal("STOPPED") response["StackSetOperation"]["Action"].should.equal("CREATE") + with pytest.raises(ClientError) as exp: + cf_conn.describe_stack_set_operation( + StackSetName="test_stack_set", OperationId="non_existing_operation" + ) + exp_err = exp.value.response.get("Error") + exp_metadata = exp.value.response.get("ResponseMetadata") + + exp_err.get("Code").should.match(r"ValidationError") + exp_err.get("Message").should.match( + r"Stack with id non_existing_operation does not exist" + ) + exp_metadata.get("HTTPStatusCode").should.equal(400) @mock_cloudformation @@ -1136,6 +1149,16 @@ def test_delete_change_set(): cf_conn.delete_change_set(ChangeSetName="NewChangeSet", StackName="NewStack") cf_conn.list_change_sets(StackName="NewStack")["Summaries"].should.have.length_of(0) + # Testing deletion by arn + result = cf_conn.create_change_set( + StackName="NewStack", + TemplateBody=dummy_template_json, + ChangeSetName="NewChangeSet1", + ChangeSetType="CREATE", + ) + cf_conn.delete_change_set(ChangeSetName=result.get("Id"), StackName="NewStack") + cf_conn.list_change_sets(StackName="NewStack")["Summaries"].should.have.length_of(0) + @mock_cloudformation @mock_ec2 @@ -1309,6 +1332,19 @@ def test_stack_events(): list(stack_events_to_look_for).should.be.empty + with pytest.raises(ClientError) as exp: + stack = cf.Stack("non_existing_stack") + events = list(stack.events.all()) + + exp_err = exp.value.response.get("Error") + exp_metadata = exp.value.response.get("ResponseMetadata") + + exp_err.get("Code").should.match(r"ValidationError") + exp_err.get("Message").should.match( + r"Stack with id non_existing_stack does not exist" + ) + exp_metadata.get("HTTPStatusCode").should.equal(400) + @mock_cloudformation def test_list_exports():