Skip to content
12 changes: 12 additions & 0 deletions linode_api4/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,15 @@ class RegionPrice(JSONObject):
id: int = 0
hourly: int = 0
monthly: int = 0


@dataclass
class LKECluster(JSONObject):
"""
LKECluster contains the core fields of a lke_cluster object returned by various node balancer endpoints.
"""

id: int = 0
label: str = ""
type: str = ""
url: str = ""
105 changes: 104 additions & 1 deletion linode_api4/objects/nodebalancer.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from pathlib import Path
from urllib import parse

from linode_api4.common import Price, RegionPrice
from linode_api4.common import LKECluster, Price, RegionPrice
from linode_api4.errors import UnexpectedResponseError
from linode_api4.objects.base import Base, MappedObject, Property
from linode_api4.objects.dbase import DerivedBase
Expand Down Expand Up @@ -231,6 +231,28 @@ def load_ssl_data(self, cert_file, key_file):
self.ssl_key = f.read()


class NodeBalancerVPCConfig(DerivedBase):
"""
The VPC configuration for this NodeBalancer.

API documentation: https://techdocs.akamai.com/linode-api/reference/get-node-balancer-vpc-config
"""

api_endpoint = "/nodebalancers/{nodebalancer_id}/vpcs/{id}"
derived_url_path = "vpcs"
parent_id_name = "nodebalancer_id"

properties = {
"id": Property(identifier=True),
"nodebalancer_id": Property(identifier=True),
"ipv4_range": Property(),
"ipv6_range": Property(),
"subnet_id": Property(),
"vpc_id": Property(),
"purpose": Property(),
}


class NodeBalancer(Base):
"""
A single NodeBalancer you can access.
Expand All @@ -255,6 +277,10 @@ class NodeBalancer(Base):
"tags": Property(mutable=True, unordered=True),
"client_udp_sess_throttle": Property(mutable=True),
"locks": Property(unordered=True),
"type": Property(),
"lke_cluster": Property(json_object=LKECluster),
"frontend_address_type": Property(),
"frontend_vpc_subnet_id": Property(),
}

# create derived objects
Expand Down Expand Up @@ -358,3 +384,80 @@ def firewalls(self):
Firewall(self._client, firewall["id"])
for firewall in result["data"]
]

def vpcs(self):
"""
View VPC information for VPCs associated with this NodeBalancer.

API Documentation: https://techdocs.akamai.com/linode-api/reference/get-node-balancer-vpcs

:returns: A List of NodeBalancerVPCConfig of the Linode NodeBalancer.
:rtype: List[NodeBalancerVPCConfig]
"""
result = self._client.get(
"{}/vpcs".format(NodeBalancer.api_endpoint), model=self
)

return [
NodeBalancerVPCConfig(self._client, vpc["id"], self.id, json=vpc)
for vpc in result["data"]
]

def vpc(self, id):
"""
View VPC information for a VPC associated with this NodeBalancer.

API Documentation: https://techdocs.akamai.com/linode-api/reference/get-node-balancer-vpc-config

:param id: The ID of the NodeBalancer VPC Config to view.
:type id: int

:returns: A NodeBalancerVPCConfig of the Linode NodeBalancer.
:rtype: NodeBalancerVPCConfig
"""
result = self._client.get(
"{}/vpcs/{}".format(
NodeBalancer.api_endpoint, parse.quote(str(id))
),
model=self,
)

return NodeBalancerVPCConfig(
self._client, result["id"], self.id, json=result
)

def backend_vpcs(self):
"""
View VPC information for backend VPCs associated with this NodeBalancer.

API Documentation: TODO

:returns: A List of NodeBalancerVPCConfig of the Linode NodeBalancer.
:rtype: List[NodeBalancerVPCConfig]
"""
result = self._client.get(
"{}/backend_vpcs".format(NodeBalancer.api_endpoint), model=self
)

return [
NodeBalancerVPCConfig(self._client, vpc["id"], self.id, json=vpc)
for vpc in result["data"]
]

def frontend_vpcs(self):
"""
View VPC information for frontend VPCs associated with this NodeBalancer.

API Documentation: TODO
Comment on lines +433 to +451

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be done when official techdoc is released


