Add tagging to all applicable EC2 objects. Closes #66.

This commit is contained in:
Steve Pulec 2014-05-11 19:00:28 -04:00
parent 06481ebe7e
commit 955b4c6c4a
9 changed files with 159 additions and 22 deletions

View File

@ -36,7 +36,13 @@ class InstanceState(object):
self.code = code
class Instance(BotoInstance):
class TaggedEC2Instance(object):
def get_tags(self, *args, **kwargs):
tags = ec2_backend.describe_tags(self.id)
return tags
class Instance(BotoInstance, TaggedEC2Instance):
def __init__(self, image_id, user_data, security_groups, **kwargs):
super(Instance, self).__init__()
self.id = random_instance_id()
@ -86,10 +92,6 @@ class Instance(BotoInstance):
self._state.name = "running"
self._state.code = 16
def get_tags(self, *args, **kwargs):
tags = ec2_backend.describe_tags(self.id)
return tags
class InstanceBackend(object):
@ -267,7 +269,7 @@ class TagBackend(object):
return results
class Ami(object):
class Ami(TaggedEC2Instance):
def __init__(self, ami_id, instance, name, description):
self.id = ami_id
self.instance = instance
@ -657,7 +659,7 @@ class EBSBackend(object):
return False
class VPC(object):
class VPC(TaggedEC2Instance):
def __init__(self, vpc_id, cidr_block):
self.id = vpc_id
self.cidr_block = cidr_block
@ -703,7 +705,7 @@ class VPCBackend(object):
return vpc
class Subnet(object):
class Subnet(TaggedEC2Instance):
def __init__(self, subnet_id, vpc_id, cidr_block):
self.id = subnet_id
self.vpc_id = vpc_id
@ -1061,7 +1063,7 @@ class ElasticAddressBackend(object):
return False
class DHCPOptionsSet(object):
class DHCPOptionsSet(TaggedEC2Instance):
def __init__(self, domain_name_servers=None, domain_name=None,
ntp_servers=None, netbios_name_servers=None,
netbios_node_type=None):
@ -1071,7 +1073,7 @@ class DHCPOptionsSet(object):
"ntp-servers": ntp_servers,
"netbios-name-servers": netbios_name_servers,
"netbios-node-type": netbios_node_type,
}
}
self.id = random_dhcp_option_id()
self.vpc = None
@ -1113,7 +1115,7 @@ class DHCPOptionsSetBackend(object):
if options_id in self.dhcp_options_sets:
if self.dhcp_options_sets[options_id].vpc:
raise DependencyViolationError("Cannot delete assigned DHCP options.")
dhcp_opt = self.dhcp_options_sets.pop(options_id)
self.dhcp_options_sets.pop(options_id)
else:
raise InvalidDHCPOptionsIdError(options_id)
return True

View File

@ -92,7 +92,16 @@ DESCRIBE_IMAGES_RESPONSE = """<DescribeImagesResponse xmlns="http://ec2.amazonaw
</item>
</blockDeviceMapping>
<virtualizationType>{{ image.virtualization_type }}</virtualizationType>
<tagSet/>
<tagSet>
{% for tag in image.get_tags() %}
<item>
<resourceId>{{ tag.resource_id }}</resourceId>
<resourceType>{{ tag.resource_type }}</resourceType>
<key>{{ tag.key }}</key>
<value>{{ tag.value }}</value>
</item>
{% endfor %}
</tagSet>
<hypervisor>xen</hypervisor>
</item>
{% endfor %}

View File

@ -6,8 +6,8 @@ from moto.ec2.utils import (
from moto.ec2.models import ec2_backend
from moto.ec2.exceptions import(
InvalidVPCIdError,
InvalidParameterValueError
)
InvalidParameterValueError,
)
NETBIOS_NODE_TYPES = [1, 2, 4, 8]
@ -52,7 +52,7 @@ class DHCPOptions(BaseResponse):
ntp_servers=ntp_servers,
netbios_name_servers=netbios_name_servers,
netbios_node_type=netbios_node_type
)
)
template = Template(CREATE_DHCP_OPTIONS_RESPONSE)
return template.render(dhcp_options_set=dhcp_options_set)
@ -105,7 +105,16 @@ CREATE_DHCP_OPTIONS_RESPONSE = u"""
{% endif %}
{% endfor %}
</dhcpConfigurationSet>
<tagSet/>
<tagSet>
{% for tag in dhcp_options_set.get_tags() %}
<item>
<resourceId>{{ tag.resource_id }}</resourceId>
<resourceType>{{ tag.resource_type }}</resourceType>
<key>{{ tag.key }}</key>
<value>{{ tag.value }}</value>
</item>
{% endfor %}
</tagSet>
</dhcpOptions>
</CreateDhcpOptionsResponse>
"""
@ -141,7 +150,16 @@ DESCRIBE_DHCP_OPTIONS_RESPONSE = u"""
{% endif %}
{% endfor %}
</dhcpConfigurationSet>
<tagSet/>
<tagSet>
{% for tag in dhcp_options_set.get_tags() %}
<item>
<resourceId>{{ tag.resource_id }}</resourceId>
<resourceType>{{ tag.resource_type }}</resourceType>
<key>{{ tag.key }}</key>
<value>{{ tag.value }}</value>
</item>
{% endfor %}
</tagSet>
</dhcpOptions>
{% endfor %}
</item>

View File

