Skip to content

Commit 6c4292d

Browse files
authored
TPT-4206: Integration tests for NB Front-End IP & 40Gbps (#664)
* Create integration tests for NodeBalancer Front-End IP & 40Gbps * Update TODO message * Linter fixes * Copilot remarks refactor * Remove redundant variables (PR remarks) * Modify tests to filter out invalid regions for premium NBs * Modify test names to indicate that test is expected to fail * Add DevCloud region to PREMIUM_REGIONS * Linter fix * Add note about no DevCloud region in PREMIUM_40GB_REGIONS list
1 parent 6d9a8bc commit 6c4292d

File tree

2 files changed

+320
-4
lines changed

2 files changed

+320
-4
lines changed

test/integration/conftest.py

Lines changed: 84 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
wait_for_condition,
1010
)
1111
from test.integration.models.database.helpers import get_db_engine_id
12-
from typing import Optional, Set
12+
from typing import List, Optional, Set
1313

1414
import pytest
1515
import requests
@@ -112,9 +112,18 @@ def get_regions(
112112

113113

114114
def get_region(
115-
client: LinodeClient, capabilities: Set[str] = None, site_type: str = "core"
115+
client: LinodeClient,
116+
capabilities: Set[str] = None,
117+
site_type: str = "core",
118+
valid_regions: List[str] = None,
116119
):
117-
return random.choice(get_regions(client, capabilities, site_type))
120+
regions = get_regions(client, capabilities, site_type)
121+
122+
# To filter out regions that cannot be used for the Linode resource
123+
if valid_regions:
124+
regions = [reg for reg in regions if reg.id in valid_regions]
125+
126+
return random.choice(regions)
118127

119128

120129
def get_api_ca_file():
@@ -457,7 +466,14 @@ def create_vpc(test_linode_client):
457466
vpc = client.vpcs.create(
458467
label=label,
459468
region=get_region(
460-
test_linode_client, {"VPCs", "VPC IPv6 Stack", "Linode Interfaces"}
469+
test_linode_client,
470+
{
471+
"VPCs",
472+
"VPC IPv6 Stack",
473+
"VPC Dual Stack",
474+
"Linode Interfaces",
475+
"NodeBalancers",
476+
},
461477
),
462478
description="test description",
463479
ipv6=[{"range": "auto"}],
@@ -467,6 +483,24 @@ def create_vpc(test_linode_client):
467483
vpc.delete()
468484

469485

486+
@pytest.fixture(scope="session")
487+
def create_vpc_ipv4(test_linode_client):
488+
client = test_linode_client
489+
490+
label = get_test_label(length=10) + "-ipv4"
491+
492+
vpc = client.vpcs.create(
493+
label=label,
494+
region=get_region(
495+
test_linode_client, {"VPCs", "Linode Interfaces", "NodeBalancers"}
496+
),
497+
description="test description",
498+
)
499+
yield vpc
500+
501+
vpc.delete()
502+
503+
470504
@pytest.fixture(scope="session")
471505
def create_vpc_with_subnet(test_linode_client, create_vpc):
472506
subnet = create_vpc.subnet_create(
@@ -480,6 +514,52 @@ def create_vpc_with_subnet(test_linode_client, create_vpc):
480514
subnet.delete()
481515

482516

517+
@pytest.fixture(scope="session")
518+
def create_vpc_with_subnet_ipv4(test_linode_client, create_vpc_ipv4):
519+
subnet = create_vpc_ipv4.subnet_create(
520+
label="test-subnet", ipv4="10.0.0.0/24"
521+
)
522+
523+
yield create_vpc_ipv4, subnet
524+
525+
subnet.delete()
526+
527+
528+
@pytest.fixture(scope="function")
529+
def create_vpc_with_subnet_in_premium_region(request, test_linode_client):
530+
premium_regions = getattr(request, "param")
531+
client = test_linode_client
532+
label = get_test_label(length=10)
533+
534+
vpc = client.vpcs.create(
535+
label=label,
536+
region=get_region(
537+
client,
538+
{
539+
"VPCs",
540+
"VPC IPv6 Stack",
541+
"VPC Dual Stack",
542+
"Linode Interfaces",
543+
"NodeBalancers",
544+
},
545+
valid_regions=premium_regions,
546+
),
547+
description="test description",
548+
ipv6=[{"range": "auto"}],
549+
)
550+
551+
subnet = vpc.subnet_create(
552+
label="test-subnet",
553+
ipv4="10.0.0.0/24",
554+
ipv6=[{"range": "auto"}],
555+
)
556+
557+
yield vpc, subnet
558+
559+
subnet.delete()
560+
vpc.delete()
561+
562+
483563
@pytest.fixture(scope="session")
484564
def create_vpc_with_subnet_and_linode(
485565
test_linode_client, create_vpc_with_subnet, e2e_test_firewall

test/integration/models/nodebalancer/test_nodebalancer.py

Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import ipaddress
12
import re
23
from test.integration.conftest import (
34
get_api_ca_file,
@@ -17,6 +18,29 @@
1718
RegionPrice,
1819
)
1920

21+
# Lists of valid regions where NodeBalancers of type "premium" or "premium_40gb" can be created
22+
PREMIUM_REGIONS = [
23+
"nl-ams",
24+
"jp-tyo-3",
25+
"sg-sin-2",
26+
"de-fra-2",
27+
"in-bom-2",
28+
"gb-lon",
29+
"us-lax",
30+
"id-cgk",
31+
"us-mia",
32+
"it-mil",
33+
"jp-osa",
34+
"in-maa",
35+
"se-sto",
36+
"br-gru",
37+
"us-sea",
38+
"fr-par",
39+
"us-iad",
40+
"pl-labkrk-2", # DevCloud
41+
]
42+
PREMIUM_40GB_REGIONS = ["us-iad"] # No DevCloud region for premium_40gb type
43+
2044
TEST_REGION = get_region(
2145
LinodeClient(
2246
token=get_token(),
@@ -272,3 +296,215 @@ def test_nodebalancer_types(test_linode_client):
272296
isinstance(region_price.monthly, (float, int))
273297
and region_price.monthly >= 0
274298
)
299+
300+
301+
def test_nb_with_backend_only(test_linode_client, create_vpc_with_subnet):
302+
client = test_linode_client
303+
label = get_test_label(8)
304+
305+
nb = client.nodebalancer_create(
306+
region=create_vpc_with_subnet[0].region,
307+
label=label,
308+
vpcs=[
309+
{
310+
"vpc_id": create_vpc_with_subnet[0].id,
311+
"subnet_id": create_vpc_with_subnet[1].id,
312+
}
313+
],
314+
)
315+
316+
assert isinstance(
317+
ipaddress.ip_address(nb.ipv4.address), ipaddress.IPv4Address
318+
)
319+
assert isinstance(ipaddress.ip_address(nb.ipv6), ipaddress.IPv6Address)
320+
assert nb.frontend_address_type == "public"
321+
assert nb.frontend_vpc_subnet_id is None
322+
323+
nb_get = NodeBalancer(client, nb.id)
324+
nb_vpcs = nb_get.vpcs()
325+
326+
assert len(nb_vpcs) == 1
327+
assert nb_vpcs[0].purpose == "backend"
328+
329+
nb_vpc = nb_get.vpc(nb_vpcs[0].id)
330+
331+
assert nb_vpc.purpose == "backend"
332+
333+
# TODO: Uncomment when API implementation of /backend_vpcs and /frontend_vpcs endpoints is finished
334+
# nb_backend_vpcs = nb_get.backend_vpcs()
335+
# assert len(nb_backend_vpcs) == 1
336+
# assert nb_backend_vpcs[0].purpose == 'backend'
337+
#
338+
# nb_frontend_vpcs = nb_get.frontend_vpcs()
339+
# assert len(nb_frontend_vpcs) == 0
340+
341+
nb.delete()
342+
343+
344+
def test_nb_with_frontend_ipv4_only_in_single_stack_vpc(
345+
test_linode_client, create_vpc_with_subnet_ipv4
346+
):
347+
client = test_linode_client
348+
subnet = create_vpc_with_subnet_ipv4[1].id
349+
label = get_test_label(8)
350+
ipv4_address = "10.0.0.2" # first available address
351+
352+
nb = client.nodebalancer_create(
353+
region=create_vpc_with_subnet_ipv4[0].region,
354+
label=label,
355+
frontend_vpcs=[{"subnet_id": subnet, "ipv4_range": "10.0.0.0/24"}],
356+
type="premium",
357+
)
358+
assert nb.ipv4.address == ipv4_address
359+
assert nb.ipv6 is None
360+
assert nb.frontend_address_type == "vpc"
361+
assert nb.frontend_vpc_subnet_id == subnet
362+
363+
# TODO: Uncomment when API implementation of /backend_vpcs and /frontend_vpcs endpoints is finished
364+
# nb_frontend_vpcs = nb_get.frontend_vpcs()
365+
# assert len(nb_frontend_vpcs) == 1
366+
# assert nb_frontend_vpcs[0].purpose == 'frontend'
367+
#
368+
# nb_backend_vpcs = nb_get.backend_vpcs()
369+
# assert len(nb_backend_vpcs) == 0
370+
371+
nb.delete()
372+
373+
374+
def test_nb_with_frontend_ipv6_in_single_stack_vpc_fail(
375+
test_linode_client, create_vpc_with_subnet_ipv4
376+
):
377+
client = test_linode_client
378+
label = get_test_label(8)
379+
380+
with pytest.raises(ApiError) as excinfo:
381+
client.nodebalancer_create(
382+
region=create_vpc_with_subnet_ipv4[0].region,
383+
label=label,
384+
frontend_vpcs=[
385+
{
386+
"subnet_id": create_vpc_with_subnet_ipv4[1].id,
387+
"ipv6_range": "/62",
388+
}
389+
],
390+
type="premium",
391+
)
392+
393+
error_msg = str(excinfo.value.json)
394+
assert excinfo.value.status == 400
395+
assert "No IPv6 subnets available in VPC" in error_msg
396+
397+
398+
def test_nb_with_frontend_and_default_type_fail(
399+
test_linode_client, create_vpc_with_subnet
400+
):
401+
client = test_linode_client
402+
label = get_test_label(8)
403+
404+
with pytest.raises(ApiError) as excinfo:
405+
client.nodebalancer_create(
406+
region=create_vpc_with_subnet[0].region,
407+
label=label,
408+
frontend_vpcs=[{"subnet_id": create_vpc_with_subnet[1].id}],
409+
)
410+
411+
error_msg = str(excinfo.value.json)
412+
assert excinfo.value.status == 400
413+
assert "NodeBalancer with frontend VPC IP must be premium" in error_msg
414+
415+
416+
@pytest.mark.parametrize(
417+
"create_vpc_with_subnet_in_premium_region",
418+
[PREMIUM_40GB_REGIONS],
419+
indirect=True,
420+
)
421+
def test_nb_with_premium40gb_type(
422+
test_linode_client, create_vpc_with_subnet_in_premium_region
423+
):
424+
client = test_linode_client
425+
426+
nb = client.nodebalancer_create(
427+
region=create_vpc_with_subnet_in_premium_region[0].region,
428+
label=get_test_label(length=8),
429+
type="premium_40gb",
430+
)
431+
assert nb.type == "premium_40gb"
432+
433+
nb_get = test_linode_client.load(
434+
NodeBalancer,
435+
nb.id,
436+
)
437+
assert nb_get.type == "premium_40gb"
438+
439+
nb.delete()
440+
441+
442+
@pytest.mark.parametrize(
443+
"create_vpc_with_subnet_in_premium_region", [PREMIUM_REGIONS], indirect=True
444+
)
445+
def test_nb_with_frontend_and_backend_in_different_vpcs(
446+
test_linode_client, create_vpc_with_subnet_in_premium_region
447+
):
448+
client = test_linode_client
449+
region = create_vpc_with_subnet_in_premium_region[0].region
450+
vpc_backend = create_vpc_with_subnet_in_premium_region[0].id
451+
subnet_backend = create_vpc_with_subnet_in_premium_region[1].id
452+
label = get_test_label(8)
453+
ipv4_range = "10.0.0.0/24"
454+
ipv4_address = "10.0.0.2" # first available address
455+
456+
vpc_frontend = client.vpcs.create(
457+
label=get_test_label(length=10),
458+
region=region,
459+
description="test description",
460+
ipv6=[{"range": "auto"}],
461+
)
462+
463+
subnet_frontend = vpc_frontend.subnet_create(
464+
label="test-subnet",
465+
ipv4=ipv4_range,
466+
ipv6=[{"range": "auto"}],
467+
)
468+
ipv6_range = subnet_frontend.ipv6[0].range
469+
ipv6_address = ipv6_range.split("::")[0] + ":1::1"
470+
471+
nb = client.nodebalancer_create(
472+
region=region,
473+
label=label,
474+
vpcs=[{"vpc_id": vpc_backend, "subnet_id": subnet_backend}],
475+
frontend_vpcs=[
476+
{
477+
"subnet_id": subnet_frontend.id,
478+
"ipv4_range": ipv4_range,
479+
"ipv6_range": ipv6_range,
480+
}
481+
],
482+
type="premium",
483+
)
484+
485+
assert nb.ipv4.address == ipv4_address
486+
assert nb.ipv6 == ipv6_address
487+
assert nb.frontend_address_type == "vpc"
488+
assert nb.frontend_vpc_subnet_id == subnet_frontend.id
489+
490+
nb_get = NodeBalancer(client, nb.id)
491+
nb_vpcs = nb_get.vpcs()
492+
nb_vpcs.sort(key=lambda x: x.purpose)
493+
494+
assert len(nb_vpcs) == 2
495+
assert nb_vpcs[0].purpose == "backend"
496+
assert nb_vpcs[1].ipv4_range == f"{ipv4_address}/32"
497+
assert nb_vpcs[1].ipv6_range == f"{ipv6_address[:-1]}/64"
498+
assert nb_vpcs[1].purpose == "frontend"
499+
500+
# TODO: Uncomment when API implementation of /backend_vpcs and /frontend_vpcs endpoints is finished
501+
# nb_backend_vpcs = nb_get.backend_vpcs()
502+
# assert len(nb_backend_vpcs) == 1
503+
# assert nb_backend_vpcs[0].purpose == 'backend'
504+
#
505+
# nb_frontend_vpcs = nb_get.frontend_vpcs()
506+
# assert len(nb_frontend_vpcs) == 1
507+
# assert nb_frontend_vpcs[0].purpose == 'frontend'
508+
509+
nb.delete()
510+
vpc_frontend.delete()

0 commit comments

Comments
 (0)