diff --git a/linode_api4/groups/database.py b/linode_api4/groups/database.py index fec3df929..9de02ac35 100644 --- a/linode_api4/groups/database.py +++ b/linode_api4/groups/database.py @@ -73,7 +73,7 @@ def mysql_config_options(self): """ Returns a detailed list of all the configuration options for MySQL Databases. - API Documentation: TODO + API Documentation: https://techdocs.akamai.com/linode-api/reference/get-databases-mysql-config :returns: The JSON configuration options for MySQL Databases. """ @@ -83,7 +83,7 @@ def postgresql_config_options(self): """ Returns a detailed list of all the configuration options for PostgreSQL Databases. - API Documentation: TODO + API Documentation: https://techdocs.akamai.com/linode-api/reference/get-databases-postgresql-config :returns: The JSON configuration options for PostgreSQL Databases. """ diff --git a/linode_api4/objects/account.py b/linode_api4/objects/account.py index c7318d871..836f41522 100644 --- a/linode_api4/objects/account.py +++ b/linode_api4/objects/account.py @@ -3,6 +3,7 @@ from datetime import datetime import requests +from deprecated import deprecated from linode_api4.errors import ApiError, UnexpectedResponseError from linode_api4.objects import DATE_FORMAT, Volume @@ -305,6 +306,12 @@ def volume(self): return Volume(self._client, self.entity.id) return None + @deprecated( + reason="`mark_read` API is deprecated. Use the 'mark_seen' " + "API instead. Please note that the `mark_seen` API functions " + "differently and will mark all events up to and including the " + "referenced event-id as 'seen' rather than individual events.", + ) def mark_read(self): """ Marks a single Event as read. diff --git a/linode_api4/objects/nodebalancer.py b/linode_api4/objects/nodebalancer.py index 840d5b965..f02dda269 100644 --- a/linode_api4/objects/nodebalancer.py +++ b/linode_api4/objects/nodebalancer.py @@ -77,6 +77,8 @@ class NodeBalancerConfig(DerivedBase): The configuration information for a single port of this NodeBalancer. API documentation: https://techdocs.akamai.com/linode-api/reference/get-node-balancer-config + + NOTE: UDP NodeBalancer Configs may not currently be available to all users. """ api_endpoint = "/nodebalancers/{nodebalancer_id}/configs/{id}" @@ -97,6 +99,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 +110,20 @@ class NodeBalancerConfig(DerivedBase): "proxy_protocol": Property(mutable=True), } + def _serialize(self, is_put: bool = False): + """ + This override removes the `cipher_suite` field from the PUT request + body on calls to save(...) for UDP configs, which is rejected by + the API. + """ + + result = super()._serialize(is_put) + + if is_put and result["protocol"] == "udp" and "cipher_suite" in result: + del result["cipher_suite"] + + return result + @property def nodes(self): """ @@ -233,6 +251,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