:returns: A List of NodeBalancerVPCConfig of the Linode NodeBalancer.
:rtype: List[NodeBalancerVPCConfig]
"""
result = self._client.get(
"{}/frontend_vpcs".format(NodeBalancer.api_endpoint), model=self
)

return [
NodeBalancerVPCConfig(self._client, vpc["id"], self.id, json=vpc)
for vpc in result["data"]
]
19 changes: 16 additions & 3 deletions test/fixtures/nodebalancers.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,16 @@
"label": "balancer123456",
"client_conn_throttle": 0,
"tags": ["something"],
"locks": ["cannot_delete_with_subresources"]
"locks": ["cannot_delete_with_subresources"],
"type": "common",
"lke_cluster": {
"id": 1234,
"type": "lkecluster",
"label": "test-cluster",
"url": "/v4/lke/clusters/1234"
},
"frontend_address_type": "vpc",
"frontend_vpc_subnet_id": 5555
},
{
"created": "2018-01-01T00:01:01",
Expand All @@ -24,10 +33,14 @@
"label": "balancer123457",
"client_conn_throttle": 0,
"tags": [],
"locks": []
"locks": [],
"type": "premium_40gb",
"lke_cluster": null,
"frontend_address_type": "vpc",
"frontend_vpc_subnet_id": 6666
}
],
"results": 2,
"page": 1,
"pages": 1
}
}
11 changes: 10 additions & 1 deletion test/fixtures/nodebalancers_123456.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,14 @@
],
"locks": [
"cannot_delete_with_subresources"
]
],
"type": "common",
"lke_cluster": {
"id": 1234,
"type": "lkecluster",
"label": "test-cluster",
"url": "/v4/lke/clusters/1234"
},
"frontend_address_type": "vpc",
"frontend_vpc_subnet_id": 5555
}
16 changes: 16 additions & 0 deletions test/fixtures/nodebalancers_12345_backend__vpcs.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"data": [
{
"id": 101,
"nodebalancer_id": 12345,
"subnet_id": 6666,
"vpc_id": 222,
"ipv4_range": "10.200.1.0/24",
"ipv6_range": "2001:db8:2::/64",
"purpose": "backend"
}
],
"page": 1,
"pages": 1,
"results": 1
}
16 changes: 16 additions & 0 deletions test/fixtures/nodebalancers_12345_frontend__vpcs.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"data": [
{
"id": 99,
"nodebalancer_id": 12345,
"subnet_id": 5555,
"vpc_id": 111,
"ipv4_range": "10.100.5.0/24",
"ipv6_range": "2001:db8::/64",
"purpose": "frontend"
}
],
"page": 1,
"pages": 1,
"results": 1
}
25 changes: 25 additions & 0 deletions test/fixtures/nodebalancers_12345_vpcs.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"data": [
{
"id": 99,
"nodebalancer_id": 12345,
"subnet_id": 5555,
"vpc_id": 111,
"ipv4_range": "10.100.5.0/24",
"ipv6_range": "2001:db8::/64",
"purpose": "frontend"
},
{
"id": 100,
"nodebalancer_id": 12345,
"subnet_id": 5556,
"vpc_id": 112,
"ipv4_range": "10.100.6.0/24",
"ipv6_range": "2001:db8:1::/64",
"purpose": "backend"
}
],
"page": 1,
"pages": 1,
"results": 2
}
9 changes: 9 additions & 0 deletions test/fixtures/nodebalancers_12345_vpcs_99.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"id": 99,
"nodebalancer_id": 12345,
"subnet_id": 5555,
"vpc_id": 111,
"ipv4_range": "10.100.5.0/24",
"ipv6_range": "2001:db8::/64",
"purpose": "frontend"
}
88 changes: 84 additions & 4 deletions test/integration/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
wait_for_condition,
)
from test.integration.models.database.helpers import get_db_engine_id
from typing import Optional, Set
from typing import List, Optional, Set

import pytest
import requests
Expand Down Expand Up @@ -111,9 +111,18 @@ def get_regions(


def get_region(
client: LinodeClient, capabilities: Set[str] = None, site_type: str = "core"
client: LinodeClient,
capabilities: Set[str] = None,
site_type: str = "core",
valid_regions: List[str] = None,
):
return random.choice(get_regions(client, capabilities, site_type))
regions = get_regions(client, capabilities, site_type)

# To filter out regions that cannot be used for the Linode resource
if valid_regions:
regions = [reg for reg in regions if reg.id in valid_regions]

return random.choice(regions)
Comment thread
mawilk90 marked this conversation as resolved.


def get_api_ca_file():
Expand Down Expand Up @@ -459,7 +468,14 @@ def create_vpc(test_linode_client):
vpc = client.vpcs.create(
label=label,
region=get_region(
test_linode_client, {"VPCs", "VPC IPv6 Stack", "Linode Interfaces"}
test_linode_client,
{
"VPCs",
"VPC IPv6 Stack",
"VPC Dual Stack",
"Linode Interfaces",
"NodeBalancers",
},
),
description="test description",
ipv6=[{"range": "auto"}],
Expand All @@ -469,6 +485,24 @@ def create_vpc(test_linode_client):
vpc.delete()


@pytest.fixture(scope="session")
def create_vpc_ipv4(test_linode_client):
client = test_linode_client

label = get_test_label(length=10) + "-ipv4"

vpc = client.vpcs.create(
label=label,
region=get_region(
test_linode_client, {"VPCs", "Linode Interfaces", "NodeBalancers"}
),
description="test description",
)
yield vpc

vpc.delete()


@pytest.fixture(scope="session")
def create_vpc_with_subnet(test_linode_client, create_vpc):
subnet = create_vpc.subnet_create(
Expand All @@ -482,6 +516,52 @@ def create_vpc_with_subnet(test_linode_client, create_vpc):
subnet.delete()


@pytest.fixture(scope="session")
def create_vpc_with_subnet_ipv4(test_linode_client, create_vpc_ipv4):
subnet = create_vpc_ipv4.subnet_create(
label="test-subnet", ipv4="10.0.0.0/24"
)

yield create_vpc_ipv4, subnet

subnet.delete()


@pytest.fixture(scope="function")
def create_vpc_with_subnet_in_premium_region(request, test_linode_client):
premium_regions = getattr(request, "param")
client = test_linode_client
label = get_test_label(length=10)

vpc = client.vpcs.create(
label=label,
region=get_region(
client,
{
"VPCs",
"VPC IPv6 Stack",
"VPC Dual Stack",
"Linode Interfaces",
"NodeBalancers",
},
valid_regions=premium_regions,
),
description="test description",
ipv6=[{"range": "auto"}],
)

subnet = vpc.subnet_create(
label="test-subnet",
ipv4="10.0.0.0/24",
ipv6=[{"range": "auto"}],
)

yield vpc, subnet

subnet.delete()
vpc.delete()


@pytest.fixture(scope="session")
def create_vpc_with_subnet_and_linode(
test_linode_client, create_vpc_with_subnet, e2e_test_firewall
Expand Down
Loading