Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions linode_api4/groups/nodebalancer.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,27 @@ def create(self, region, **kwargs):
:param ipv4: A reserved IPv4 address to assign to this NodeBalancer.
NOTE: Reserved IP feature may not currently be available to all users.
:type ipv4: str
:param type: The NodeBalancer type. Supported values include
``common``, ``basic``, ``premium``, ``premium_40g``,
and ``enterprise``. This cannot be changed after creation.
NOTE: Creating premium or enterprise NodeBalancers may not
currently be available to all users.
:type type: str
:param backend_connectivity: How this NodeBalancer communicates with
backends (``legacy``, ``ipv6``, or ``vpc``). If omitted,
the API infers a value from ``vpcs`` or config nodes, or
returns ``undefined`` until the first node is added.
``undefined`` cannot be sent by clients. This cannot be
changed after creation.
NOTE: This field may not currently be available to all users.
:type backend_connectivity: str
:param vpcs: VPC attachments for this NodeBalancer. Required when
``backend_connectivity`` is ``vpc``.
:type vpcs: list[dict]
:param configs: NodeBalancer configs and optional nodes to create
with this NodeBalancer. Node addresses must match the
selected or inferred backend connectivity.
:type configs: list[dict]

:returns: The new NodeBalancer
:rtype: NodeBalancer
Expand Down
8 changes: 6 additions & 2 deletions linode_api4/objects/nodebalancer.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,10 @@ def node_create(self, label, address, **kwargs):

API documentation: https://techdocs.akamai.com/linode-api/reference/post-node-balancer-node

:param address: The private IP Address where this backend can be reached.
This must be a private IP address.
:param address: The address where this backend can be reached. This may
be a private IPv4 address, a public IPv6 address in
``[IPv6]:port`` format, or a VPC address. The address
type must match this NodeBalancer's ``backend_connectivity``.
Comment on lines +165 to +168
:type address: str

:param label: The label for this node. This is for display purposes only.
Expand Down Expand Up @@ -255,6 +257,8 @@ class NodeBalancer(Base):
"tags": Property(mutable=True, unordered=True),
"client_udp_sess_throttle": Property(mutable=True),
"locks": Property(unordered=True),
"type": Property(),
"backend_connectivity": Property(),
}

# create derived objects
Expand Down
2 changes: 2 additions & 0 deletions test/fixtures/linode_instances_123_nodebalancers.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
"ipv4": "203.0.113.1",
"ipv6": null,
"label": "balancer12345",
"type": "premium",
"backend_connectivity": "ipv6",
"region": "us-east",
"tags": [
"example tag",
Expand Down
8 changes: 6 additions & 2 deletions test/fixtures/nodebalancers.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
"label": "balancer123456",
"client_conn_throttle": 0,
"tags": ["something"],
"locks": ["cannot_delete_with_subresources"]
"locks": ["cannot_delete_with_subresources"],
"type": "premium",
"backend_connectivity": "ipv6"
},
{
"created": "2018-01-01T00:01:01",
Expand All @@ -24,7 +26,9 @@
"label": "balancer123457",
"client_conn_throttle": 0,
"tags": [],
"locks": []
"locks": [],
"type": "premium",
"backend_connectivity": "ipv6"
}
],
"results": 2,
Expand Down
6 changes: 4 additions & 2 deletions test/fixtures/nodebalancers_123456.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,7 @@
],
"locks": [
"cannot_delete_with_subresources"
]
}
],
"type": "premium",
"backend_connectivity": "ipv6"
}
4 changes: 4 additions & 0 deletions test/integration/models/nodebalancer/test_nodebalancer.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,15 @@ def test_create_nb(test_linode_client, e2e_test_firewall):
label=label,
firewall=e2e_test_firewall.id,
client_udp_sess_throttle=5,
type="premium",
backend_connectivity="ipv6",
)
Comment on lines 108 to 111

assert TEST_REGION, nb.region
assert label == nb.label
assert 5 == nb.client_udp_sess_throttle
assert nb.type == "premium"
assert nb.backend_connectivity == "ipv6"

nb.delete()

Expand Down
72 changes: 72 additions & 0 deletions test/unit/linode_client_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -1589,6 +1589,78 @@ def test_nodebalancer_types(self):
self.assertEqual(types[0].region_prices[0].hourly, 0.018)
self.assertEqual(types[0].region_prices[0].monthly, 12)

def test_create_with_type_and_backend_connectivity(self):
"""
Tests that creating a NodeBalancer forwards type, backend_connectivity,
vpcs, and config node addresses.
"""
with self.mock_post(
{
"id": 1234,
"label": "my-premium-nb",
"type": "premium",
"backend_connectivity": "ipv6",
"region": "us-east",
}
) as m:
nb = self.client.nodebalancers.create(
"us-east",
label="my-premium-nb",
type="premium",
backend_connectivity="ipv6",
configs=[
{
"port": 80,
"nodes": [
{
"address": "[2001:db8:abcd:0012::1]:80",
"label": "node1",
}
],
}
],
)

