diff --git a/IMPLEMENTATION_COVERAGE.md b/IMPLEMENTATION_COVERAGE.md
index c026ef60..d8e02f59 100644
--- a/IMPLEMENTATION_COVERAGE.md
+++ b/IMPLEMENTATION_COVERAGE.md
@@ -2217,8 +2217,8 @@
- [X] describe_volumes
- [ ] describe_volumes_modifications
- [X] describe_vpc_attribute
-- [ ] describe_vpc_classic_link
-- [ ] describe_vpc_classic_link_dns_support
+- [X] describe_vpc_classic_link
+- [X] describe_vpc_classic_link_dns_support
- [ ] describe_vpc_endpoint_connection_notifications
- [ ] describe_vpc_endpoint_connections
- [ ] describe_vpc_endpoint_service_configurations
@@ -2237,8 +2237,8 @@
- [ ] disable_ebs_encryption_by_default
- [ ] disable_transit_gateway_route_table_propagation
- [ ] disable_vgw_route_propagation
-- [ ] disable_vpc_classic_link
-- [ ] disable_vpc_classic_link_dns_support
+- [X] disable_vpc_classic_link
+- [X] disable_vpc_classic_link_dns_support
- [X] disassociate_address
- [ ] disassociate_client_vpn_target_network
- [ ] disassociate_iam_instance_profile
@@ -2250,8 +2250,8 @@
- [ ] enable_transit_gateway_route_table_propagation
- [ ] enable_vgw_route_propagation
- [ ] enable_volume_io
-- [ ] enable_vpc_classic_link
-- [ ] enable_vpc_classic_link_dns_support
+- [X] enable_vpc_classic_link
+- [X] enable_vpc_classic_link_dns_support
- [ ] export_client_vpn_client_certificate_revocation_list
- [ ] export_client_vpn_client_configuration
- [ ] export_image
diff --git a/moto/ec2/models.py b/moto/ec2/models.py
index ccc7c7a3..afb23dc8 100644
--- a/moto/ec2/models.py
+++ b/moto/ec2/models.py
@@ -2444,6 +2444,7 @@ class VPC(TaggedEC2Resource):
self.instance_tenancy = instance_tenancy
self.is_default = "true" if is_default else "false"
self.enable_dns_support = "true"
+ self.classic_link_enabled = "false"
# This attribute is set to 'true' only for default VPCs
# or VPCs created using the wizard of the VPC console
self.enable_dns_hostnames = "true" if is_default else "false"
@@ -2540,6 +2541,32 @@ class VPC(TaggedEC2Resource):
self.cidr_block_association_set[association_id] = association_set
return association_set
+ def enable_vpc_classic_link(self):
+ # Check if current cidr block doesn't fall within the 10.0.0.0/8 block, excluding 10.0.0.0/16 and 10.1.0.0/16.
+ # Doesn't check any route tables, maybe something for in the future?
+ # See https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/vpc-classiclink.html#classiclink-limitations
+ network_address = ipaddress.ip_network(self.cidr_block).network_address
+ if (
+ network_address not in ipaddress.ip_network("10.0.0.0/8")
+ or network_address in ipaddress.ip_network("10.0.0.0/16")
+ or network_address in ipaddress.ip_network("10.1.0.0/16")
+ ):
+ self.classic_link_enabled = "true"
+
+ return self.classic_link_enabled
+
+ def disable_vpc_classic_link(self):
+ self.classic_link_enabled = "false"
+ return self.classic_link_enabled
+
+ def enable_vpc_classic_link_dns_support(self):
+ self.classic_link_dns_supported = "true"
+ return self.classic_link_dns_supported
+
+ def disable_vpc_classic_link_dns_support(self):
+ self.classic_link_dns_supported = "false"
+ return self.classic_link_dns_supported
+
def disassociate_vpc_cidr_block(self, association_id):
if self.cidr_block == self.cidr_block_association_set.get(
association_id, {}
@@ -2670,6 +2697,22 @@ class VPCBackend(object):
else:
raise InvalidParameterValueError(attr_name)
+ def enable_vpc_classic_link(self, vpc_id):
+ vpc = self.get_vpc(vpc_id)
+ return vpc.enable_vpc_classic_link()
+
+ def disable_vpc_classic_link(self, vpc_id):
+ vpc = self.get_vpc(vpc_id)
+ return vpc.disable_vpc_classic_link()
+
+ def enable_vpc_classic_link_dns_support(self, vpc_id):
+ vpc = self.get_vpc(vpc_id)
+ return vpc.enable_vpc_classic_link_dns_support()
+
+ def disable_vpc_classic_link_dns_support(self, vpc_id):
+ vpc = self.get_vpc(vpc_id)
+ return vpc.disable_vpc_classic_link_dns_support()
+
def modify_vpc_attribute(self, vpc_id, attr_name, attr_value):
vpc = self.get_vpc(vpc_id)
if attr_name in ("enable_dns_support", "enable_dns_hostnames"):
diff --git a/moto/ec2/responses/vpcs.py b/moto/ec2/responses/vpcs.py
index 1773e4cc..0fd19837 100644
--- a/moto/ec2/responses/vpcs.py
+++ b/moto/ec2/responses/vpcs.py
@@ -5,6 +5,13 @@ from moto.ec2.utils import filters_from_querystring
class VPCs(BaseResponse):
+ def _get_doc_date(self):
+ return (
+ "2013-10-15"
+ if "Boto/" in self.headers.get("user-agent", "")
+ else "2016-11-15"
+ )
+
def create_vpc(self):
cidr_block = self._get_param("CidrBlock")
instance_tenancy = self._get_param("InstanceTenancy", if_none="default")
@@ -16,11 +23,7 @@ class VPCs(BaseResponse):
instance_tenancy,
amazon_provided_ipv6_cidr_block=amazon_provided_ipv6_cidr_blocks,
)
- doc_date = (
- "2013-10-15"
- if "Boto/" in self.headers.get("user-agent", "")
- else "2016-11-15"
- )
+ doc_date = self._get_doc_date()
template = self.response_template(CREATE_VPC_RESPONSE)
return template.render(vpc=vpc, doc_date=doc_date)
@@ -50,6 +53,64 @@ class VPCs(BaseResponse):
template = self.response_template(DESCRIBE_VPC_ATTRIBUTE_RESPONSE)
return template.render(vpc_id=vpc_id, attribute=attribute, value=value)
+ def describe_vpc_classic_link_dns_support(self):
+ vpc_ids = self._get_multi_param("VpcIds")
+ filters = filters_from_querystring(self.querystring)
+ vpcs = self.ec2_backend.get_all_vpcs(vpc_ids=vpc_ids, filters=filters)
+ doc_date = self._get_doc_date()
+ template = self.response_template(
+ DESCRIBE_VPC_CLASSIC_LINK_DNS_SUPPORT_RESPONSE
+ )
+ return template.render(vpcs=vpcs, doc_date=doc_date)
+
+ def enable_vpc_classic_link_dns_support(self):
+ vpc_id = self._get_param("VpcId")
+ classic_link_dns_supported = self.ec2_backend.enable_vpc_classic_link_dns_support(
+ vpc_id=vpc_id
+ )
+ doc_date = self._get_doc_date()
+ template = self.response_template(ENABLE_VPC_CLASSIC_LINK_DNS_SUPPORT_RESPONSE)
+ return template.render(
+ classic_link_dns_supported=classic_link_dns_supported, doc_date=doc_date
+ )
+
+ def disable_vpc_classic_link_dns_support(self):
+ vpc_id = self._get_param("VpcId")
+ classic_link_dns_supported = self.ec2_backend.disable_vpc_classic_link_dns_support(
+ vpc_id=vpc_id
+ )
+ doc_date = self._get_doc_date()
+ template = self.response_template(DISABLE_VPC_CLASSIC_LINK_DNS_SUPPORT_RESPONSE)
+ return template.render(
+ classic_link_dns_supported=classic_link_dns_supported, doc_date=doc_date
+ )
+
+ def describe_vpc_classic_link(self):
+ vpc_ids = self._get_multi_param("VpcId")
+ filters = filters_from_querystring(self.querystring)
+ vpcs = self.ec2_backend.get_all_vpcs(vpc_ids=vpc_ids, filters=filters)
+ doc_date = self._get_doc_date()
+ template = self.response_template(DESCRIBE_VPC_CLASSIC_LINK_RESPONSE)
+ return template.render(vpcs=vpcs, doc_date=doc_date)
+
+ def enable_vpc_classic_link(self):
+ vpc_id = self._get_param("VpcId")
+ classic_link_enabled = self.ec2_backend.enable_vpc_classic_link(vpc_id=vpc_id)
+ doc_date = self._get_doc_date()
+ template = self.response_template(ENABLE_VPC_CLASSIC_LINK_RESPONSE)
+ return template.render(
+ classic_link_enabled=classic_link_enabled, doc_date=doc_date
+ )
+
+ def disable_vpc_classic_link(self):
+ vpc_id = self._get_param("VpcId")
+ classic_link_enabled = self.ec2_backend.disable_vpc_classic_link(vpc_id=vpc_id)
+ doc_date = self._get_doc_date()
+ template = self.response_template(DISABLE_VPC_CLASSIC_LINK_RESPONSE)
+ return template.render(
+ classic_link_enabled=classic_link_enabled, doc_date=doc_date
+ )
+
def modify_vpc_attribute(self):
vpc_id = self._get_param("VpcId")
@@ -149,6 +210,56 @@ CREATE_VPC_RESPONSE = """
"""
+DESCRIBE_VPC_CLASSIC_LINK_DNS_SUPPORT_RESPONSE = """
+
+ 7a62c442-3484-4f42-9342-6942EXAMPLE
+
+ {% for vpc in vpcs %}
+ -
+ {{ vpc.id }}
+ {{ vpc.classic_link_dns_supported }}
+
+ {% endfor %}
+
+"""
+
+ENABLE_VPC_CLASSIC_LINK_DNS_SUPPORT_RESPONSE = """
+
+ 7a62c442-3484-4f42-9342-6942EXAMPLE
+ {{ classic_link_dns_supported }}
+"""
+
+DISABLE_VPC_CLASSIC_LINK_DNS_SUPPORT_RESPONSE = """
+
+ 7a62c442-3484-4f42-9342-6942EXAMPLE
+ {{ classic_link_dns_supported }}
+"""
+
+DESCRIBE_VPC_CLASSIC_LINK_RESPONSE = """
+
+ 7a62c442-3484-4f42-9342-6942EXAMPLE
+
+ {% for vpc in vpcs %}
+ -
+ {{ vpc.id }}
+ {{ vpc.classic_link_enabled }}
+
+ {% endfor %}
+
+"""
+
+ENABLE_VPC_CLASSIC_LINK_RESPONSE = """
+
+ 7a62c442-3484-4f42-9342-6942EXAMPLE
+ {{ classic_link_enabled }}
+"""
+
+DISABLE_VPC_CLASSIC_LINK_RESPONSE = """
+
+ 7a62c442-3484-4f42-9342-6942EXAMPLE
+ {{ classic_link_enabled }}
+"""
+
DESCRIBE_VPCS_RESPONSE = """
7a62c442-3484-4f42-9342-6942EXAMPLE
diff --git a/tests/test_ec2/test_vpcs.py b/tests/test_ec2/test_vpcs.py
index 0894a8b8..1bc3ddd9 100644
--- a/tests/test_ec2/test_vpcs.py
+++ b/tests/test_ec2/test_vpcs.py
@@ -678,3 +678,150 @@ def test_create_vpc_with_invalid_cidr_range():
"An error occurred (InvalidVpc.Range) when calling the CreateVpc "
"operation: The CIDR '{}' is invalid.".format(vpc_cidr_block)
)
+
+
+@mock_ec2
+def test_enable_vpc_classic_link():
+ ec2 = boto3.resource("ec2", region_name="us-west-1")
+
+ # Create VPC
+ vpc = ec2.create_vpc(CidrBlock="10.1.0.0/16")
+
+ response = ec2.meta.client.enable_vpc_classic_link(VpcId=vpc.id)
+ assert response.get("Return").should.be.true
+
+
+@mock_ec2
+def test_enable_vpc_classic_link_failure():
+ ec2 = boto3.resource("ec2", region_name="us-west-1")
+
+ # Create VPC
+ vpc = ec2.create_vpc(CidrBlock="10.90.0.0/16")
+
+ response = ec2.meta.client.enable_vpc_classic_link(VpcId=vpc.id)
+ assert response.get("Return").should.be.false
+
+
+@mock_ec2
+def test_disable_vpc_classic_link():
+ ec2 = boto3.resource("ec2", region_name="us-west-1")
+
+ # Create VPC
+ vpc = ec2.create_vpc(CidrBlock="10.0.0.0/16")
+
+ ec2.meta.client.enable_vpc_classic_link(VpcId=vpc.id)
+ response = ec2.meta.client.disable_vpc_classic_link(VpcId=vpc.id)
+ assert response.get("Return").should.be.false
+
+
+@mock_ec2
+def test_describe_classic_link_enabled():
+ ec2 = boto3.resource("ec2", region_name="us-west-1")
+
+ # Create VPC
+ vpc = ec2.create_vpc(CidrBlock="10.0.0.0/16")
+
+ ec2.meta.client.enable_vpc_classic_link(VpcId=vpc.id)
+ response = ec2.meta.client.describe_vpc_classic_link(VpcIds=[vpc.id])
+ assert response.get("Vpcs")[0].get("ClassicLinkEnabled").should.be.true
+
+
+@mock_ec2
+def test_describe_classic_link_disabled():
+ ec2 = boto3.resource("ec2", region_name="us-west-1")
+
+ # Create VPC
+ vpc = ec2.create_vpc(CidrBlock="10.90.0.0/16")
+
+ response = ec2.meta.client.describe_vpc_classic_link(VpcIds=[vpc.id])
+ assert response.get("Vpcs")[0].get("ClassicLinkEnabled").should.be.false
+
+
+@mock_ec2
+def test_describe_classic_link_multiple():
+ ec2 = boto3.resource("ec2", region_name="us-west-1")
+
+ # Create VPC
+ vpc1 = ec2.create_vpc(CidrBlock="10.90.0.0/16")
+ vpc2 = ec2.create_vpc(CidrBlock="10.0.0.0/16")
+
+ ec2.meta.client.enable_vpc_classic_link(VpcId=vpc2.id)
+ response = ec2.meta.client.describe_vpc_classic_link(VpcIds=[vpc1.id, vpc2.id])
+ expected = [
+ {"VpcId": vpc1.id, "ClassicLinkDnsSupported": False},
+ {"VpcId": vpc2.id, "ClassicLinkDnsSupported": True},
+ ]
+
+ # Ensure response is sorted, because they can come in random order
+ assert response.get("Vpcs").sort(key=lambda x: x["VpcId"]) == expected.sort(
+ key=lambda x: x["VpcId"]
+ )
+
+
+@mock_ec2
+def test_enable_vpc_classic_link_dns_support():
+ ec2 = boto3.resource("ec2", region_name="us-west-1")
+
+ # Create VPC
+ vpc = ec2.create_vpc(CidrBlock="10.1.0.0/16")
+
+ response = ec2.meta.client.enable_vpc_classic_link_dns_support(VpcId=vpc.id)
+ assert response.get("Return").should.be.true
+
+
+@mock_ec2
+def test_disable_vpc_classic_link_dns_support():
+ ec2 = boto3.resource("ec2", region_name="us-west-1")
+
+ # Create VPC
+ vpc = ec2.create_vpc(CidrBlock="10.0.0.0/16")
+
+ ec2.meta.client.enable_vpc_classic_link_dns_support(VpcId=vpc.id)
+ response = ec2.meta.client.disable_vpc_classic_link_dns_support(VpcId=vpc.id)
+ assert response.get("Return").should.be.false
+
+
+@mock_ec2
+def test_describe_classic_link_dns_support_enabled():
+ ec2 = boto3.resource("ec2", region_name="us-west-1")
+
+ # Create VPC
+ vpc = ec2.create_vpc(CidrBlock="10.0.0.0/16")
+
+ ec2.meta.client.enable_vpc_classic_link_dns_support(VpcId=vpc.id)
+ response = ec2.meta.client.describe_vpc_classic_link_dns_support(VpcIds=[vpc.id])
+ assert response.get("Vpcs")[0].get("ClassicLinkDnsSupported").should.be.true
+
+
+@mock_ec2
+def test_describe_classic_link_dns_support_disabled():
+ ec2 = boto3.resource("ec2", region_name="us-west-1")
+
+ # Create VPC
+ vpc = ec2.create_vpc(CidrBlock="10.90.0.0/16")
+
+ response = ec2.meta.client.describe_vpc_classic_link_dns_support(VpcIds=[vpc.id])
+ assert response.get("Vpcs")[0].get("ClassicLinkDnsSupported").should.be.false
+
+
+@mock_ec2
+def test_describe_classic_link_dns_support_multiple():
+ ec2 = boto3.resource("ec2", region_name="us-west-1")
+
+ # Create VPC
+ vpc1 = ec2.create_vpc(CidrBlock="10.90.0.0/16")
+ vpc2 = ec2.create_vpc(CidrBlock="10.0.0.0/16")
+
+ ec2.meta.client.enable_vpc_classic_link_dns_support(VpcId=vpc2.id)
+ response = ec2.meta.client.describe_vpc_classic_link_dns_support(
+ VpcIds=[vpc1.id, vpc2.id]
+ )
+ expected = [
+ {"VpcId": vpc1.id, "ClassicLinkDnsSupported": False},
+ {"VpcId": vpc2.id, "ClassicLinkDnsSupported": True},
+ ]
+
+ # Ensure response is sorted, because they can come in random order
+ assert response.get("Vpcs").sort(key=lambda x: x["VpcId"]) == expected.sort(
+ key=lambda x: x["VpcId"]
+ )