diff --git a/moto/ec2/models.py b/moto/ec2/models.py index a9e9f0ad7..b2e6b050e 100644 --- a/moto/ec2/models.py +++ b/moto/ec2/models.py @@ -1,9 +1,9 @@ from __future__ import unicode_literals -import six import copy import itertools from collections import defaultdict +import six import boto from boto.ec2.instance import Instance as BotoInstance, Reservation from boto.ec2.blockdevicemapping import BlockDeviceMapping, BlockDeviceType @@ -70,7 +70,7 @@ from .utils import ( random_volume_id, random_vpc_id, random_vpc_peering_connection_id, -) + is_filter_matching) class InstanceState(object): @@ -93,6 +93,9 @@ class TaggedEC2Instance(object): if tag['key'] == tagname: return tag['value'] + if filter_name == 'tag-key': + return [tag['key'] 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): @@ -1194,7 +1197,7 @@ class VPCBackend(object): if filters: for (_filter, _filter_value) in filters.items(): - vpcs = [ vpc for vpc in vpcs if vpc.get_filter_value(_filter) in _filter_value ] + vpcs = [ vpc for vpc in vpcs if is_filter_matching(vpc, _filter, _filter_value) ] return vpcs diff --git a/moto/ec2/utils.py b/moto/ec2/utils.py index 181732856..a6935aa2c 100644 --- a/moto/ec2/utils.py +++ b/moto/ec2/utils.py @@ -278,6 +278,19 @@ def filter_reservations(reservations, filter_dict): return result +def is_filter_matching(obj, filter, filter_value): + value = obj.get_filter_value(filter) + + if isinstance(value, six.string_types): + return value in filter_value + + try: + value = set(value) + return value.issubset(filter_value) or value.issuperset(filter_value) + except TypeError: + return value in filter_value + + # 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 d9d06fd68..47dc855c4 100644 --- a/tests/test_ec2/test_vpcs.py +++ b/tests/test_ec2/test_vpcs.py @@ -1,6 +1,6 @@ from __future__ import unicode_literals # Ensure 'assert_raises' context manager support for Python 2.6 -#import tests.backport_assert_raises +# import tests.backport_assert_raises from nose.tools import assert_raises import boto @@ -128,4 +128,43 @@ def test_vpc_get_by_tag(): 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_key_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-key': 'Name'}) + 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_key_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') + vpc3.add_tag('Test', 'TestVPC2') + + vpcs = conn.get_all_vpcs(filters={'tag-key': ['Name', 'Key']}) + 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