@ -37,7 +37,16 @@ CREATE_SUBNET_RESPONSE = """
<cidrBlock>{{ subnet.cidr_block }}</cidrBlock>
<availableIpAddressCount>251</availableIpAddressCount>
<availabilityZone>us-east-1a</availabilityZone>
<tagSet/>
<tagSet>
{% for tag in subnet.get_tags() %}
<item>
<resourceId>{{ tag.resource_id }}</resourceId>
<resourceType>{{ tag.resource_type }}</resourceType>
<key>{{ tag.key }}</key>
<value>{{ tag.value }}</value>
</item>
{% endfor %}
</tagSet>
</subnet>
</CreateSubnetResponse>"""
@ -59,7 +68,16 @@ DESCRIBE_SUBNETS_RESPONSE = """
<cidrBlock>{{ subnet.cidr_block }}</cidrBlock>
<availableIpAddressCount>251</availableIpAddressCount>
<availabilityZone>us-east-1a</availabilityZone>
<tagSet/>
<tagSet>
{% for tag in subnet.get_tags() %}
<item>
<resourceId>{{ tag.resource_id }}</resourceId>
<resourceType>{{ tag.resource_type }}</resourceType>
<key>{{ tag.key }}</key>
<value>{{ tag.value }}</value>
</item>
{% endfor %}
</tagSet>
</item>
{% endfor %}
</subnetSet>

View File

@ -35,7 +35,16 @@ CREATE_VPC_RESPONSE = """
<cidrBlock>{{ vpc.cidr_block }}</cidrBlock>
<dhcpOptionsId>dopt-1a2b3c4d2</dhcpOptionsId>
<instanceTenancy>default</instanceTenancy>
<tagSet/>
<tagSet>
{% for tag in vpc.get_tags() %}
<item>
<resourceId>{{ tag.resource_id }}</resourceId>
<resourceType>{{ tag.resource_type }}</resourceType>
<key>{{ tag.key }}</key>
<value>{{ tag.value }}</value>
</item>
{% endfor %}
</tagSet>
</vpc>
</CreateVpcResponse>"""
@ -50,7 +59,16 @@ DESCRIBE_VPCS_RESPONSE = """
<cidrBlock>{{ vpc.cidr_block }}</cidrBlock>
<dhcpOptionsId>dopt-7a8b9c2d</dhcpOptionsId>
<instanceTenancy>default</instanceTenancy>
<tagSet/>
<tagSet>
{% for tag in vpc.get_tags() %}
<item>
<resourceId>{{ tag.resource_id }}</resourceId>
<resourceType>{{ tag.resource_type }}</resourceType>
<key>{{ tag.key }}</key>
<value>{{ tag.value }}</value>
</item>
{% endfor %}
</tagSet>
</item>
{% endfor %}
</vpcSet>

View File

@ -22,6 +22,26 @@ def test_ami_create_and_delete():
success = conn.deregister_image.when.called_with(image).should.throw(EC2ResponseError)
@mock_ec2
def test_ami_tagging():
conn = boto.connect_vpc('the_key', 'the_secret')
reservation = conn.run_instances('ami-1234abcd')
instance = reservation.instances[0]
conn.create_image(instance.id, "test-ami", "this is a test ami")
image = conn.get_all_images()[0]
image.add_tag("a key", "some value")
tag = conn.get_all_tags()[0]
tag.name.should.equal("a key")
tag.value.should.equal("some value")
# Refresh the DHCP options
image = conn.get_all_images()[0]
image.tags.should.have.length_of(1)
image.tags["a key"].should.equal("some value")
@mock_ec2
def test_ami_create_from_missing_instance():
conn = boto.connect_ec2('the_key', 'the_secret')

View File

@ -114,5 +114,22 @@ def test_delete_dhcp_options():
def test_delete_dhcp_options_invalid_id():
conn = boto.connect_vpc('the_key', 'the_secret')
dhcp_option = conn.create_dhcp_options()
conn.create_dhcp_options()
conn.delete_dhcp_options.when.called_with("1").should.throw(EC2ResponseError)
@mock_ec2
def test_dhcp_tagging():
conn = boto.connect_vpc('the_key', 'the_secret')
dhcp_option = conn.create_dhcp_options()
dhcp_option.add_tag("a key", "some value")
tag = conn.get_all_tags()[0]
tag.name.should.equal("a key")
tag.value.should.equal("some value")
# Refresh the DHCP options
dhcp_option = conn.get_all_dhcp_options()[0]
dhcp_option.tags.should.have.length_of(1)
dhcp_option.tags["a key"].should.equal("some value")

View File

@ -21,3 +21,21 @@ def test_subnets():
conn.delete_subnet.when.called_with(
subnet.id).should.throw(EC2ResponseError)
@mock_ec2
def test_subnet_tagging():
conn = boto.connect_vpc('the_key', 'the_secret')
vpc = conn.create_vpc("10.0.0.0/16")
subnet = conn.create_subnet(vpc.id, "10.0.0.0/18")
subnet.add_tag("a key", "some value")
tag = conn.get_all_tags()[0]
tag.name.should.equal("a key")
tag.value.should.equal("some value")
# Refresh the subnet
subnet = conn.get_all_subnets()[0]
subnet.tags.should.have.length_of(1)
subnet.tags["a key"].should.equal("some value")

View File

@ -21,3 +21,20 @@ def test_vpcs():
conn.delete_vpc.when.called_with(
"vpc-1234abcd").should.throw(EC2ResponseError)
@mock_ec2
def test_vpc_tagging():
conn = boto.connect_vpc()
vpc = conn.create_vpc("10.0.0.0/16")
vpc.add_tag("a key", "some value")
tag = conn.get_all_tags()[0]
tag.name.should.equal("a key")
tag.value.should.equal("some value")
# Refresh the vpc
vpc = conn.get_all_vpcs()[0]
vpc.tags.should.have.length_of(1)
vpc.tags["a key"].should.equal("some value")