moto/tests/test_route53resolver/test_route53resolver_endpoint.py

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

962 lines
39 KiB
Python
Raw Normal View History

from datetime import datetime, timezone
import boto3
import pytest
from botocore.config import Config
from botocore.exceptions import ClientError
2024-01-07 12:03:33 +00:00
from moto import mock_aws, settings
2022-08-13 09:49:43 +00:00
from moto.core import DEFAULT_ACCOUNT_ID as ACCOUNT_ID
from moto.moto_api._internal import mock_random
TEST_REGION = "us-east-1" if settings.TEST_SERVER_MODE else "us-west-2"
def create_security_group(ec2_client):
"""Return a security group ID."""
group_name = "RRUnitTests"
# Does the security group already exist?
groups = ec2_client.describe_security_groups(
Filters=[{"Name": "group-name", "Values": [group_name]}]
)
# If so, we're done. Otherwise, create it.
if groups["SecurityGroups"]:
return groups["SecurityGroups"][0]["GroupId"]
response = ec2_client.create_security_group(
Description="Security group used by unit tests", GroupName=group_name
)
return response["GroupId"]
def create_vpc(ec2_client):
"""Return the ID for a valid VPC."""
return ec2_client.create_vpc(CidrBlock="10.0.0.0/16")["Vpc"]["VpcId"]
def create_subnets(ec2_client, vpc_id):
"""Returns the IDs for two valid subnets."""
subnet_ids = []
for cidr_block in ["10.0.1.0/24", "10.0.0.0/24"]:
subnet_ids.append(
ec2_client.create_subnet(
VpcId=vpc_id, CidrBlock=cidr_block, AvailabilityZone=f"{TEST_REGION}a"
)["Subnet"]["SubnetId"]
)
return subnet_ids
def create_test_endpoint(client, ec2_client, name=None, tags=None):
"""Create an endpoint that can be used for testing purposes.
Can't be used for unit tests that need to know/test the arguments.
"""
if not tags:
tags = []
random_num = mock_random.get_random_hex(10)
subnet_ids = create_subnets(ec2_client, create_vpc(ec2_client))
resolver_endpoint = client.create_resolver_endpoint(
CreatorRequestId=random_num,
Name=name if name else "X" + random_num,
SecurityGroupIds=[create_security_group(ec2_client)],
Direction="INBOUND",
IpAddresses=[
{"SubnetId": subnet_ids[0], "Ip": "10.0.1.200"},
{"SubnetId": subnet_ids[1], "Ip": "10.0.0.20"},
],
Tags=tags,
)
return resolver_endpoint["ResolverEndpoint"]
2024-01-07 12:03:33 +00:00
@mock_aws
def test_route53resolver_invalid_create_endpoint_args():
# Some validation is now part of botocore-1.34.3
# Disable validation to verify that Moto has the same validation
config = Config(parameter_validation=False)
client = boto3.client("route53resolver", region_name=TEST_REGION, config=config)
random_num = mock_random.get_random_hex(10)
# Verify ValidationException error messages are accumulated properly:
# - creator requestor ID that exceeds the allowed length of 255.
# - name that exceeds the allowed length of 64.
# - direction that's neither INBOUND or OUTBOUND.
# - more than 10 IP Address sets.
# - too many security group IDs.
long_id = random_num * 25 + "123456"
long_name = random_num * 6 + "abcde"
too_many_security_groups = ["sg-" + mock_random.get_random_hex(63)]
bad_direction = "foo"
too_many_ip_addresses = [{"SubnetId": f"{x}", "Ip": f"{x}" * 7} for x in range(11)]
with pytest.raises(ClientError) as exc:
client.create_resolver_endpoint(
CreatorRequestId=long_id,
Name=long_name,
SecurityGroupIds=too_many_security_groups,
Direction=bad_direction,
IpAddresses=too_many_ip_addresses,
)
err = exc.value.response["Error"]
assert err["Code"] == "ValidationException"
assert "5 validation errors detected" in err["Message"]
assert (
f"Value '{long_id}' at 'creatorRequestId' failed to satisfy constraint: "
f"Member must have length less than or equal to 255"
) in err["Message"]
assert (
f"Value '{too_many_security_groups}' at 'securityGroupIds' failed to "
f"satisfy constraint: Member must have length less than or equal to 64"
) in err["Message"]
assert (
f"Value '{long_name}' at 'name' failed to satisfy constraint: "
f"Member must have length less than or equal to 64"
) in err["Message"]
assert (
f"Value '{bad_direction}' at 'direction' failed to satisfy constraint: "
f"Member must satisfy enum value set: [INBOUND, OUTBOUND]"
) in err["Message"]
assert (
f"Value '{too_many_ip_addresses}' at 'ipAddresses' failed to satisfy "
f"constraint: Member must have length less than or equal to 10"
) in err["Message"]
# Some single ValidationException errors ...
bad_chars_in_name = "0@*3"
ok_group_ids = ["sg-" + random_num]
ok_ip_addrs = [{"SubnetId": f"{x}", "Ip": f"{x}" * 7} for x in range(10)]
with pytest.raises(ClientError) as exc:
client.create_resolver_endpoint(
CreatorRequestId=random_num,
Name=bad_chars_in_name,
SecurityGroupIds=ok_group_ids,
Direction="INBOUND",
IpAddresses=ok_ip_addrs,
)
err = exc.value.response["Error"]
assert err["Code"] == "ValidationException"
assert "1 validation error detected" in err["Message"]
assert (
rf"Value '{bad_chars_in_name}' at 'name' failed to satisfy constraint: "
rf"Member must satisfy regular expression pattern: "
rf"^(?!^[0-9]+$)([a-zA-Z0-9-_' ']+)$"
) in err["Message"]
subnet_too_long = [{"SubnetId": "a" * 33, "Ip": "1.2.3.4"}]
with pytest.raises(ClientError) as exc:
client.create_resolver_endpoint(
CreatorRequestId=random_num,
Name="X" + random_num,
SecurityGroupIds=ok_group_ids,
Direction="OUTBOUND",
IpAddresses=subnet_too_long,
)
err = exc.value.response["Error"]
assert err["Code"] == "ValidationException"
assert "1 validation error detected" in err["Message"]
assert (
f"Value '{subnet_too_long}' at 'ipAddresses.subnetId' failed to "
f"satisfy constraint: Member must have length less than or equal to 32"
) in err["Message"]
2024-01-07 12:03:33 +00:00
@mock_aws
def test_route53resolver_bad_create_endpoint_subnets():
# Some validation is now part of botocore-1.34.3
# Disable validation to verify that Moto has the same validation
config = Config(parameter_validation=False)
client = boto3.client("route53resolver", region_name=TEST_REGION, config=config)
ec2_client = boto3.client("ec2", region_name=TEST_REGION)
random_num = mock_random.get_random_hex(10)
# Need 2 IP addresses at the minimum.
subnet_ids = create_subnets(ec2_client, create_vpc(ec2_client))
with pytest.raises(ClientError) as exc:
client.create_resolver_endpoint(
CreatorRequestId=random_num,
Name="X" + random_num,
SecurityGroupIds=[f"sg-{random_num}"],
Direction="INBOUND",
IpAddresses=[{"SubnetId": subnet_ids[0], "Ip": "1.2.3.4"}],
)
err = exc.value.response["Error"]
assert err["Code"] == "InvalidRequestException"
assert "Resolver endpoint needs to have at least 2 IP addresses" in err["Message"]
# Need an IP that's within in the subnet.
bad_ip_addr = "1.2.3.4"
with pytest.raises(ClientError) as exc:
client.create_resolver_endpoint(
CreatorRequestId=random_num,
Name="X" + random_num,
SecurityGroupIds=[f"sg-{random_num}"],
Direction="INBOUND",
IpAddresses=[
{"SubnetId": subnet_ids[0], "Ip": bad_ip_addr},
{"SubnetId": subnet_ids[1], "Ip": bad_ip_addr},
],
)
err = exc.value.response["Error"]
assert err["Code"] == "InvalidRequestException"
assert (
f"IP address '{bad_ip_addr}' is either not in subnet "
f"'{subnet_ids[0]}' CIDR range or is reserved"
) in err["Message"]
# Bad subnet ID.
with pytest.raises(ClientError) as exc:
client.create_resolver_endpoint(
CreatorRequestId=random_num,
Name="X" + random_num,
SecurityGroupIds=[f"sg-{random_num}"],
Direction="INBOUND",
IpAddresses=[
{"SubnetId": "foo", "Ip": "1.2.3.4"},
{"SubnetId": subnet_ids[1], "Ip": "1.2.3.4"},
],
)
err = exc.value.response["Error"]
assert err["Code"] == "InvalidParameterException"
assert "The subnet ID 'foo' does not exist" in err["Message"]
# Can't reuse a ip address in a subnet.
subnet_ids = create_subnets(ec2_client, create_vpc(ec2_client))
with pytest.raises(ClientError) as exc:
client.create_resolver_endpoint(
CreatorRequestId="B" + random_num,
Name="B" + random_num,
SecurityGroupIds=[create_security_group(ec2_client)],
Direction="INBOUND",
IpAddresses=[
{"SubnetId": subnet_ids[0], "Ip": "10.0.1.200"},
{"SubnetId": subnet_ids[0], "Ip": "10.0.1.200"},
],
)
err = exc.value.response["Error"]
assert err["Code"] == "ResourceExistsException"
assert (
f"The IP address '10.0.1.200' in subnet '{subnet_ids[0]}' is already in use"
) in err["Message"]
2024-01-07 12:03:33 +00:00
@mock_aws
def test_route53resolver_bad_create_endpoint_security_groups():
"""Test bad security group scenarios for create_resolver_endpoint API."""
client = boto3.client("route53resolver", region_name=TEST_REGION)
ec2_client = boto3.client("ec2", region_name=TEST_REGION)
random_num = mock_random.get_random_hex(10)
subnet_ids = create_subnets(ec2_client, create_vpc(ec2_client))
ip_addrs = [
{"SubnetId": subnet_ids[0], "Ip": "10.0.1.200"},
{"SubnetId": subnet_ids[1], "Ip": "10.0.0.20"},
]
# Subnet must begin with "sg-".
with pytest.raises(ClientError) as exc:
client.create_resolver_endpoint(
CreatorRequestId=random_num,
Name="X" + random_num,
SecurityGroupIds=["foo"],
Direction="INBOUND",
IpAddresses=ip_addrs,
)
err = exc.value.response["Error"]
assert err["Code"] == "InvalidParameterException"
assert (
"Malformed security group ID: Invalid id: 'foo' (expecting 'sg-...')"
) in err["Message"]
# Non-existent security group id.
with pytest.raises(ClientError) as exc:
client.create_resolver_endpoint(
CreatorRequestId=random_num,
Name="X" + random_num,
SecurityGroupIds=["sg-abc"],
Direction="INBOUND",
IpAddresses=ip_addrs,
)
err = exc.value.response["Error"]
assert err["Code"] == "ResourceNotFoundException"
assert "The security group 'sg-abc' does not exist" in err["Message"]
# Too many security group ids.
with pytest.raises(ClientError) as exc:
client.create_resolver_endpoint(
CreatorRequestId=random_num,
Name="X" + random_num,
SecurityGroupIds=["sg-abc"] * 11,
Direction="INBOUND",
IpAddresses=ip_addrs,
)
err = exc.value.response["Error"]
assert err["Code"] == "InvalidParameterException"
assert "Maximum of 10 security groups are allowed" in err["Message"]
2024-01-07 12:03:33 +00:00
@mock_aws
def test_route53resolver_create_resolver_endpoint(): # pylint: disable=too-many-locals
"""Test good create_resolver_endpoint API calls."""
client = boto3.client("route53resolver", region_name=TEST_REGION)
ec2_client = boto3.client("ec2", region_name=TEST_REGION)
random_num = mock_random.get_random_hex(10)
vpc_id = create_vpc(ec2_client)
subnet_ids = create_subnets(ec2_client, vpc_id)
ip_addrs = [
{"SubnetId": subnet_ids[0], "Ip": "10.0.1.200"},
{"SubnetId": subnet_ids[1], "Ip": "10.0.0.20"},
]
security_group_id = create_security_group(ec2_client)
creator_request_id = random_num
name = "X" + random_num
response = client.create_resolver_endpoint(
CreatorRequestId=creator_request_id,
Name=name,
SecurityGroupIds=[security_group_id],
Direction="INBOUND",
IpAddresses=ip_addrs,
)
endpoint = response["ResolverEndpoint"]
id_value = endpoint["Id"]
assert id_value.startswith("rslvr-in-")
assert endpoint["CreatorRequestId"] == creator_request_id
assert (
endpoint["Arn"]
== f"arn:aws:route53resolver:{TEST_REGION}:{ACCOUNT_ID}:resolver-endpoint/{id_value}"
)
assert endpoint["Name"] == name
assert endpoint["SecurityGroupIds"] == [security_group_id]
assert endpoint["Direction"] == "INBOUND"
assert endpoint["IpAddressCount"] == 2
assert endpoint["HostVPCId"] == vpc_id
assert endpoint["Status"] == "OPERATIONAL"
assert "Successfully created Resolver Endpoint" in endpoint["StatusMessage"]
time_format = "%Y-%m-%dT%H:%M:%S.%f+00:00"
now = datetime.now(timezone.utc).replace(tzinfo=None)
creation_time = datetime.strptime(endpoint["CreationTime"], time_format)
creation_time = creation_time.replace(tzinfo=None)
assert creation_time <= now
modification_time = datetime.strptime(endpoint["ModificationTime"], time_format)
modification_time = modification_time.replace(tzinfo=None)
assert modification_time <= now
2024-01-07 12:03:33 +00:00
@mock_aws
def test_route53resolver_other_create_resolver_endpoint_errors():
"""Test other error scenarios for create_resolver_endpoint API calls."""
client = boto3.client("route53resolver", region_name=TEST_REGION)
ec2_client = boto3.client("ec2", region_name=TEST_REGION)
# Create a good endpoint that we can use to test.
created_endpoint = create_test_endpoint(client, ec2_client)
request_id = created_endpoint["CreatorRequestId"]
# Attempt to create another endpoint with the same creator request id.
vpc_id = create_vpc(ec2_client)
subnet_ids = create_subnets(ec2_client, vpc_id)
with pytest.raises(ClientError) as exc:
client.create_resolver_endpoint(
CreatorRequestId=created_endpoint["CreatorRequestId"],
Name="X" + mock_random.get_random_hex(10),
SecurityGroupIds=created_endpoint["SecurityGroupIds"],
Direction="INBOUND",
IpAddresses=[
{"SubnetId": subnet_ids[0], "Ip": "10.0.1.200"},
{"SubnetId": subnet_ids[1], "Ip": "10.0.0.20"},
],
)
err = exc.value.response["Error"]
assert err["Code"] == "ResourceExistsException"
assert (
f"Resolver endpoint with creator request ID '{request_id}' already exists"
) in err["Message"]
# Too many endpoints.
random_num = mock_random.get_random_hex(10)
for idx in range(4):
create_test_endpoint(client, ec2_client, name=f"A{idx}-{random_num}")
with pytest.raises(ClientError) as exc:
create_test_endpoint(client, ec2_client, name=f"A5-{random_num}")
err = exc.value.response["Error"]
assert err["Code"] == "LimitExceededException"
assert f"Account '{ACCOUNT_ID}' has exceeded 'max-endpoints" in err["Message"]
2024-01-07 12:03:33 +00:00
@mock_aws
def test_route53resolver_delete_resolver_endpoint():
"""Test good delete_resolver_endpoint API calls."""
client = boto3.client("route53resolver", region_name=TEST_REGION)
ec2_client = boto3.client("ec2", region_name=TEST_REGION)
created_endpoint = create_test_endpoint(client, ec2_client)
# Now delete the resolver endpoint and verify the response.
response = client.delete_resolver_endpoint(
ResolverEndpointId=created_endpoint["Id"]
)
endpoint = response["ResolverEndpoint"]
assert endpoint["CreatorRequestId"] == created_endpoint["CreatorRequestId"]
assert endpoint["Id"] == created_endpoint["Id"]
assert endpoint["Arn"] == created_endpoint["Arn"]
assert endpoint["Name"] == created_endpoint["Name"]
assert endpoint["SecurityGroupIds"] == created_endpoint["SecurityGroupIds"]
assert endpoint["Direction"] == created_endpoint["Direction"]
assert endpoint["IpAddressCount"] == created_endpoint["IpAddressCount"]
assert endpoint["HostVPCId"] == created_endpoint["HostVPCId"]
assert endpoint["Status"] == "DELETING"
assert "Deleting" in endpoint["StatusMessage"]
assert endpoint["CreationTime"] == created_endpoint["CreationTime"]
# Verify there are no endpoints or no network interfaces.
response = client.list_resolver_endpoints()
assert len(response["ResolverEndpoints"]) == 0
result = ec2_client.describe_network_interfaces()
assert len(result["NetworkInterfaces"]) == 0
2024-01-07 12:03:33 +00:00
@mock_aws
def test_route53resolver_bad_delete_resolver_endpoint():
"""Test delete_resolver_endpoint API calls with a bad ID."""
client = boto3.client("route53resolver", region_name=TEST_REGION)
ec2_client = boto3.client("ec2", region_name=TEST_REGION)
random_num = mock_random.get_random_hex(10)
# Use a resolver endpoint id that is too long.
long_id = "0123456789" * 6 + "xxxxx"
with pytest.raises(ClientError) as exc:
client.delete_resolver_endpoint(ResolverEndpointId=long_id)
err = exc.value.response["Error"]
assert err["Code"] == "ValidationException"
assert "1 validation error detected" in err["Message"]
assert (
f"Value '{long_id}' at 'resolverEndpointId' failed to satisfy "
f"constraint: Member must have length less than or equal to 64"
) in err["Message"]
# Delete a non-existent resolver endpoint.
with pytest.raises(ClientError) as exc:
client.delete_resolver_endpoint(ResolverEndpointId=random_num)
err = exc.value.response["Error"]
assert err["Code"] == "ResourceNotFoundException"
assert f"Resolver endpoint with ID '{random_num}' does not exist" in err["Message"]
# Create an endpoint and a rule referencing that endpoint. Verify the
# endpoint can't be deleted due to that rule.
endpoint = create_test_endpoint(client, ec2_client)
resolver_rule = client.create_resolver_rule(
CreatorRequestId=random_num,
RuleType="FORWARD",
DomainName=f"X{random_num}.com",
ResolverEndpointId=endpoint["Id"],
)
with pytest.raises(ClientError) as exc:
client.delete_resolver_endpoint(ResolverEndpointId=endpoint["Id"])
err = exc.value.response["Error"]
assert err["Code"] == "InvalidRequestException"
assert (
f"Cannot delete resolver endpoint unless its related resolver rules "
f"are deleted. The following rules still exist for this resolver "
f"endpoint: {resolver_rule['ResolverRule']['Id']}"
) in err["Message"]
2024-01-07 12:03:33 +00:00
@mock_aws
def test_route53resolver_get_resolver_endpoint():
"""Test good get_resolver_endpoint API calls."""
client = boto3.client("route53resolver", region_name=TEST_REGION)
ec2_client = boto3.client("ec2", region_name=TEST_REGION)
created_endpoint = create_test_endpoint(client, ec2_client)
# Now get the resolver endpoint and verify the response.
response = client.get_resolver_endpoint(ResolverEndpointId=created_endpoint["Id"])
endpoint = response["ResolverEndpoint"]
assert endpoint["CreatorRequestId"] == created_endpoint["CreatorRequestId"]
assert endpoint["Id"] == created_endpoint["Id"]
assert endpoint["Arn"] == created_endpoint["Arn"]
assert endpoint["Name"] == created_endpoint["Name"]
assert endpoint["SecurityGroupIds"] == created_endpoint["SecurityGroupIds"]
assert endpoint["Direction"] == created_endpoint["Direction"]
assert endpoint["IpAddressCount"] == created_endpoint["IpAddressCount"]
assert endpoint["HostVPCId"] == created_endpoint["HostVPCId"]
assert endpoint["Status"] == created_endpoint["Status"]
assert endpoint["StatusMessage"] == created_endpoint["StatusMessage"]
assert endpoint["CreationTime"] == created_endpoint["CreationTime"]
assert endpoint["ModificationTime"] == created_endpoint["ModificationTime"]
2024-01-07 12:03:33 +00:00
@mock_aws
def test_route53resolver_bad_get_resolver_endpoint():
"""Test get_resolver_endpoint API calls with a bad ID."""
client = boto3.client("route53resolver", region_name=TEST_REGION)
random_num = mock_random.get_random_hex(10)
# Use a resolver endpoint id that is too long.
long_id = "0123456789" * 6 + "xxxxx"
with pytest.raises(ClientError) as exc:
client.get_resolver_endpoint(ResolverEndpointId=long_id)
err = exc.value.response["Error"]
assert err["Code"] == "ValidationException"
assert "1 validation error detected" in err["Message"]
assert (
f"Value '{long_id}' at 'resolverEndpointId' failed to satisfy "
f"constraint: Member must have length less than or equal to 64"
) in err["Message"]
# Delete a non-existent resolver endpoint.
with pytest.raises(ClientError) as exc:
client.get_resolver_endpoint(ResolverEndpointId=random_num)
err = exc.value.response["Error"]
assert err["Code"] == "ResourceNotFoundException"
assert f"Resolver endpoint with ID '{random_num}' does not exist" in err["Message"]
2024-01-07 12:03:33 +00:00
@mock_aws
def test_route53resolver_update_resolver_endpoint():
"""Test good update_resolver_endpoint API calls."""
client = boto3.client("route53resolver", region_name=TEST_REGION)
ec2_client = boto3.client("ec2", region_name=TEST_REGION)
created_endpoint = create_test_endpoint(client, ec2_client)
# Now update the resolver endpoint name and verify the response.
new_name = "NewName" + mock_random.get_random_hex(6)
response = client.update_resolver_endpoint(
ResolverEndpointId=created_endpoint["Id"], Name=new_name
)
endpoint = response["ResolverEndpoint"]
assert endpoint["CreatorRequestId"] == created_endpoint["CreatorRequestId"]
assert endpoint["Id"] == created_endpoint["Id"]
assert endpoint["Arn"] == created_endpoint["Arn"]
assert endpoint["Name"] == new_name
assert endpoint["SecurityGroupIds"] == created_endpoint["SecurityGroupIds"]
assert endpoint["Direction"] == created_endpoint["Direction"]
assert endpoint["IpAddressCount"] == created_endpoint["IpAddressCount"]
assert endpoint["HostVPCId"] == created_endpoint["HostVPCId"]
assert endpoint["Status"] == created_endpoint["Status"]
assert endpoint["StatusMessage"] == created_endpoint["StatusMessage"]
assert endpoint["CreationTime"] == created_endpoint["CreationTime"]
assert endpoint["ModificationTime"] != created_endpoint["ModificationTime"]
2024-01-07 12:03:33 +00:00
@mock_aws
def test_route53resolver_bad_update_resolver_endpoint():
"""Test update_resolver_endpoint API calls with a bad ID."""
client = boto3.client("route53resolver", region_name=TEST_REGION)
random_num = mock_random.get_random_hex(10)
random_name = "Z" + mock_random.get_random_hex(10)
# Use a resolver endpoint id that is too long.
long_id = "0123456789" * 6 + "xxxxx"
with pytest.raises(ClientError) as exc:
client.update_resolver_endpoint(ResolverEndpointId=long_id, Name=random_name)
err = exc.value.response["Error"]
assert err["Code"] == "ValidationException"
assert "1 validation error detected" in err["Message"]
assert (
f"Value '{long_id}' at 'resolverEndpointId' failed to satisfy "
f"constraint: Member must have length less than or equal to 64"
) in err["Message"]
# Delete a non-existent resolver endpoint.
with pytest.raises(ClientError) as exc:
client.update_resolver_endpoint(ResolverEndpointId=random_num, Name=random_name)
err = exc.value.response["Error"]
assert err["Code"] == "ResourceNotFoundException"
assert f"Resolver endpoint with ID '{random_num}' does not exist" in err["Message"]
2024-01-07 12:03:33 +00:00
@mock_aws
def test_route53resolver_list_resolver_endpoint_ip_addresses():
"""Test good list_resolver_endpoint_ip_addresses API calls."""
client = boto3.client("route53resolver", region_name=TEST_REGION)
ec2_client = boto3.client("ec2", region_name=TEST_REGION)
random_num = mock_random.get_random_hex(10)
subnet_ids = create_subnets(ec2_client, create_vpc(ec2_client))
response = client.create_resolver_endpoint(
CreatorRequestId="B" + random_num,
Name="B" + random_num,
SecurityGroupIds=[create_security_group(ec2_client)],
Direction="INBOUND",
IpAddresses=[
{"SubnetId": subnet_ids[0], "Ip": "10.0.1.200"},
{"SubnetId": subnet_ids[1], "Ip": "10.0.0.20"},
{"SubnetId": subnet_ids[0], "Ip": "10.0.1.201"},
],
)
endpoint_id = response["ResolverEndpoint"]["Id"]
response = client.list_resolver_endpoint_ip_addresses(
ResolverEndpointId=endpoint_id
)
assert len(response["IpAddresses"]) == 3
assert response["MaxResults"] == 10
# Set max_results to return 1 address, use next_token to get remaining.
response = client.list_resolver_endpoint_ip_addresses(
ResolverEndpointId=endpoint_id, MaxResults=1
)
ip_addresses = response["IpAddresses"]
assert len(ip_addresses) == 1
assert response["MaxResults"] == 1
assert "NextToken" in response
assert ip_addresses[0]["IpId"].startswith("rni-")
assert ip_addresses[0]["SubnetId"] == subnet_ids[0]
assert ip_addresses[0]["Ip"] == "10.0.1.200"
assert ip_addresses[0]["Status"] == "ATTACHED"
assert ip_addresses[0]["StatusMessage"] == "This IP address is operational."
assert "CreationTime" in ip_addresses[0]
assert "ModificationTime" in ip_addresses[0]
response = client.list_resolver_endpoint_ip_addresses(
ResolverEndpointId=endpoint_id, NextToken=response["NextToken"]
)
assert len(response["IpAddresses"]) == 2
assert response["MaxResults"] == 10
assert "NextToken" not in response
2024-01-07 12:03:33 +00:00
@mock_aws
def test_route53resolver_bad_list_resolver_endpoint_ip_addresses():
"""Test bad list_resolver_endpoint_ip_addresses API calls."""
client = boto3.client("route53resolver", region_name=TEST_REGION)
ec2_client = boto3.client("ec2", region_name=TEST_REGION)
# Bad endpoint id.
with pytest.raises(ClientError) as exc:
client.list_resolver_endpoint_ip_addresses(ResolverEndpointId="foo")
err = exc.value.response["Error"]
assert err["Code"] == "ResourceNotFoundException"
assert "Resolver endpoint with ID 'foo' does not exist" in err["Message"]
# Good endpoint id, but bad max_results.
random_num = mock_random.get_random_hex(10)
response = create_test_endpoint(client, ec2_client, name=f"A-{random_num}")
with pytest.raises(ClientError) as exc:
client.list_resolver_endpoint_ip_addresses(
ResolverEndpointId=response["Id"], MaxResults=250
)
err = exc.value.response["Error"]
assert err["Code"] == "ValidationException"
assert "1 validation error detected" in err["Message"]
assert (
"Value '250' at 'maxResults' failed to satisfy constraint: Member "
"must have length less than or equal to 100"
) in err["Message"]
2024-01-07 12:03:33 +00:00
@mock_aws
def test_route53resolver_list_resolver_endpoints():
"""Test good list_resolver_endpoints API calls."""
client = boto3.client("route53resolver", region_name=TEST_REGION)
ec2_client = boto3.client("ec2", region_name=TEST_REGION)
random_num = mock_random.get_random_hex(10)
# List endpoints when there are none.
response = client.list_resolver_endpoints()
assert len(response["ResolverEndpoints"]) == 0
assert response["MaxResults"] == 10
assert "NextToken" not in response
# Create 5 endpoints, verify all 5 are listed when no filters, max_results.
for idx in range(4):
create_test_endpoint(client, ec2_client, name=f"A{idx}-{random_num}")
response = client.list_resolver_endpoints()
endpoints = response["ResolverEndpoints"]
assert len(endpoints) == 4
assert response["MaxResults"] == 10
for idx in range(4):
assert endpoints[idx]["Name"].startswith(f"A{idx}")
# Set max_results to return 1 endpoint, use next_token to get remaining 3.
response = client.list_resolver_endpoints(MaxResults=1)
endpoints = response["ResolverEndpoints"]
assert len(endpoints) == 1
assert response["MaxResults"] == 1
assert "NextToken" in response
assert endpoints[0]["Name"].startswith("A0")
response = client.list_resolver_endpoints(NextToken=response["NextToken"])
endpoints = response["ResolverEndpoints"]
assert len(endpoints) == 3
assert response["MaxResults"] == 10
assert "NextToken" not in response
for idx, endpoint in enumerate(endpoints):
assert endpoint["Name"].startswith(f"A{idx + 1}")
2024-01-07 12:03:33 +00:00
@mock_aws
def test_route53resolver_list_resolver_endpoints_filters():
"""Test good list_resolver_endpoints API calls that use filters."""
client = boto3.client("route53resolver", region_name=TEST_REGION)
ec2_client = boto3.client("ec2", region_name=TEST_REGION)
random_num = mock_random.get_random_hex(10)
# Create some endpoints for testing purposes
security_group_id = create_security_group(ec2_client)
vpc_id = create_vpc(ec2_client)
subnet_ids = create_subnets(ec2_client, vpc_id)
ip0_values = ["10.0.1.201", "10.0.1.202", "10.0.1.203", "10.0.1.204"]
ip1_values = ["10.0.0.21", "10.0.0.22", "10.0.0.23", "10.0.0.24"]
endpoints = []
for idx in range(1, 5):
ip_addrs = [
{"SubnetId": subnet_ids[0], "Ip": "10.0.1.200"},
{"SubnetId": subnet_ids[1], "Ip": "10.0.0.20"},
{"SubnetId": subnet_ids[0], "Ip": ip0_values[idx - 1]},
{"SubnetId": subnet_ids[1], "Ip": ip1_values[idx - 1]},
]
response = client.create_resolver_endpoint(
CreatorRequestId=f"F{idx}-{random_num}",
Name=f"F{idx}-{random_num}",
SecurityGroupIds=[security_group_id],
Direction="INBOUND" if idx % 2 else "OUTBOUND",
IpAddresses=ip_addrs,
)
endpoints.append(response["ResolverEndpoint"])
# Try all the valid filter names, including some of the old style names.
response = client.list_resolver_endpoints(
Filters=[{"Name": "CreatorRequestId", "Values": [f"F3-{random_num}"]}]
)
assert len(response["ResolverEndpoints"]) == 1
assert response["ResolverEndpoints"][0]["CreatorRequestId"] == f"F3-{random_num}"
response = client.list_resolver_endpoints(
Filters=[
{
"Name": "CREATOR_REQUEST_ID",
"Values": [f"F2-{random_num}", f"F4-{random_num}"],
}
]
)
assert len(response["ResolverEndpoints"]) == 2
assert response["ResolverEndpoints"][0]["CreatorRequestId"] == f"F2-{random_num}"
assert response["ResolverEndpoints"][1]["CreatorRequestId"] == f"F4-{random_num}"
response = client.list_resolver_endpoints(
Filters=[{"Name": "Direction", "Values": ["INBOUND"]}]
)
assert len(response["ResolverEndpoints"]) == 2
assert response["ResolverEndpoints"][0]["CreatorRequestId"] == f"F1-{random_num}"
assert response["ResolverEndpoints"][1]["CreatorRequestId"] == f"F3-{random_num}"
response = client.list_resolver_endpoints(
Filters=[{"Name": "HostVPCId", "Values": [vpc_id]}]
)
assert len(response["ResolverEndpoints"]) == 4
response = client.list_resolver_endpoints(
Filters=[{"Name": "IpAddressCount", "Values": ["4"]}]
)
assert len(response["ResolverEndpoints"]) == 4
response = client.list_resolver_endpoints(
Filters=[{"Name": "IpAddressCount", "Values": ["0", "7"]}]
)
assert len(response["ResolverEndpoints"]) == 0
response = client.list_resolver_endpoints(
Filters=[{"Name": "Name", "Values": [f"F1-{random_num}"]}]
)
assert len(response["ResolverEndpoints"]) == 1
assert response["ResolverEndpoints"][0]["Name"] == f"F1-{random_num}"
response = client.list_resolver_endpoints(
Filters=[
{"Name": "HOST_VPC_ID", "Values": [vpc_id]},
{"Name": "DIRECTION", "Values": ["INBOUND"]},
{"Name": "NAME", "Values": [f"F3-{random_num}"]},
]
)
assert len(response["ResolverEndpoints"]) == 1
assert response["ResolverEndpoints"][0]["Name"] == f"F3-{random_num}"
response = client.list_resolver_endpoints(
Filters=[{"Name": "SecurityGroupIds", "Values": [security_group_id]}]
)
assert len(response["ResolverEndpoints"]) == 4
response = client.list_resolver_endpoints(
Filters=[{"Name": "Status", "Values": ["OPERATIONAL"]}]
)
assert len(response["ResolverEndpoints"]) == 4
response = client.list_resolver_endpoints(
Filters=[{"Name": "Status", "Values": ["CREATING"]}]
)
assert len(response["ResolverEndpoints"]) == 0
2024-01-07 12:03:33 +00:00
@mock_aws
def test_route53resolver_bad_list_resolver_endpoints_filters():
"""Test bad list_resolver_endpoints API calls that use filters."""
client = boto3.client("route53resolver", region_name=TEST_REGION)
# botocore barfs on an empty "Values":
# TypeError: list_resolver_endpoints() only accepts keyword arguments.
# client.list_resolver_endpoints([{"Name": "Direction", "Values": []}])
# client.list_resolver_endpoints([{"Values": []}])
with pytest.raises(ClientError) as exc:
client.list_resolver_endpoints(Filters=[{"Name": "foo", "Values": ["bar"]}])
err = exc.value.response["Error"]
assert err["Code"] == "InvalidParameterException"
assert "The filter 'foo' is invalid" in err["Message"]
with pytest.raises(ClientError) as exc:
client.list_resolver_endpoints(
Filters=[{"Name": "HostVpcId", "Values": ["bar"]}]
)
err = exc.value.response["Error"]
assert err["Code"] == "InvalidParameterException"
assert "The filter 'HostVpcId' is invalid" in err["Message"]
2024-01-07 12:03:33 +00:00
@mock_aws
def test_route53resolver_bad_list_resolver_endpoints():
"""Test bad list_resolver_endpoints API calls."""
client = boto3.client("route53resolver", region_name=TEST_REGION)
ec2_client = boto3.client("ec2", region_name=TEST_REGION)
# Bad max_results.
random_num = mock_random.get_random_hex(10)
create_test_endpoint(client, ec2_client, name=f"A-{random_num}")
with pytest.raises(ClientError) as exc:
client.list_resolver_endpoints(MaxResults=250)
err = exc.value.response["Error"]
assert err["Code"] == "ValidationException"
assert "1 validation error detected" in err["Message"]
assert (
"Value '250' at 'maxResults' failed to satisfy constraint: Member "
"must have length less than or equal to 100"
) in err["Message"]
2022-06-25 21:39:58 +00:00
2024-01-07 12:03:33 +00:00
@mock_aws
2022-06-25 21:39:58 +00:00
def test_associate_resolver_endpoint_ip_address():
client = boto3.client("route53resolver", region_name=TEST_REGION)
ec2_client = boto3.client("ec2", region_name=TEST_REGION)
# create subnet
vpc_id = create_vpc(ec2_client)
subnet = ec2_client.create_subnet(
VpcId=vpc_id, CidrBlock="10.0.2.0/24", AvailabilityZone=f"{TEST_REGION}a"
)["Subnet"]
# create resolver
random_num = mock_random.get_random_hex(10)
2022-06-25 21:39:58 +00:00
resolver = create_test_endpoint(client, ec2_client, name=f"A-{random_num}")
assert resolver["IpAddressCount"] == 2
2022-06-25 21:39:58 +00:00
# associate
resp = client.associate_resolver_endpoint_ip_address(
IpAddress={"Ip": "10.0.2.126", "SubnetId": subnet["SubnetId"]},
ResolverEndpointId=resolver["Id"],
)["ResolverEndpoint"]
assert resp["Id"] == resolver["Id"]
assert resp["IpAddressCount"] == 3
assert len(resp["SecurityGroupIds"]) == 1
2022-06-25 21:39:58 +00:00
# verify ENI was created
enis = ec2_client.describe_network_interfaces()["NetworkInterfaces"]
ip_addresses = [eni["PrivateIpAddress"] for eni in enis]
assert "10.0.2.126" in ip_addresses
2022-06-25 21:39:58 +00:00
2024-01-07 12:03:33 +00:00
@mock_aws
2022-06-25 21:39:58 +00:00
def test_associate_resolver_endpoint_ip_address__invalid_resolver():
client = boto3.client("route53resolver", region_name=TEST_REGION)
with pytest.raises(ClientError) as exc:
client.associate_resolver_endpoint_ip_address(
IpAddress={"Ip": "notapplicable"}, ResolverEndpointId="unknown"
)
err = exc.value.response["Error"]
assert err["Code"] == "ResourceNotFoundException"
assert err["Message"] == "Resolver endpoint with ID 'unknown' does not exist"
2022-06-25 21:39:58 +00:00
2024-01-07 12:03:33 +00:00
@mock_aws
2022-06-25 21:39:58 +00:00
def test_disassociate_resolver_endpoint_ip_address__using_ip():
client = boto3.client("route53resolver", region_name=TEST_REGION)
ec2_client = boto3.client("ec2", region_name=TEST_REGION)
# create subnet
vpc_id = create_vpc(ec2_client)
subnet_id = ec2_client.create_subnet(
VpcId=vpc_id, CidrBlock="10.0.2.0/24", AvailabilityZone=f"{TEST_REGION}a"
)["Subnet"]["SubnetId"]
# create resolver
random_num = mock_random.get_random_hex(10)
2022-06-25 21:39:58 +00:00
resolver = create_test_endpoint(client, ec2_client, name=f"A-{random_num}")
# associate
client.associate_resolver_endpoint_ip_address(
IpAddress={"Ip": "10.0.2.126", "SubnetId": subnet_id},
ResolverEndpointId=resolver["Id"],
)
enis_before = ec2_client.describe_network_interfaces()["NetworkInterfaces"]
# disassociate
client.disassociate_resolver_endpoint_ip_address(
ResolverEndpointId=resolver["Id"],
IpAddress={"SubnetId": subnet_id, "Ip": "10.0.2.126"},
)
# One ENI was deleted
enis_after = ec2_client.describe_network_interfaces()["NetworkInterfaces"]
assert (len(enis_after) + 1) == len(enis_before)
2022-06-25 21:39:58 +00:00
2024-01-07 12:03:33 +00:00
@mock_aws
2022-06-25 21:39:58 +00:00
def test_disassociate_resolver_endpoint_ip_address__using_ipid_and_subnet():
client = boto3.client("route53resolver", region_name=TEST_REGION)
ec2_client = boto3.client("ec2", region_name=TEST_REGION)
# create subnet
vpc_id = create_vpc(ec2_client)
subnet_id = ec2_client.create_subnet(
VpcId=vpc_id, CidrBlock="10.0.2.0/24", AvailabilityZone=f"{TEST_REGION}a"
)["Subnet"]["SubnetId"]
# create resolver
random_num = mock_random.get_random_hex(10)
2022-06-25 21:39:58 +00:00
resolver = create_test_endpoint(client, ec2_client, name=f"A-{random_num}")
# associate
client.associate_resolver_endpoint_ip_address(
IpAddress={"Ip": "10.0.2.126", "SubnetId": subnet_id},
ResolverEndpointId=resolver["Id"],
)
ip_addresses = client.list_resolver_endpoint_ip_addresses(
ResolverEndpointId=resolver["Id"]
)["IpAddresses"]
ip_id = [ip["IpId"] for ip in ip_addresses if ip["Ip"] == "10.0.2.126"][0]
# disassociate
resp = client.disassociate_resolver_endpoint_ip_address(
ResolverEndpointId=resolver["Id"],
IpAddress={"SubnetId": subnet_id, "IpId": ip_id},
)["ResolverEndpoint"]
assert resp["IpAddressCount"] == 2
2022-06-25 21:39:58 +00:00
2024-01-07 12:03:33 +00:00
@mock_aws
2022-06-25 21:39:58 +00:00
def test_disassociate_resolver_endpoint_ip_address__using_subnet_alone():
client = boto3.client("route53resolver", region_name=TEST_REGION)
ec2_client = boto3.client("ec2", region_name=TEST_REGION)
# create subnet
vpc_id = create_vpc(ec2_client)
subnet_id = ec2_client.create_subnet(
VpcId=vpc_id, CidrBlock="10.0.2.0/24", AvailabilityZone=f"{TEST_REGION}a"
)["Subnet"]["SubnetId"]
# create resolver
random_num = mock_random.get_random_hex(10)
2022-06-25 21:39:58 +00:00
resolver = create_test_endpoint(client, ec2_client, name=f"A-{random_num}")
# associate
client.associate_resolver_endpoint_ip_address(
IpAddress={"Ip": "10.0.2.126", "SubnetId": subnet_id},
ResolverEndpointId=resolver["Id"],
)
# disassociate without specifying IP
with pytest.raises(ClientError) as exc:
client.disassociate_resolver_endpoint_ip_address(
ResolverEndpointId=resolver["Id"], IpAddress={"SubnetId": subnet_id}
)
err = exc.value.response["Error"]
assert err["Code"] == "InvalidRequestException"
assert (
err["Message"]
== "[RSLVR-00503] Need to specify either the IP ID or both subnet and IP address in order to remove IP address."
2022-06-25 21:39:58 +00:00
)