Skip to content

Commit

Permalink
Add ability to get VPC endpoint ID by VPC name and Service name (#161)
Browse files Browse the repository at this point in the history
  • Loading branch information
William Chieng authored Feb 13, 2020
1 parent 3121b6e commit 36d44b7
Show file tree
Hide file tree
Showing 3 changed files with 186 additions and 0 deletions.
10 changes: 10 additions & 0 deletions doc/symbol-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,16 @@ which is looked in 'proto3' as:<br>
returns:<br>
```vpce-123```

#### {{aws:ec2:vpc-endpoint/vpc-endpoint-id/by-vpc-service,\<vpc_name/service_name>}}
Returns: VPC Endpoint ID<br>
Needs: VPC friendly name and service name<br>
Example:<br>
```{{aws:ec2:vpc-endpoint/vpc-endpoint-id/by-vpc-service,vpc-{{ENV}}/com.amazonaws.{{REGION}}.execute-api}}```<br>
which is looked in 'proto3' as:<br>
```{{aws:ec2:vpc-endpoint/vpc-endpoint-id/by-vpc-service,vpc-proto3/com.amazonaws.us-east-1.execute-api}}```<br>
returns:<br>
```vpce-123```

#### {{aws:ec2:vpc/vpn-gateway-id,\<vgw_friendly_name>}}
Returns: Virtual Private Gateway's ID<br>
Needs: VPN Gateway's friendly name, which is always "vgw-\<az>"<br>
Expand Down
28 changes: 28 additions & 0 deletions efopen/ef_aws_resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,32 @@ def ec2_vpc_endpoint_id(self, lookup, default=None):
else:
return default

def ec2_vpc_endpoint_id_by_vpc_service(self, lookup, default=None):
"""
Args:
lookup: a forward-slash-delimited string of [vpc-name, service-name] "vpc-name/service-name"
default: the optional value to return if lookup failed; returns None if not set
Returns:
The ID of the OLDEST VPC endpoint found in the given VPC for the given service
"""
vpc_name, service_name = lookup.split("/")
vpc_id = self.ec2_vpc_vpc_id(vpc_name)
if vpc_id is None:
return default

vpc_endpoints = EFAwsResolver.__CLIENTS["ec2"].describe_vpc_endpoints(Filters=[
{"Name": "vpc-id", "Values":[vpc_id]},
{"Name": "service-name", "Values":[service_name]}
])
if len(vpc_endpoints.get("VpcEndpoints")) < 1:
return default

oldest = None
for vpce in vpc_endpoints.get("VpcEndpoints"):
if oldest is None or vpce["CreationTimestamp"] < oldest["CreationTimestamp"]:
oldest = vpce
return oldest["VpcEndpointId"]

def ec2_vpc_vpn_gateway_id(self, lookup, default=None):
"""
Args:
Expand Down Expand Up @@ -838,6 +864,8 @@ def lookup(self, token):
return self.ec2_vpc_vpc_id(*kv[1:])
elif kv[0] == "ec2:vpc-endpoint/vpc-endpoint-id":
return self.ec2_vpc_endpoint_id(*kv[1:])
elif kv[0] == "ec2:vpc-endpoint/vpc-endpoint-id/by-vpc-service":
return self.ec2_vpc_endpoint_id_by_vpc_service(*kv[1:])
elif kv[0] == "ec2:vpc/vpn-gateway-id":
return self.ec2_vpc_vpn_gateway_id(*kv[1:])
elif kv[0] == "elbv2:load-balancer/dns-name":
Expand Down
148 changes: 148 additions & 0 deletions tests/unit_tests/test_ef_aws_resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -956,6 +956,154 @@ def test_ec2_vpc_endpoint_id_none(self):
result = ef_aws_resolver.lookup("ec2:vpc-endpoint/vpc-endpoint-id,target_vpc_endpoint_name")
self.assertIsNone(result)

def test_ec2_vpc_endpoint_id_by_vpc_service_one_vpce(self):
"""
Tests that this function returns the VPC endpoint ID when it finds the VPC ID,
and a single VPC endpoint matching the VPC ID + service.
Returns:
None
Raises:
AssertionError if any of the assert checks fail
"""
expected_vpce_id = "vpce-456"
describe_vpc_response = {
"Vpcs": [
{
"VpcId": "vpc-123"
}
]
}
describe_vpce_response = {
"VpcEndpoints": [
{
"VpcEndpointId": "vpce-456",
"VpcEndpointType": "Interface",
"VpcId": "vpc-123",
"CreationTimestamp": datetime.datetime(2020,1,2),
"ServiceName": "target_service_name"
}
]
}
self._clients["ec2"].describe_vpcs.return_value = describe_vpc_response
self._clients["ec2"].describe_vpc_endpoints.return_value = describe_vpce_response
ef_aws_resolver = EFAwsResolver(self._clients)
result = ef_aws_resolver.lookup("ec2:vpc-endpoint/vpc-endpoint-id/by-vpc-service,target_vpc_name/target_service_name")
self.assertEquals(expected_vpce_id, result)

def test_ec2_vpc_endpoint_id_by_vpc_service_3_vpces(self):
"""
Tests that this function returns the ID of the oldest VPC endpoint when it finds multiple
VPC endpoints in this VPC for this service.
Returns:
None
Raises:
AssertionError if any of the assert checks fail
"""
expected_vpce_id = "vpce-2" # Oldest creation timestamp
describe_vpc_response = {
"Vpcs": [
{
"VpcId": "vpc-123"
}
]
}
describe_vpce_response = {
"VpcEndpoints": [
{
"VpcEndpointId": "vpce-1",
"VpcEndpointType": "Interface",
"VpcId": "vpc-123",
"CreationTimestamp": datetime.datetime(2020,1,2),
"ServiceName": "target_service_name"
},
{
"VpcEndpointId": "vpce-2",
"VpcEndpointType": "Interface",
"VpcId": "vpc-123",
"CreationTimestamp": datetime.datetime(2019,12,2),
"ServiceName": "target_service_name"
},
{
"VpcEndpointId": "vpce-3",
"VpcEndpointType": "Interface",
"VpcId": "vpc-123",
"CreationTimestamp": datetime.datetime(2020,3,2),
"ServiceName": "target_service_name"
}
]
}
self._clients["ec2"].describe_vpcs.return_value = describe_vpc_response
self._clients["ec2"].describe_vpc_endpoints.return_value = describe_vpce_response
ef_aws_resolver = EFAwsResolver(self._clients)
result = ef_aws_resolver.lookup("ec2:vpc-endpoint/vpc-endpoint-id/by-vpc-service,target_vpc_name/target_service_name")
self.assertEquals(expected_vpce_id, result)

def test_ec2_vpc_endpoint_id_by_vpc_service_no_vpc_id(self):
"""
Tests that this function returns the default value if no VPC ID could be found.
Returns:
None
Raises:
AssertionError if any of the assert checks fail
"""
describe_vpc_response = {
"Vpcs": []
}
self._clients["ec2"].describe_vpcs.return_value = describe_vpc_response
ef_aws_resolver = EFAwsResolver(self._clients)
result = ef_aws_resolver.lookup("ec2:vpc-endpoint/vpc-endpoint-id/by-vpc-service,target_vpc_name/target_service_name,default-value")
self.assertEquals("default-value", result)

def test_ec2_vpc_endpoint_id_by_vpc_service_no_vpce(self):
"""
Tests that this function returns the default value if no VPC endpoints are found.
Returns:
None
Raises:
AssertionError if any of the assert checks fail
"""
expected_vpce_id = "vpce-456"
describe_vpc_response = {
"Vpcs": [
{
"VpcId": "vpc-123"
}
]
}
describe_vpce_response = {
"VpcEndpoints": []
}
self._clients["ec2"].describe_vpcs.return_value = describe_vpc_response
self._clients["ec2"].describe_vpc_endpoints.return_value = describe_vpce_response
ef_aws_resolver = EFAwsResolver(self._clients)
result = ef_aws_resolver.lookup("ec2:vpc-endpoint/vpc-endpoint-id/by-vpc-service,target_vpc_name/target_service_name,default-value")
self.assertEquals("default-value", result)

def test_ec2_vpc_endpoint_id_by_vpc_service_missing_args(self):
"""
Tests that this function raises an Error if the vpc name or the service name are not provided.
Returns:
None
Raises:
AssertionError if any of the assert checks fail
"""
ef_aws_resolver = EFAwsResolver(self._clients)
try:
ef_aws_resolver.lookup("ec2:vpc-endpoint/vpc-endpoint-id/by-vpc-service,target_vpc_name,default-value")
self.assertIsNone("Should have raised an error")
except:
self.assertTrue(True)

def test_ec2_vpc_vpn_gateway_id(self):
"""
Tests VPN Gateway ID lookup
Expand Down

0 comments on commit 36d44b7

Please sign in to comment.