Skip to content

Commit cf5fd4f

Browse files
authored
Merge pull request #577 from linode/test_vpc
Integration tests for VPC Dual Stack(TPT-3639)
2 parents 8d042be + beaa902 commit cf5fd4f

File tree

5 files changed

+154
-29
lines changed

5 files changed

+154
-29
lines changed

test/integration/conftest.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -402,9 +402,10 @@ def create_vpc(test_linode_client):
402402
label = get_test_label(length=10)
403403

404404
vpc = client.vpcs.create(
405-
label,
406-
get_region(test_linode_client, {"VPCs"}),
405+
label=label,
406+
region=get_region(test_linode_client, {"VPCs"}),
407407
description="test description",
408+
ipv6=[{"range": "auto"}],
408409
)
409410
yield vpc
410411

@@ -413,7 +414,11 @@ def create_vpc(test_linode_client):
413414

414415
@pytest.fixture(scope="session")
415416
def create_vpc_with_subnet(test_linode_client, create_vpc):
416-
subnet = create_vpc.subnet_create("test-subnet", ipv4="10.0.0.0/24")
417+
subnet = create_vpc.subnet_create(
418+
label="test-subnet",
419+
ipv4="10.0.0.0/24",
420+
ipv6=[{"range": "auto"}],
421+
)
417422

418423
yield create_vpc, subnet
419424

test/integration/linode_client/test_linode_client.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,7 @@
66
import pytest
77

88
from linode_api4 import ApiError
9-
from linode_api4.objects import (
10-
ConfigInterface,
11-
ObjectStorageKeys,
12-
Region,
13-
)
9+
from linode_api4.objects import ConfigInterface, ObjectStorageKeys, Region
1410

1511

1612
@pytest.fixture(scope="session")

test/integration/models/linode/interfaces/test_interfaces.py

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
LinodeInterfacePublicIPv6RangeOptions,
1515
LinodeInterfacePublicOptions,
1616
LinodeInterfaceVLANOptions,
17+
LinodeInterfaceVPCIPv4AddressOptions,
1718
LinodeInterfaceVPCIPv4Options,
1819
LinodeInterfaceVPCIPv4RangeOptions,
1920
LinodeInterfaceVPCOptions,
@@ -69,6 +70,13 @@ def __assert_vpc(iface: LinodeInterface):
6970

7071
assert len(iface.vpc.ipv4.ranges) == 0
7172

73+
slaac_entry = iface.vpc.ipv6.slaac[0]
74+
assert ipaddress.ip_address(
75+
slaac_entry.address
76+
) in ipaddress.ip_network(slaac_entry.range)
77+
assert not iface.vpc.ipv6.is_public
78+
assert len(iface.vpc.ipv6.ranges) == 0
79+
7280
def __assert_vlan(iface: LinodeInterface):
7381
__assert_base(iface)
7482

@@ -145,19 +153,18 @@ def linode_interface_vpc(
145153
vpc=LinodeInterfaceVPCOptions(
146154
subnet_id=subnet.id,
147155
ipv4=LinodeInterfaceVPCIPv4Options(
148-
# TODO (Enhanced Interfaces): Not currently working as expected
149-
# addresses=[
150-
# LinodeInterfaceVPCIPv4AddressOptions(
151-
# address="auto",
152-
# primary=True,
153-
# nat_1_1_address="any",
154-
# )
155-
# ],
156+
addresses=[
157+
LinodeInterfaceVPCIPv4AddressOptions(
158+
address="auto",
159+
primary=True,
160+
nat_1_1_address=None,
161+
)
162+
],
156163
ranges=[
157164
LinodeInterfaceVPCIPv4RangeOptions(
158165
range="/29",
159166
)
160-
]
167+
],
161168
),
162169
),
163170
), instance, vpc, subnet
@@ -256,7 +263,7 @@ def test_linode_interface_create_vpc(linode_interface_vpc):
256263
assert iface.version
257264

258265
assert iface.default_route.ipv4
259-
assert not iface.default_route.ipv6
266+
assert iface.default_route.ipv6
260267

261268
assert iface.vpc.vpc_id == vpc.id
262269
assert iface.vpc.subnet_id == subnet.id
@@ -267,6 +274,16 @@ def test_linode_interface_create_vpc(linode_interface_vpc):
267274

268275
assert iface.vpc.ipv4.ranges[0].range.split("/")[1] == "29"
269276

