diff --git a/linode_api4/objects/nodebalancer.py b/linode_api4/objects/nodebalancer.py index 840d5b965..ef1aee951 100644 --- a/linode_api4/objects/nodebalancer.py +++ b/linode_api4/objects/nodebalancer.py @@ -97,6 +97,8 @@ class NodeBalancerConfig(DerivedBase): "check_path": Property(mutable=True), "check_body": Property(mutable=True), "check_passive": Property(mutable=True), + "udp_check_port": Property(mutable=True), + "udp_session_timeout": Property(), "ssl_cert": Property(mutable=True), "ssl_key": Property(mutable=True), "ssl_commonname": Property(), @@ -106,6 +108,39 @@ class NodeBalancerConfig(DerivedBase): "proxy_protocol": Property(mutable=True), } + def save(self, force=True) -> bool: + """ + Send this NodeBalancerConfig's mutable values to the server in a PUT request. + :param force: If true, this method will always send a PUT request regardless of + whether the field has been explicitly updated. For optimization + purposes, this field should be set to false for typical update + operations. (Defaults to True) + :type force: bool + """ + + if not force and not self._changed: + return False + + data = self._serialize() + + print(data) + + if data.get("protocol") == "udp" and "cipher_suite" in data: + data.pop("cipher_suite") + + print(data) + + result = self._client.put( + NodeBalancerConfig.api_endpoint, model=self, data=data + ) + + if "error" in result: + return False + + self._populate(result) + + return True + @property def nodes(self): """ @@ -233,6 +268,7 @@ class NodeBalancer(Base): "configs": Property(derived_class=NodeBalancerConfig), "transfer": Property(), "tags": Property(mutable=True, unordered=True), + "client_udp_sess_throttle": Property(mutable=True), } # create derived objects diff --git a/test/fixtures/nodebalancers_123456_configs.json b/test/fixtures/nodebalancers_123456_configs.json index f12f1345f..cab9fb981 100644 --- a/test/fixtures/nodebalancers_123456_configs.json +++ b/test/fixtures/nodebalancers_123456_configs.json @@ -24,9 +24,35 @@ "protocol": "http", "ssl_fingerprint": "", "proxy_protocol": "none" + }, + { + "check": "connection", + "check_attempts": 2, + "stickiness": "table", + "check_interval": 5, + "check_body": "", + "id": 65431, + "check_passive": true, + "algorithm": "roundrobin", + "check_timeout": 3, + "check_path": "/", + "ssl_cert": null, + "ssl_commonname": "", + "port": 80, + "nodebalancer_id": 123456, + "cipher_suite": "none", + "ssl_key": null, + "nodes_status": { + "up": 0, + "down": 0 + }, + "protocol": "udp", + "ssl_fingerprint": "", + "proxy_protocol": "none", + "udp_check_port": 12345 } ], - "results": 1, + "results": 2, "page": 1, "pages": 1 } diff --git a/test/fixtures/nodebalancers_123456_configs_65432_nodes.json b/test/fixtures/nodebalancers_123456_configs_65432_nodes.json index 658edbb50..f8ffd9edf 100644 --- a/test/fixtures/nodebalancers_123456_configs_65432_nodes.json +++ b/test/fixtures/nodebalancers_123456_configs_65432_nodes.json @@ -9,9 +9,19 @@ "mode": "accept", "config_id": 54321, "nodebalancer_id": 123456 + }, + { + "id": 12345, + "address": "192.168.210.120", + "label": "node12345", + "status": "UP", + "weight": 50, + "mode": "none", + "config_id": 123456, + "nodebalancer_id": 123456 } ], "pages": 1, "page": 1, - "results": 1 + "results": 2 } diff --git a/test/integration/models/nodebalancer/test_nodebalancer.py b/test/integration/models/nodebalancer/test_nodebalancer.py index 21f4d0322..df07de215 100644 --- a/test/integration/models/nodebalancer/test_nodebalancer.py +++ b/test/integration/models/nodebalancer/test_nodebalancer.py @@ -9,7 +9,7 @@ import pytest -from linode_api4 import ApiError, LinodeClient +from linode_api4 import ApiError, LinodeClient, NodeBalancer from linode_api4.objects import ( NodeBalancerConfig, NodeBalancerNode, @@ -64,6 +64,55 @@ def create_nb_config(test_linode_client, e2e_test_firewall): nb.delete() +@pytest.fixture(scope="session") +def create_nb_config_with_udp(test_linode_client, e2e_test_firewall): + client = test_linode_client + label = get_test_label(8) + + nb = client.nodebalancer_create( + region=TEST_REGION, label=label, firewall=e2e_test_firewall.id + ) + + config = nb.config_create(protocol="udp", udp_check_port=1234) + + yield config + + config.delete() + nb.delete() + + +@pytest.fixture(scope="session") +def create_nb(test_linode_client, e2e_test_firewall): + client = test_linode_client + label = get_test_label(8) + + nb = client.nodebalancer_create( + region=TEST_REGION, label=label, firewall=e2e_test_firewall.id + ) + + yield nb + + nb.delete() + + +def test_create_nb(test_linode_client, e2e_test_firewall): + client = test_linode_client + label = get_test_label(8) + + nb = client.nodebalancer_create( + region=TEST_REGION, + label=label, + firewall=e2e_test_firewall.id, + client_udp_sess_throttle=5, + ) + + assert TEST_REGION, nb.region + assert label == nb.label + assert 5 == nb.client_udp_sess_throttle + + nb.delete() + + def test_get_nodebalancer_config(test_linode_client, create_nb_config): config = test_linode_client.load( NodeBalancerConfig, @@ -72,6 +121,65 @@ def test_get_nodebalancer_config(test_linode_client, create_nb_config): ) +def test_get_nb_config_with_udp(test_linode_client, create_nb_config_with_udp): + config = test_linode_client.load( + NodeBalancerConfig, + create_nb_config_with_udp.id, + create_nb_config_with_udp.nodebalancer_id, + ) + + assert "udp" == config.protocol + assert 1234 == config.udp_check_port + assert 16 == config.udp_session_timeout + + +def test_update_nb_config(test_linode_client, create_nb_config_with_udp): + config = test_linode_client.load( + NodeBalancerConfig, + create_nb_config_with_udp.id, + create_nb_config_with_udp.nodebalancer_id, + ) + + config.udp_check_port = 4321 + config.save() + + config_updated = test_linode_client.load( + NodeBalancerConfig, + create_nb_config_with_udp.id, + create_nb_config_with_udp.nodebalancer_id, + ) + + assert 4321 == config_updated.udp_check_port + + +def test_get_nb(test_linode_client, create_nb): + nb = test_linode_client.load( + NodeBalancer, + create_nb.id, + ) + + assert nb.id == create_nb.id + + +def test_update_nb(test_linode_client, create_nb): + nb = test_linode_client.load( + NodeBalancer, + create_nb.id, + ) + + nb.label = "ThisNewLabel" + nb.client_udp_sess_throttle = 5 + nb.save() + + nb_updated = test_linode_client.load( + NodeBalancer, + create_nb.id, + ) + + assert "ThisNewLabel" == nb_updated.label + assert 5 == nb_updated.client_udp_sess_throttle + + @pytest.mark.smoke def test_create_nb_node( test_linode_client, create_nb_config, linode_with_private_ip diff --git a/test/unit/objects/nodebalancers_test.py b/test/unit/objects/nodebalancers_test.py index 05f0ad7de..ed0f0c320 100644 --- a/test/unit/objects/nodebalancers_test.py +++ b/test/unit/objects/nodebalancers_test.py @@ -42,6 +42,23 @@ def test_get_config(self): self.assertEqual(config.ssl_fingerprint, "") self.assertEqual(config.proxy_protocol, "none") + config_udp = NodeBalancerConfig(self.client, 65431, 123456) + self.assertEqual(config_udp.protocol, "udp") + self.assertEqual(config_udp.udp_check_port, 12345) + + def test_update_config_udp(self): + """ + Tests that a config with a protocol of udp can be updated and that cipher suite is properly excluded in save() + """ + with self.mock_put("nodebalancers/123456/configs/65431") as m: + config = self.client.load(NodeBalancerConfig, 65431, 123456) + config.udp_check_port = 54321 + config.save() + + self.assertEqual(m.call_url, "/nodebalancers/123456/configs/65431") + self.assertEqual(m.call_data["udp_check_port"], 54321) + self.assertNotIn("cipher_suite", m.call_data) + class NodeBalancerNodeTest(ClientBaseCase): """ @@ -66,6 +83,9 @@ def test_get_node(self): self.assertEqual(node.config_id, 65432) self.assertEqual(node.nodebalancer_id, 123456) + node_udp = NodeBalancerNode(self.client, 12345, (65432, 123456)) + self.assertEqual(node_udp.mode, "none") + def test_create_node(self): """ Tests that a node can be created