From bcc3e57949831b31661c1c91d739128e8bbd56d9 Mon Sep 17 00:00:00 2001 From: David Wilcox Date: Sun, 5 Mar 2017 14:26:23 +1100 Subject: [PATCH] Cloudformation ResourceMaps incorrectly share namespaces for Conditions and Resources (#828) * add tests to check CF's conditions and resources have distinct namespace * separate the resource and condition namespaces for CF --- moto/cloudformation/parsing.py | 14 ++--- .../test_cloudformation_stack_crud.py | 54 +++++++++++++++++++ 2 files changed, 61 insertions(+), 7 deletions(-) diff --git a/moto/cloudformation/parsing.py b/moto/cloudformation/parsing.py index 521658cee..fdc569dc1 100644 --- a/moto/cloudformation/parsing.py +++ b/moto/cloudformation/parsing.py @@ -143,7 +143,7 @@ def clean_json(resource_json, resources_map): if 'Fn::If' in resource_json: condition_name, true_value, false_value = resource_json['Fn::If'] - if resources_map[condition_name]: + if resources_map.lazy_condition_map[condition_name]: return clean_json(true_value, resources_map) else: return clean_json(false_value, resources_map) @@ -206,7 +206,7 @@ def parse_resource(logical_id, resource_json, resources_map): def parse_and_create_resource(logical_id, resource_json, resources_map, region_name): condition = resource_json.get('Condition') - if condition and not resources_map[condition]: + if condition and not resources_map.lazy_condition_map[condition]: # If this has a False condition, don't create the resource return None @@ -352,13 +352,13 @@ class ResourceMap(collections.Mapping): def load_conditions(self): conditions = self._template.get('Conditions', {}) - lazy_condition_map = LazyDict() + self.lazy_condition_map = LazyDict() for condition_name, condition in conditions.items(): - lazy_condition_map[condition_name] = functools.partial(parse_condition, - condition, self._parsed_resources, lazy_condition_map) + self.lazy_condition_map[condition_name] = functools.partial(parse_condition, + condition, self._parsed_resources, self.lazy_condition_map) - for condition_name in lazy_condition_map: - self._parsed_resources[condition_name] = lazy_condition_map[condition_name] + for condition_name in self.lazy_condition_map: + _ = self.lazy_condition_map[condition_name] def create(self): self.load_mapping() diff --git a/tests/test_cloudformation/test_cloudformation_stack_crud.py b/tests/test_cloudformation/test_cloudformation_stack_crud.py index e45dafbfa..0696d5ada 100644 --- a/tests/test_cloudformation/test_cloudformation_stack_crud.py +++ b/tests/test_cloudformation/test_cloudformation_stack_crud.py @@ -279,6 +279,60 @@ def test_cloudformation_params(): param.value.should.equal('testing123') +@mock_cloudformation() +def test_cloudformation_params_conditions_and_resources_are_distinct(): + dummy_template = { + "AWSTemplateFormatVersion": "2010-09-09", + "Description": "Stack 1", + "Conditions": { + "FooEnabled": { + "Fn::Equals": [ + { + "Ref": "FooEnabled" + }, + "true" + ] + }, + "FooDisabled": { + "Fn::Not": [ + { + "Fn::Equals": [ + { + "Ref": "FooEnabled" + }, + "true" + ] + } + ] + } + }, + "Parameters": { + "FooEnabled": { + "Type": "String", + "AllowedValues": [ + "true", + "false" + ] + } + }, + "Resources": { + "Bar": { + "Properties": { + "CidrBlock": "192.168.0.0/16", + }, + "Condition": "FooDisabled", + "Type": "AWS::EC2::VPC" + } + } + } + dummy_template_json = json.dumps(dummy_template) + cfn = boto.connect_cloudformation() + cfn.create_stack('test_stack1', template_body=dummy_template_json, parameters=[('FooEnabled', 'true')]) + stack = cfn.describe_stacks('test_stack1')[0] + resources = stack.list_resources() + assert not [resource for resource in resources if resource.logical_resource_id == 'Bar'] + + @mock_cloudformation def test_stack_tags(): conn = boto.connect_cloudformation()