self.assertEqual(m.call_url, "/nodebalancers")
self.assertEqual(m.call_data["region"], "us-east")
self.assertEqual(m.call_data["type"], "premium")
self.assertEqual(m.call_data["backend_connectivity"], "ipv6")
self.assertEqual(
m.call_data["configs"][0]["nodes"][0]["address"],
"[2001:db8:abcd:0012::1]:80",
)
self.assertEqual(nb.id, 1234)
self.assertEqual(nb.type, "premium")
self.assertEqual(nb.backend_connectivity, "ipv6")

def test_create_with_vpc_backend_connectivity(self):
"""
Tests that creating a NodeBalancer forwards vpc backend connectivity.
"""
with self.mock_post(
{
"id": 1234,
"label": "my-nb",
"type": "common",
"backend_connectivity": "vpc",
"region": "us-east",
}
) as m:
nb = self.client.nodebalancers.create(
"us-east",
label="my-nb",
backend_connectivity="vpc",
vpcs=[{"subnet_id": 123456, "ipv4_range": "10.0.250.4/30"}],
)

self.assertEqual(m.call_url, "/nodebalancers")
self.assertEqual(m.call_data["backend_connectivity"], "vpc")
self.assertEqual(
m.call_data["vpcs"],
[{"subnet_id": 123456, "ipv4_range": "10.0.250.4/30"}],
)
self.assertEqual(nb.backend_connectivity, "vpc")


class VolumeGroupTest(ClientBaseCase):
"""
Expand Down
77 changes: 77 additions & 0 deletions test/unit/objects/nodebalancers_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,29 @@ def test_create_node(self):
},
)

def test_create_ipv6_node(self):
"""
Tests that a node can be created with a public IPv6 backend address.
"""
with self.mock_post(
"nodebalancers/123456/configs/65432/nodes/54321"
) as m:
config = NodeBalancerConfig(self.client, 65432, 123456)
node = config.node_create(
"node54321",
"[2001:db8:abcd:0012::1]:80",
weight=50,
mode="accept",
)

self.assertIsNotNone(node)
self.assertEqual(
m.call_url, "/nodebalancers/123456/configs/65432/nodes"
)
self.assertEqual(
m.call_data["address"], "[2001:db8:abcd:0012::1]:80"
)

def test_update_node(self):
"""
Tests that a node can be updated
Expand Down Expand Up @@ -154,6 +177,18 @@ def test_delete_node(self):


class NodeBalancerTest(ClientBaseCase):
def test_get(self):
"""
Tests that a NodeBalancer is loaded correctly by ID.
"""
nb = NodeBalancer(self.client, 123456)
self.assertEqual(nb._populated, False)

self.assertEqual(nb.label, "balancer123456")
self.assertEqual(nb._populated, True)
self.assertEqual(nb.type, "premium")
self.assertEqual(nb.backend_connectivity, "ipv6")

def test_update(self):
"""
Test that you can update a NodeBalancer.
Expand Down Expand Up @@ -193,6 +228,24 @@ def test_locks_not_in_put(self):
self.assertNotIn("locks", m.call_data)
self.assertEqual(m.call_data["label"], "new-label")

def test_type_and_backend_connectivity_not_in_put(self):
"""
Test that type and backend_connectivity are not included in PUT
requests. These fields cannot be changed after creation.
"""
nb = NodeBalancer(self.client, 123456)
self.assertEqual(nb.type, "premium")
self.assertEqual(nb.backend_connectivity, "ipv6")

nb.label = "new-label"

with self.mock_put("nodebalancers/123456") as m:
nb.save()
self.assertEqual(m.call_url, "/nodebalancers/123456")
self.assertNotIn("type", m.call_data)
self.assertNotIn("backend_connectivity", m.call_data)
self.assertEqual(m.call_data["label"], "new-label")

def test_firewalls(self):
"""
Test that you can get the firewalls for the requested NodeBalancer.
Expand Down Expand Up @@ -251,6 +304,30 @@ def test_config_rebuild(self):
},
)

def test_config_rebuild_ipv6(self):
"""
Test that a config can be rebuilt with public IPv6 backend addresses.
"""
config_rebuild_url = "/nodebalancers/12345/configs/4567/rebuild"
with self.mock_post(config_rebuild_url) as m:
nb = NodeBalancer(self.client, 12345)
nodes = [
{
"address": "[2001:db8:abcd:0012::1]:80",
"label": "node1",
"weight": 50,
"mode": "accept",
}
]

result = nb.config_rebuild(4567, nodes, port=80, protocol="http")
self.assertIsNotNone(result)
self.assertEqual(m.call_url, config_rebuild_url)
self.assertEqual(
m.call_data["nodes"][0]["address"],
"[2001:db8:abcd:0012::1]:80",
)

def test_statistics(self):
"""
Test that you can get the statistics about the requested NodeBalancer.
Expand Down