diff --git a/moto/ec2/models.py b/moto/ec2/models.py index b2e6b050e..6f9ada1f3 100644 --- a/moto/ec2/models.py +++ b/moto/ec2/models.py @@ -70,7 +70,7 @@ from .utils import ( random_volume_id, random_vpc_id, random_vpc_peering_connection_id, - is_filter_matching) + generic_filter) class InstanceState(object): @@ -96,6 +96,9 @@ class TaggedEC2Instance(object): if filter_name == 'tag-key': return [tag['key'] for tag in tags] + if filter_name == 'tag-value': + return [tag['value'] for tag in tags] + class NetworkInterface(object): def __init__(self, subnet, private_ip_address, device_index=0, public_ip_auto_assign=True, group_ids=None): @@ -1156,7 +1159,7 @@ class VPC(TaggedEC2Instance): filter_value = super(VPC, self).get_filter_value(filter_name) - if not filter_value: + if filter_value is None: msg = "The filter '{0}' for DescribeVPCs has not been" \ " implemented in Moto yet. Feel free to open an issue at" \ " https://github.com/spulec/moto/issues".format(filter_name) @@ -1195,11 +1198,7 @@ class VPCBackend(object): else: vpcs = self.vpcs.values() - if filters: - for (_filter, _filter_value) in filters.items(): - vpcs = [ vpc for vpc in vpcs if is_filter_matching(vpc, _filter, _filter_value) ] - - return vpcs + return generic_filter(filters, vpcs) def delete_vpc(self, vpc_id): # Delete route table if only main route table remains. diff --git a/moto/ec2/utils.py b/moto/ec2/utils.py index a6935aa2c..8a0c702d3 100644 --- a/moto/ec2/utils.py +++ b/moto/ec2/utils.py @@ -286,11 +286,19 @@ def is_filter_matching(obj, filter, filter_value): try: value = set(value) - return value.issubset(filter_value) or value.issuperset(filter_value) + return (value and value.issubset(filter_value)) or value.issuperset(filter_value) except TypeError: return value in filter_value +def generic_filter(filters, objects): + if filters: + for (_filter, _filter_value) in filters.items(): + objects = [obj for obj in objects if is_filter_matching(obj, _filter, _filter_value)] + + return objects + + # not really random ( http://xkcd.com/221/ ) def random_key_pair(): return { diff --git a/tests/test_ec2/test_vpcs.py b/tests/test_ec2/test_vpcs.py index 47dc855c4..9f3bc8351 100644 --- a/tests/test_ec2/test_vpcs.py +++ b/tests/test_ec2/test_vpcs.py @@ -150,6 +150,7 @@ def test_vpc_get_by_tag_key_superset(): vpc1.id.should.be.within(vpc_ids) vpc2.id.should.be.within(vpc_ids) + @mock_ec2 def test_vpc_get_by_tag_key_subset(): conn = boto.connect_vpc() @@ -167,4 +168,43 @@ def test_vpc_get_by_tag_key_subset(): vpcs.should.have.length_of(2) vpc_ids = tuple(map(lambda v: v.id, vpcs)) vpc1.id.should.be.within(vpc_ids) + vpc2.id.should.be.within(vpc_ids) + + +@mock_ec2 +def test_vpc_get_by_tag_value_superset(): + conn = boto.connect_vpc() + vpc1 = conn.create_vpc("10.0.0.0/16") + vpc2 = conn.create_vpc("10.0.0.0/16") + vpc3 = conn.create_vpc("10.0.0.0/24") + + vpc1.add_tag('Name', 'TestVPC') + vpc1.add_tag('Key', 'TestVPC2') + vpc2.add_tag('Name', 'TestVPC') + vpc2.add_tag('Key', 'TestVPC2') + vpc3.add_tag('Key', 'TestVPC2') + + vpcs = conn.get_all_vpcs(filters={'tag-value': 'TestVPC'}) + vpcs.should.have.length_of(2) + vpc_ids = tuple(map(lambda v: v.id, vpcs)) + vpc1.id.should.be.within(vpc_ids) + vpc2.id.should.be.within(vpc_ids) + + +@mock_ec2 +def test_vpc_get_by_tag_value_subset(): + conn = boto.connect_vpc() + vpc1 = conn.create_vpc("10.0.0.0/16") + vpc2 = conn.create_vpc("10.0.0.0/16") + vpc3 = conn.create_vpc("10.0.0.0/24") + + vpc1.add_tag('Name', 'TestVPC') + vpc1.add_tag('Key', 'TestVPC2') + vpc2.add_tag('Name', 'TestVPC') + vpc2.add_tag('Key', 'TestVPC2') + + vpcs = conn.get_all_vpcs(filters={'tag-value': ['TestVPC', 'TestVPC2']}) + vpcs.should.have.length_of(2) + vpc_ids = tuple(map(lambda v: v.id, vpcs)) + vpc1.id.should.be.within(vpc_ids) vpc2.id.should.be.within(vpc_ids) \ No newline at end of file