From 832769b8a7b08707985545588fbae9e0381ab42f Mon Sep 17 00:00:00 2001 From: Joseph Lawson Date: Mon, 20 Oct 2014 15:16:39 -0400 Subject: [PATCH] Name resources via CloudFormation naming convention and tag EC2 resources. * Make sure taggable resources are tagged per convention: http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-stack-parameters.html * Update CloudFormation parsing to use logical resource ids and name resources as CloudFomation would. * Fix tests for CloudFormation stack integration. --- moto/cloudformation/parsing.py | 37 ++++++++++++------- .../test_cloudformation_stack_integration.py | 26 +++++++------ 2 files changed, 39 insertions(+), 24 deletions(-) diff --git a/moto/cloudformation/parsing.py b/moto/cloudformation/parsing.py index 46adf154..71597c10 100644 --- a/moto/cloudformation/parsing.py +++ b/moto/cloudformation/parsing.py @@ -94,7 +94,7 @@ def resource_name_property_from_type(resource_type): return NAME_TYPE_MAP.get(resource_type) -def parse_resource(resource_name, resource_json, resources_map): +def parse_resource(logical_id, resource_json, resources_map): resource_type = resource_json['Type'] resource_class = resource_class_from_type(resource_type) if not resource_class: @@ -108,11 +108,17 @@ def parse_resource(resource_name, resource_json, resources_map): if not resource_name_property in resource_json['Properties']: resource_json['Properties'][resource_name_property] = '{0}-{1}-{2}'.format( resources_map.get('AWS::StackName'), - resource_name, + logical_id, random_suffix()) + resource_name = resource_json['Properties'][resource_name_property] + else: + resource_name = '{0}-{1}-{2}'.format(resources_map.get('AWS::StackName'), + logical_id, + random_suffix()) + resource = resource_class.create_from_cloudformation_json(resource_name, resource_json) resource.type = resource_type - resource.logical_resource_id = resource_name + resource.logical_resource_id = logical_id return resource @@ -136,24 +142,24 @@ class ResourceMap(collections.Mapping): } def __getitem__(self, key): - resource_name = key + resource_logical_id = key - if resource_name in self._parsed_resources: - return self._parsed_resources[resource_name] + if resource_logical_id in self._parsed_resources: + return self._parsed_resources[resource_logical_id] else: - resource_json = self._resource_json_map.get(resource_name) - new_resource = parse_resource(resource_name, resource_json, self) - self._parsed_resources[resource_name] = new_resource + resource_json = self._resource_json_map.get(resource_logical_id) + new_resource = parse_resource(resource_logical_id, resource_json, self) + self._parsed_resources[resource_logical_id] = new_resource return new_resource def __iter__(self): - return iter(self.resource_names) + return iter(self.resources) def __len__(self): return len(self._resource_json_map) @property - def resource_names(self): + def resources(self): return self._resource_json_map.keys() def load_parameters(self): @@ -167,5 +173,10 @@ class ResourceMap(collections.Mapping): # Since this is a lazy map, to create every object we just need to # iterate through self. - for resource_name in self.resource_names: - self[resource_name] + tags = {'aws:cloudformation:stack-name': self.get('AWS::StackName'), + 'aws:cloudformation:stack-id': self.get('AWS::StackId')} + for resource in self.resources: + self[resource] + if isinstance(self[resource], ec2_models.TaggedEC2Resource): + tags['aws:cloudformation:logical-id'] = resource + ec2_models.ec2_backend.create_tags([self[resource].physical_resource_id],tags) diff --git a/tests/test_cloudformation/test_cloudformation_stack_integration.py b/tests/test_cloudformation/test_cloudformation_stack_integration.py index befb6adf..499762d3 100644 --- a/tests/test_cloudformation/test_cloudformation_stack_integration.py +++ b/tests/test_cloudformation/test_cloudformation_stack_integration.py @@ -75,7 +75,7 @@ def test_stack_ec2_integration(): stack = conn.describe_stacks()[0] instance = stack.describe_resources()[0] instance.resource_type.should.equal('AWS::EC2::Instance') - instance.logical_resource_id.should.equal("WebServerGroup") + instance.logical_resource_id.should.contain("WebServerGroup") instance.physical_resource_id.should.equal(ec2_instance.id) @@ -186,7 +186,7 @@ def test_stack_security_groups(): ec2_conn = boto.connect_ec2() security_groups = ec2_conn.get_all_security_groups() for group in security_groups: - if group.name == "InstanceSecurityGroup": + if "InstanceSecurityGroup" in group.name: instance_group = group else: other_group = group @@ -245,6 +245,7 @@ def test_autoscaling_group_with_elb(): "InstancePort": "80", "Protocol": "HTTP" }], + "LoadBalancerName": "my-elb", "HealthCheck": { "Target": "80", "HealthyThreshold": "3", @@ -267,7 +268,7 @@ def test_autoscaling_group_with_elb(): autoscale_conn = boto.connect_autoscale() autoscale_group = autoscale_conn.get_all_groups()[0] - autoscale_group.launch_config_name.should.equal("my-launch-config") + autoscale_group.launch_config_name.should.contain("my-launch-config") autoscale_group.load_balancers[0].should.equal('my-elb') # Confirm the Launch config was actually created @@ -280,13 +281,13 @@ def test_autoscaling_group_with_elb(): stack = conn.describe_stacks()[0] resources = stack.describe_resources() as_group_resource = [resource for resource in resources if resource.resource_type == 'AWS::AutoScaling::AutoScalingGroup'][0] - as_group_resource.physical_resource_id.should.equal("my-as-group") + as_group_resource.physical_resource_id.should.contain("my-as-group") launch_config_resource = [resource for resource in resources if resource.resource_type == 'AWS::AutoScaling::LaunchConfiguration'][0] - launch_config_resource.physical_resource_id.should.equal("my-launch-config") + launch_config_resource.physical_resource_id.should.contain("my-launch-config") elb_resource = [resource for resource in resources if resource.resource_type == 'AWS::ElasticLoadBalancing::LoadBalancer'][0] - elb_resource.physical_resource_id.should.equal("my-elb") + elb_resource.physical_resource_id.should.contain("my-elb") @mock_ec2() @@ -427,18 +428,21 @@ def test_iam_roles(): iam_conn = boto.connect_iam() - role = iam_conn.get_role("my-role") - role.role_name.should.equal("my-role") + role_result = iam_conn.list_roles()['list_roles_response']['list_roles_result']['roles'][0] + role = iam_conn.get_role(role_result.role_name) + role.role_name.should.contain("my-role") role.path.should.equal("my-path") - instance_profile = iam_conn.get_instance_profile("my-instance-profile") - instance_profile.instance_profile_name.should.equal("my-instance-profile") + instance_profile_response = iam_conn.list_instance_profiles()['list_instance_profiles_response'] + cfn_instance_profile = instance_profile_response['list_instance_profiles_result']['instance_profiles'][0] + instance_profile = iam_conn.get_instance_profile(cfn_instance_profile.instance_profile_name) + instance_profile.instance_profile_name.should.contain("my-instance-profile") instance_profile.path.should.equal("my-path") instance_profile.role_id.should.equal(role.role_id) autoscale_conn = boto.connect_autoscale() launch_config = autoscale_conn.get_all_launch_configurations()[0] - launch_config.instance_profile_name.should.equal("my-instance-profile") + launch_config.instance_profile_name.should.contain("my-instance-profile") stack = conn.describe_stacks()[0] resources = stack.describe_resources()