From 32af8753864b181f3dc74d487cd6f3bcc2e302fb Mon Sep 17 00:00:00 2001 From: Hugo Lopes Tavares Date: Wed, 5 Nov 2014 12:11:56 -0500 Subject: [PATCH] Implement InternetGateway filters support More information about IGW filters: http://docs.aws.amazon.com/AWSEC2/latest/APIReference/ApiReference-query-DescribeInternetGateways.html#query-DescribeInternetGateways-filters --- moto/ec2/models.py | 7 +++ moto/ec2/responses/internet_gateways.py | 16 +++-- moto/ec2/utils.py | 79 +++++++++++++++++++----- tests/test_ec2/test_internet_gateways.py | 57 +++++++++++++++++ 4 files changed, 137 insertions(+), 22 deletions(-) diff --git a/moto/ec2/models.py b/moto/ec2/models.py index 89e25ba9c..92e1ac1af 100644 --- a/moto/ec2/models.py +++ b/moto/ec2/models.py @@ -1867,6 +1867,13 @@ class InternetGateway(TaggedEC2Resource): def physical_resource_id(self): return self.id + @property + def attachment_state(self): + if self.vpc: + return "available" + else: + return "detached" + class InternetGatewayBackend(object): def __init__(self): diff --git a/moto/ec2/responses/internet_gateways.py b/moto/ec2/responses/internet_gateways.py index c5b237e87..e6fb09962 100644 --- a/moto/ec2/responses/internet_gateways.py +++ b/moto/ec2/responses/internet_gateways.py @@ -1,8 +1,13 @@ from __future__ import unicode_literals from moto.core.responses import BaseResponse -from moto.ec2.utils import sequence_from_querystring +from moto.ec2.utils import ( + sequence_from_querystring, + filters_from_querystring, + filter_internet_gateways, +) from jinja2 import Template + class InternetGateways(BaseResponse): def attach_internet_gateway(self): igw_id = self.querystring.get("InternetGatewayId", [None])[0] @@ -23,15 +28,16 @@ class InternetGateways(BaseResponse): return template.render() def describe_internet_gateways(self): - if "Filter.1.Name" in self.querystring: - raise NotImplementedError( - "Filtering not supported in describe_internet_gateways.") - elif "InternetGatewayId.1" in self.querystring: + if "InternetGatewayId.1" in self.querystring: igw_ids = sequence_from_querystring( "InternetGatewayId", self.querystring) igws = self.ec2_backend.describe_internet_gateways(igw_ids) else: igws = self.ec2_backend.describe_internet_gateways() + + filter_dict = filters_from_querystring(self.querystring) + igws = filter_internet_gateways(igws, filter_dict) + template = Template(DESCRIBE_INTERNET_GATEWAYS_RESPONSE) return template.render(internet_gateways=igws) diff --git a/moto/ec2/utils.py b/moto/ec2/utils.py index d5fba9d5f..bb15f7920 100644 --- a/moto/ec2/utils.py +++ b/moto/ec2/utils.py @@ -269,17 +269,9 @@ def keypair_names_from_querystring(querystring_dict): return keypair_names -filter_dict_attribute_mapping = { - 'instance-state-name': 'state', - 'instance-id': 'id', - 'state-reason-code': '_state_reason.code', - 'source-dest-check': 'source_dest_check', - 'vpc-id': 'vpc_id', -} - -def get_instance_value(instance, instance_attr): - keys = instance_attr.split('.') - val = instance +def get_object_value(obj, attr): + keys = attr.split('.') + val = obj for key in keys: if hasattr(val, key): val = getattr(val, key) @@ -289,18 +281,40 @@ def get_instance_value(instance, instance_attr): return None return val + +def is_tag_filter(filter_name): + return filter_name.startswith('tag:') + + +def get_obj_tag(obj, filter_name): + tag_name = filter_name.replace('tag:', '', 1) + tags = dict((tag['key'], tag['value']) for tag in obj.get_tags()) + return tags.get(tag_name) + + +def tag_filter_matches(obj, filter_name, filter_values): + tag_value = get_obj_tag(obj, filter_name) + return tag_value in filter_values + + +filter_dict_attribute_mapping = { + 'instance-state-name': 'state', + 'instance-id': 'id', + 'state-reason-code': '_state_reason.code', + 'source-dest-check': 'source_dest_check', + 'vpc-id': 'vpc_id', +} + + def passes_filter_dict(instance, filter_dict): for filter_name, filter_values in filter_dict.items(): if filter_name in filter_dict_attribute_mapping: instance_attr = filter_dict_attribute_mapping[filter_name] - instance_value = get_instance_value(instance, instance_attr) + instance_value = get_object_value(instance, instance_attr) if instance_value not in filter_values: return False - elif filter_name.startswith('tag:'): - tags = dict((tag['key'], tag['value']) for tag in instance.get_tags()) - tag_name = filter_name.replace('tag:', '', 1) - tag_value = tags.get(tag_name) - if tag_value not in filter_values: + elif is_tag_filter(filter_name): + if not tag_filter_matches(instance, filter_name, filter_values): return False else: raise NotImplementedError( @@ -322,6 +336,37 @@ def filter_reservations(reservations, filter_dict): return result +filter_dict_igw_mapping = { + "attachment.vpc-id": "vpc.id", + "attachment.state": "attachment_state", + "internet-gateway-id": "id", +} + + +def passes_igw_filter_dict(igw, filter_dict): + for filter_name, filter_values in filter_dict.items(): + if filter_name in filter_dict_igw_mapping: + igw_attr = filter_dict_igw_mapping[filter_name] + if get_object_value(igw, igw_attr) not in filter_values: + return False + elif is_tag_filter(filter_name): + if not tag_filter_matches(igw, filter_name, filter_values): + return False + else: + raise NotImplementedError( + "Internet Gateway filter dicts have not been implemented in Moto for '%s' yet. Feel free to open an issue at https://github.com/spulec/moto/issues", + filter_name) + return True + + +def filter_internet_gateways(igws, filter_dict): + result = [] + for igw in igws: + if passes_igw_filter_dict(igw, filter_dict): + result.append(igw) + return result + + def is_filter_matching(obj, filter, filter_value): value = obj.get_filter_value(filter) diff --git a/tests/test_ec2/test_internet_gateways.py b/tests/test_ec2/test_internet_gateways.py index da1545f09..a17ba37dc 100644 --- a/tests/test_ec2/test_internet_gateways.py +++ b/tests/test_ec2/test_internet_gateways.py @@ -163,3 +163,60 @@ def test_igw_desribe_bad_id(): cm.exception.code.should.equal('InvalidInternetGatewayID.NotFound') cm.exception.status.should.equal(400) cm.exception.request_id.should_not.be.none + + +@mock_ec2 +def test_igw_filter_by_vpc_id(): + """ internet gateway filter by vpc id """ + conn = boto.connect_vpc('the_key', 'the_secret') + + igw1 = conn.create_internet_gateway() + igw2 = conn.create_internet_gateway() + vpc = conn.create_vpc(VPC_CIDR) + conn.attach_internet_gateway(igw1.id, vpc.id) + + result = conn.get_all_internet_gateways(filters={"attachment.vpc-id": vpc.id}) + result.should.have.length_of(1) + result[0].id.should.equal(igw1.id) + + +@mock_ec2 +def test_igw_filter_by_tags(): + """ internet gateway filter by vpc id """ + conn = boto.connect_vpc('the_key', 'the_secret') + + igw1 = conn.create_internet_gateway() + igw2 = conn.create_internet_gateway() + igw1.add_tag("tests", "yes") + + result = conn.get_all_internet_gateways(filters={"tag:tests": "yes"}) + result.should.have.length_of(1) + result[0].id.should.equal(igw1.id) + + +@mock_ec2 +def test_igw_filter_by_internet_gateway_id(): + """ internet gateway filter by internet gateway id """ + conn = boto.connect_vpc('the_key', 'the_secret') + + igw1 = conn.create_internet_gateway() + igw2 = conn.create_internet_gateway() + + result = conn.get_all_internet_gateways(filters={"internet-gateway-id": igw1.id}) + result.should.have.length_of(1) + result[0].id.should.equal(igw1.id) + + +@mock_ec2 +def test_igw_filter_by_attachment_state(): + """ internet gateway filter by attachment state """ + conn = boto.connect_vpc('the_key', 'the_secret') + + igw1 = conn.create_internet_gateway() + igw2 = conn.create_internet_gateway() + vpc = conn.create_vpc(VPC_CIDR) + conn.attach_internet_gateway(igw1.id, vpc.id) + + result = conn.get_all_internet_gateways(filters={"attachment.state": "available"}) + result.should.have.length_of(1) + result[0].id.should.equal(igw1.id)