From 620091f84d772e0e2bb06c11f0d0370530bd863f Mon Sep 17 00:00:00 2001 From: Zhiwei Liang <121905282+zliang-akamai@users.noreply.github.com> Date: Mon, 14 Aug 2023 13:58:12 -0400 Subject: [PATCH 1/2] Add LinodeClient type annotation in Group class (#314) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 📝 Description Better dev experience with an IDE or text editor with this typing. --- linode_api4/groups/group.py | 10 +++++++++- linode_api4/linode_client.py | 21 +++++++++++++++++++-- pyproject.toml | 2 +- 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/linode_api4/groups/group.py b/linode_api4/groups/group.py index 1ca41627a..c591b7fda 100644 --- a/linode_api4/groups/group.py +++ b/linode_api4/groups/group.py @@ -1,3 +1,11 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from linode_api4 import LinodeClient + + class Group: - def __init__(self, client: "LinodeClient"): + def __init__(self, client: LinodeClient): self.client = client diff --git a/linode_api4/linode_client.py b/linode_api4/linode_client.py index 831e320fb..5b2cc8561 100644 --- a/linode_api4/linode_client.py +++ b/linode_api4/linode_client.py @@ -10,8 +10,25 @@ from requests.adapters import HTTPAdapter, Retry from linode_api4.errors import ApiError, UnexpectedResponseError -from linode_api4.groups import * -from linode_api4.objects import * +from linode_api4.groups import ( + AccountGroup, + DatabaseGroup, + DomainGroup, + ImageGroup, + LinodeGroup, + LKEGroup, + LongviewGroup, + NetworkingGroup, + NodeBalancerGroup, + ObjectStorageGroup, + PollingGroup, + ProfileGroup, + RegionGroup, + SupportGroup, + TagGroup, + VolumeGroup, +) +from linode_api4.objects import Image, and_ from linode_api4.objects.filtering import Filter from .common import SSH_KEY_TYPES, load_and_validate_keys diff --git a/pyproject.toml b/pyproject.toml index d04b49d84..ccb0ec5f1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,7 +11,7 @@ line-length = 80 target-version = ["py37", "py38", "py39", "py310", "py311"] [tool.autoflake] -expand-star-imports = false +expand-star-imports = true ignore-init-module-imports = true ignore-pass-after-docstring = true in-place = true From 12c60ef517aa5c875addafa388c76755b5c2c4c6 Mon Sep 17 00:00:00 2001 From: Ye Chen <127243817+yec-akamai@users.noreply.github.com> Date: Tue, 15 Aug 2023 11:44:33 -0400 Subject: [PATCH 2/2] fix: `ip_addresses_share` to remove shared IPs (#316) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 📝 Description Fix an issue in `ip_addresses_share` to allow it unshare IPs. In the API doc, entering an empty IP array is allowed and it means removing the shared IPs from the Linode. In the previous implementation, we always looked for `ips[0]` when building the request param. It brought the index out of range error when an empty IP array is given. ## ✔️ How to Test `tox` --- linode_api4/groups/networking.py | 20 +++++++++--- test/fixtures/networking_ips_share.json | 1 + test/integration/models/test_networking.py | 36 ++++++++++++++++++++++ 3 files changed, 52 insertions(+), 5 deletions(-) create mode 100644 test/fixtures/networking_ips_share.json diff --git a/linode_api4/groups/networking.py b/linode_api4/groups/networking.py index d22486652..a226874ef 100644 --- a/linode_api4/groups/networking.py +++ b/linode_api4/groups/networking.py @@ -284,22 +284,32 @@ def ips_share(self, linode, *ips): def ip_addresses_share(self, ips, linode): """ - Configure shared IPs. P sharing allows IP address reassignment + Configure shared IPs. IP sharing allows IP address reassignment (also referred to as IP failover) from one Linode to another if the primary Linode becomes unresponsive. This means that requests to the primary Linode’s IP address can be automatically rerouted to secondary Linodes at the configured shared IP addresses. + API Documentation: https://www.linode.com/docs/api/networking/#ip-addresses-share + :param linode: The id of the Instance or the Instance to share the IPAddresses with. This Instance will be able to bring up the given addresses. :type: linode: int or Instance - :param ips: Any number of IPAddresses to share to the Instance. + :param ips: Any number of IPAddresses to share to the Instance. Enter an empty array to + remove all shared IP addresses. :type ips: str or IPAddress """ + shared_ips = [] + for ip in ips: + if isinstance(ip, str): + shared_ips.append(ip) + elif isinstance(ip, IPAddress): + shared_ips.append(ip.address) + else: + shared_ips.append(str(ip)) # and hope that works + params = { - "ips": ips - if not isinstance(ips[0], IPAddress) - else [ip.address for ip in ips], + "ips": shared_ips, "linode_id": linode if not isinstance(linode, Instance) else linode.id, diff --git a/test/fixtures/networking_ips_share.json b/test/fixtures/networking_ips_share.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/test/fixtures/networking_ips_share.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/test/integration/models/test_networking.py b/test/integration/models/test_networking.py index e90b63ecb..d3b01e0bd 100644 --- a/test/integration/models/test_networking.py +++ b/test/integration/models/test_networking.py @@ -13,3 +13,39 @@ def test_get_networking_rules(get_client, create_firewall): assert "inbound_policy" in str(rules) assert "outbound" in str(rules) assert "outbound_policy" in str(rules) + + +@pytest.mark.smoke +def test_ip_addresses_share(self): + """ + Test that you can share IP addresses with Linode. + """ + ip_share_url = "/networking/ips/share" + ips = ["127.0.0.1"] + linode_id = 12345 + with self.mock_post(ip_share_url) as m: + result = self.client.networking.ip_addresses_share(ips, linode_id) + + self.assertIsNotNone(result) + self.assertEqual(m.call_url, ip_share_url) + self.assertEqual( + m.call_data, + { + "ips": ips, + "linode": linode_id, + }, + ) + + # Test that entering an empty IP array is allowed. + with self.mock_post(ip_share_url) as m: + result = self.client.networking.ip_addresses_share([], linode_id) + + self.assertIsNotNone(result) + self.assertEqual(m.call_url, ip_share_url) + self.assertEqual( + m.call_data, + { + "ips": [], + "linode": linode_id, + }, + )