From 6e58dc3f1202c51cfb5c2f19f8f8b552bf56c706 Mon Sep 17 00:00:00 2001 From: Bert Blommers Date: Sun, 7 Nov 2021 15:01:39 -0100 Subject: [PATCH] CloudFormation - allow updates of unknown resources (#4538) --- moto/cloudformation/parsing.py | 5 +- .../test_cloudformation_stack_crud_boto3.py | 46 +++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/moto/cloudformation/parsing.py b/moto/cloudformation/parsing.py index bfc282701..5dbec31d8 100644 --- a/moto/cloudformation/parsing.py +++ b/moto/cloudformation/parsing.py @@ -335,9 +335,12 @@ def parse_and_create_resource(logical_id, resource_json, resources_map, region_n def parse_and_update_resource(logical_id, resource_json, resources_map, region_name): - resource_class, resource_json, new_resource_name = parse_resource_and_generate_name( + resource_tuple = parse_resource_and_generate_name( logical_id, resource_json, resources_map ) + if not resource_tuple: + return None + resource_class, resource_json, new_resource_name = resource_tuple original_resource = resources_map[logical_id] if not hasattr( resource_class.update_from_cloudformation_json, "__isabstractmethod__" diff --git a/tests/test_cloudformation/test_cloudformation_stack_crud_boto3.py b/tests/test_cloudformation/test_cloudformation_stack_crud_boto3.py index 7882384bb..59bb012c0 100644 --- a/tests/test_cloudformation/test_cloudformation_stack_crud_boto3.py +++ b/tests/test_cloudformation/test_cloudformation_stack_crud_boto3.py @@ -1,3 +1,4 @@ +import copy import json from collections import OrderedDict from datetime import datetime, timedelta @@ -239,6 +240,14 @@ dummy_template_special_chars_in_description = { }, } +dummy_unknown_template = { + "AWSTemplateFormatVersion": "2010-09-09", + "Description": "Stack 1", + "Resources": { + "UnknownResource": {"Type": "AWS::Cloud9::EnvironmentEC2", "Properties": {}}, + }, +} + dummy_template_json = json.dumps(dummy_template) dummy_template_special_chars_in_description_json = json.dumps( dummy_template_special_chars_in_description @@ -249,6 +258,7 @@ dummy_update_template_json = json.dumps(dummy_update_template) dummy_output_template_json = json.dumps(dummy_output_template) dummy_import_template_json = json.dumps(dummy_import_template) dummy_redrive_template_json = json.dumps(dummy_redrive_template) +dummy_unknown_template_json = json.dumps(dummy_unknown_template) @mock_cloudformation @@ -2112,6 +2122,42 @@ def test_create_stack_lambda_and_dynamodb(): resource_types.should.contain("AWS::Lambda::EventSourceMapping") +@mock_cloudformation +@mock_ec2 +def test_create_and_update_stack_with_unknown_resource(): + cf_conn = boto3.client("cloudformation", region_name="us-east-1") + # Creating a stack with an unknown resource should throw a warning + expected_err = "Tried to parse AWS::Cloud9::EnvironmentEC2 but it's not supported by moto's CloudFormation implementation" + if settings.TEST_SERVER_MODE: + # Can't verify warnings in ServerMode though + cf_conn.create_stack( + StackName="test_stack", TemplateBody=dummy_unknown_template_json + ) + else: + with pytest.warns(UserWarning, match=expected_err): + cf_conn.create_stack( + StackName="test_stack", TemplateBody=dummy_unknown_template_json + ) + + # The stack should exist though + stacks = cf_conn.describe_stacks()["Stacks"] + stacks.should.have.length_of(1) + stacks[0].should.have.key("StackName").equal("test_stack") + + # Updating an unknown resource should throw a warning, but not fail + new_template = copy.deepcopy(dummy_unknown_template) + new_template["Resources"]["UnknownResource"]["Properties"]["Sth"] = "other" + if settings.TEST_SERVER_MODE: + cf_conn.update_stack( + StackName="test_stack", TemplateBody=json.dumps(new_template) + ) + else: + with pytest.warns(UserWarning, match=expected_err): + cf_conn.update_stack( + StackName="test_stack", TemplateBody=json.dumps(new_template) + ) + + def get_role_name(): with mock_iam(): iam = boto3.client("iam", region_name="us-east-1")