277+
assert iface.default_route.ipv6
278+
ipv6 = iface.vpc.ipv6
279+
assert ipv6 and ipv6.is_public is False
280+
281+
if ipv6.slaac:
282+
assert ipv6.ranges == [] and len(ipv6.slaac) == 1
283+
assert ipv6.slaac[0].range and ipv6.slaac[0].address
284+
elif ipv6.ranges:
285+
assert ipv6.slaac == [] and len(ipv6.ranges) > 0
286+
270287

271288
def test_linode_interface_update_vpc(linode_interface_vpc):
272289
iface, instance, vpc, subnet = linode_interface_vpc

test/integration/models/linode/test_linode.py

Lines changed: 77 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import ipaddress
12
import time
23
from test.integration.conftest import get_region
34
from test.integration.helpers import (
@@ -651,7 +652,7 @@ def __assert_public(iface: LinodeInterface):
651652
__assert_base(iface)
652653

653654
assert not iface.default_route.ipv4
654-
assert iface.default_route.ipv6
655+
assert not iface.default_route.ipv6
655656

656657
assert len(iface.public.ipv4.addresses) == 0
657658
assert len(iface.public.ipv4.shared) == 0
@@ -666,7 +667,7 @@ def __assert_vpc(iface: LinodeInterface):
666667
__assert_base(iface)
667668

668669
assert iface.default_route.ipv4
669-
assert not iface.default_route.ipv6
670+
assert iface.default_route.ipv6
670671

671672
assert iface.vpc.vpc_id == vpc.id
672673
assert iface.vpc.subnet_id == subnet.id
@@ -679,6 +680,14 @@ def __assert_vpc(iface: LinodeInterface):
679680
assert len(iface.vpc.ipv4.ranges) == 1
680681
assert iface.vpc.ipv4.ranges[0].range == "10.0.0.5/32"
681682

683+
assert len(iface.vpc.ipv6.slaac) == 1
684+
685+
ipaddress.IPv6Network(iface.vpc.ipv6.slaac[0].range)
686+
ipaddress.IPv6Address(iface.vpc.ipv6.slaac[0].address)
687+
688+
assert len(iface.vpc.ipv6.ranges) == 0
689+
assert iface.vpc.ipv6.is_public is False
690+
682691
def __assert_vlan(iface: LinodeInterface):
683692
__assert_base(iface)
684693

@@ -888,9 +897,12 @@ def test_create_vpc(
888897
test_linode_client,
889898
linode_and_vpc_for_legacy_interface_tests_offline,
890899
):
891-
vpc, subnet, linode, _ = (
892-
linode_and_vpc_for_legacy_interface_tests_offline
893-
)
900+
(
901+
vpc,
902+
subnet,
903+
linode,
904+
_,
905+
) = linode_and_vpc_for_legacy_interface_tests_offline
894906

895907
config: Config = linode.configs[0]
896908

@@ -927,11 +939,30 @@ def test_create_vpc(
927939
assert vpc_range_ip.address_range == "10.0.0.5/32"
928940
assert not vpc_range_ip.active
929941

942+
assert isinstance(vpc.ipv6, list)
943+
assert len(vpc.ipv6) > 0
944+
assert isinstance(vpc.ipv6[0].range, str)
945+
assert ":" in vpc.ipv6[0].range
946+
930947
# TODO:: Add `VPCIPAddress.filters.linode_id == linode.id` filter back
931948

932949
# Attempt to resolve the IP from /vpcs/ips
933950
all_vpc_ips = test_linode_client.vpcs.ips()
934-
assert all_vpc_ips[0].dict == vpc_ip.dict
951+
matched_ip = next(
952+
(
953+
ip
954+
for ip in all_vpc_ips
955+
if ip.address == vpc_ip.address
956+
and ip.vpc_id == vpc_ip.vpc_id
957+
and ip.linode_id == vpc_ip.linode_id
958+
),
959+
None,
960+
)
961+
962+
assert (
963+
matched_ip is not None
964+
), f"Expected VPC IP {vpc_ip.address} not found in /vpcs/ips"
965+
assert matched_ip.dict == vpc_ip.dict
935966

936967
# Test getting the ips under this specific VPC
937968
vpc_ips = vpc.ips
@@ -941,13 +972,50 @@ def test_create_vpc(
941972
assert vpc_ips[0].linode_id == linode.id
942973
assert vpc_ips[0].nat_1_1 == linode.ips.ipv4.public[0].address
943974

975+
# Validate VPC IPv6 IPs from /vpcs/ips
976+
all_vpc_ipv6 = test_linode_client.get("/vpcs/ipv6s")["data"]
977+
978+
# Find matching VPC IPv6 entry
979+
matched_ipv6 = next(
980+
(
981+
ip
982+
for ip in all_vpc_ipv6
983+
if ip["vpc_id"] == vpc.id
984+
and ip["linode_id"] == linode.id
985+
and ip["interface_id"] == interface.id
986+
and ip["subnet_id"] == subnet.id
987+
),
988+
None,
989+
)
990+
991+
assert (
992+
matched_ipv6
993+
), f"No VPC IPv6 found for Linode {linode.id} in VPC {vpc.id}"
994+
995+
assert matched_ipv6["ipv6_range"].count(":") >= 2
996+
assert not matched_ipv6["ipv6_is_public"]
997+
998+
ipv6_addresses = matched_ipv6.get("ipv6_addresses", [])
999+
assert (
1000+
isinstance(ipv6_addresses, list) and ipv6_addresses
1001+
), "No IPv6 addresses found"
1002+
1003+
slaac = ipv6_addresses[0]
1004+
assert (
1005+
isinstance(slaac.get("slaac_address"), str)
1006+
and ":" in slaac["slaac_address"]
1007+
)
1008+
9441009
def test_update_vpc(
9451010
self,
9461011
linode_and_vpc_for_legacy_interface_tests_offline,
9471012
):
948-
vpc, subnet, linode, _ = (
949-
linode_and_vpc_for_legacy_interface_tests_offline
950-
)
1013+
(
1014+
vpc,
1015+
subnet,
1016+
linode,
1017+
_,
1018+
) = linode_and_vpc_for_legacy_interface_tests_offline
9511019

9521020
config: Config = linode.configs[0]
9531021

test/integration/models/vpc/test_vpc.py

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ def test_get_vpc(test_linode_client, create_vpc):
1010
vpc = test_linode_client.load(VPC, create_vpc.id)
1111
test_linode_client.vpcs()
1212
assert vpc.id == create_vpc.id
13+
assert isinstance(vpc.ipv6[0].range, str)
1314

1415

1516
@pytest.mark.smoke
@@ -31,7 +32,11 @@ def test_update_vpc(test_linode_client, create_vpc):
3132
def test_get_subnet(test_linode_client, create_vpc_with_subnet):
3233
vpc, subnet = create_vpc_with_subnet
3334
loaded_subnet = test_linode_client.load(VPCSubnet, subnet.id, vpc.id)
34-
35+
assert loaded_subnet.ipv4 == subnet.ipv4
36+
assert loaded_subnet.ipv6 is not None
37+
assert loaded_subnet.ipv6[0].range.startswith(
38+
vpc.ipv6[0].range.split("::")[0]
39+
)
3540
assert loaded_subnet.id == subnet.id
3641

3742

@@ -88,7 +93,9 @@ def test_fails_create_subnet_invalid_data(create_vpc):
8893
create_vpc.subnet_create("test-subnet", ipv4=invalid_ipv4)
8994

9095
assert excinfo.value.status == 400
91-
assert "ipv4 must be an IPv4 network" in str(excinfo.value.json)
96+
error_msg = str(excinfo.value.json)
97+
98+
assert "Must be an IPv4 network" in error_msg
9299

93100

94101
def test_fails_update_subnet_invalid_data(create_vpc_with_subnet):
@@ -101,3 +108,35 @@ def test_fails_update_subnet_invalid_data(create_vpc_with_subnet):
101108

102109
assert excinfo.value.status == 400
103110
assert "Label must include only ASCII" in str(excinfo.value.json)
111+
112+
113+
def test_fails_create_subnet_with_invalid_ipv6_range(create_vpc):
114+
valid_ipv4 = "10.0.0.0/24"
115+
invalid_ipv6 = [{"range": "2600:3c11:e5b9::/5a"}]
116+
117+
with pytest.raises(ApiError) as excinfo:
118+
create_vpc.subnet_create(
119+
label="bad-ipv6-subnet",
120+
ipv4=valid_ipv4,
121+
ipv6=invalid_ipv6,
122+
)
123+
124+
assert excinfo.value.status == 400
125+
error = excinfo.value.json["errors"]
126+
127+
assert any(
128+
e["field"] == "ipv6[0].range"
129+
and "Must be an IPv6 network" in e["reason"]
130+
for e in error
131+
)
132+
133+
134+
def test_get_vpc_ipv6s(test_linode_client):
135+
ipv6s = test_linode_client.get("/vpcs/ipv6s")["data"]
136+
137+
assert isinstance(ipv6s, list)
138+
139+
for ipv6 in ipv6s:
140+
assert "vpc_id" in ipv6
141+
assert isinstance(ipv6["ipv6_range"], str)
142+
assert isinstance(ipv6["ipv6_addresses"], list)

0 commit comments

Comments
 (0)