From 391c4e7551f0de2e29a3d1a4fcbc03bcdfec6f62 Mon Sep 17 00:00:00 2001 From: Lena Garber Date: Wed, 15 Jan 2025 15:31:24 -0500 Subject: [PATCH 1/8] Add APL-related fields --- linode_api4/groups/lke.py | 11 +++++++++-- linode_api4/objects/lke.py | 31 +++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/linode_api4/groups/lke.py b/linode_api4/groups/lke.py index d0de66f37..af539711d 100644 --- a/linode_api4/groups/lke.py +++ b/linode_api4/groups/lke.py @@ -66,6 +66,7 @@ def cluster_create( control_plane: Union[ LKEClusterControlPlaneOptions, Dict[str, Any] ] = None, + apl_enabled: bool = False, **kwargs, ): """ @@ -100,8 +101,10 @@ def cluster_create( formatted dicts. :param kube_version: The version of Kubernetes to use :type kube_version: KubeVersion or str - :param control_plane: Dict[str, Any] or LKEClusterControlPlaneRequest - :type control_plane: The control plane configuration of this LKE cluster. + :param control_plane: The control plane configuration of this LKE cluster. + :type control_plane: Dict[str, Any] or LKEClusterControlPlaneRequest + :param apl_enabled: Whether this cluster should use APL. + :type apl_enabled: bool :param kwargs: Any other arguments to pass along to the API. See the API docs for possible values. @@ -120,6 +123,10 @@ def cluster_create( } params.update(kwargs) + # Prevent errors for users without access to APL + if apl_enabled: + params["apl_enabled"] = apl_enabled + result = self.client.post( "/lke/clusters", data=_flatten_request_body_recursive(drop_null_keys(params)), diff --git a/linode_api4/objects/lke.py b/linode_api4/objects/lke.py index 7ff6b0fd8..bdea1c1ca 100644 --- a/linode_api4/objects/lke.py +++ b/linode_api4/objects/lke.py @@ -257,6 +257,7 @@ class LKECluster(Base): "k8s_version": Property(slug_relationship=KubeVersion, mutable=True), "pools": Property(derived_class=LKENodePool), "control_plane": Property(mutable=True), + "apl_enabled": Property(mutable=True), } def invalidate(self): @@ -353,6 +354,36 @@ def control_plane_acl(self) -> LKEClusterControlPlaneACL: return LKEClusterControlPlaneACL.from_json(self._control_plane_acl) + @property + def apl_console_url(self) -> Optional[str]: + """ + Returns the URL of this cluster's APL installation if this cluster + is APL-enabled, else None. + + :returns: The URL of the APL console for this cluster. + :rtype: str or None + """ + + if not self.apl_enabled: + return None + + return f"https://console.lke{self.id}.akamai-apl.net" + + @property + def apl_health_check_url(self) -> Optional[str]: + """ + Returns the URL of this cluster's APL health check endpoint if this cluster + is APL-enabled, else None. + + :returns: The URL of the APL console for this cluster. + :rtype: str or None + """ + + if not self.apl_enabled: + return None + + return f"https://auth.lke{self.id}.akamai-apl.net/ready" + def node_pool_create( self, node_type: Union[Type, str], From 8d37b84649b0d6e4fb742eb5f3d4485c3df88ead Mon Sep 17 00:00:00 2001 From: Lena Garber Date: Wed, 15 Jan 2025 15:45:22 -0500 Subject: [PATCH 2/8] Add unit test --- test/fixtures/lke_clusters.json | 3 ++- test/fixtures/lke_clusters_18881.json | 3 ++- test/unit/objects/lke_test.py | 34 +++++++++++++++++++++++++++ 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/test/fixtures/lke_clusters.json b/test/fixtures/lke_clusters.json index 787a2fae5..1a932c8ec 100644 --- a/test/fixtures/lke_clusters.json +++ b/test/fixtures/lke_clusters.json @@ -6,5 +6,6 @@ "label": "example-cluster", "region": "ap-west", "k8s_version": "1.19", - "tags": [] + "tags": [], + "apl_enabled": true } diff --git a/test/fixtures/lke_clusters_18881.json b/test/fixtures/lke_clusters_18881.json index 755d11c58..bb5807c18 100644 --- a/test/fixtures/lke_clusters_18881.json +++ b/test/fixtures/lke_clusters_18881.json @@ -9,5 +9,6 @@ "tags": [], "control_plane": { "high_availability": true - } + }, + "apl_enabled": true } \ No newline at end of file diff --git a/test/unit/objects/lke_test.py b/test/unit/objects/lke_test.py index 100f36487..a8aaa0340 100644 --- a/test/unit/objects/lke_test.py +++ b/test/unit/objects/lke_test.py @@ -39,6 +39,7 @@ def test_get_cluster(self): self.assertEqual(cluster.region.id, "ap-west") self.assertEqual(cluster.k8s_version.id, "1.19") self.assertTrue(cluster.control_plane.high_availability) + self.assertTrue(cluster.apl_enabled) def test_get_pool(self): """ @@ -352,6 +353,39 @@ def test_cluster_create_with_labels_and_taints(self): ], } + def test_cluster_create_with_apl(self): + """ + Tests that an LKE cluster can be created with APL enabled. + """ + + with self.mock_post("lke/clusters") as m: + cluster = self.client.lke.cluster_create( + "us-mia", + "test-aapl-cluster", + [ + self.client.lke.node_pool( + "g6-dedicated-4", + 3, + ) + ], + "1.29", + apl_enabled=True, + control_plane=LKEClusterControlPlaneOptions( + high_availability=True, + ), + ) + + assert m.call_data["apl_enabled"] == True + assert m.call_data["control_plane"]["high_availability"] == True + + assert ( + cluster.apl_console_url == "https://console.lke18881.akamai-apl.net" + ) + assert ( + cluster.apl_health_check_url + == "https://auth.lke18881.akamai-apl.net/ready" + ) + def test_populate_with_taints(self): """ Tests that LKENodePool correctly handles a list of LKENodePoolTaint and Dict objects. From 8b5b2a27e271b686fe89b01ab6ba49f8ec643afa Mon Sep 17 00:00:00 2001 From: Lena Garber Date: Wed, 15 Jan 2025 15:49:02 -0500 Subject: [PATCH 3/8] Add integration test --- test/integration/models/lke/test_lke.py | 38 +++++++++++++++++++++++++ test/unit/objects/lke_test.py | 1 + 2 files changed, 39 insertions(+) diff --git a/test/integration/models/lke/test_lke.py b/test/integration/models/lke/test_lke.py index 4a3ba6c7e..015334c36 100644 --- a/test/integration/models/lke/test_lke.py +++ b/test/integration/models/lke/test_lke.py @@ -110,6 +110,31 @@ def lke_cluster_with_labels_and_taints(test_linode_client): cluster.delete() +@pytest.fixture(scope="session") +def lke_cluster_with_apl(test_linode_client): + version = test_linode_client.lke.versions()[0] + + region = get_region(test_linode_client, {"Kubernetes", "Disk Encryption"}) + + node_pools = test_linode_client.lke.node_pool("g6-dedicated-2", 3) + label = get_test_label() + "_cluster" + + cluster = test_linode_client.lke.cluster_create( + region, + label, + node_pools, + version, + control_plane=LKEClusterControlPlaneOptions( + high_availability=True, + ), + apl_enabled=True, + ) + + yield cluster + + cluster.delete() + + def get_cluster_status(cluster: LKECluster, status: str): return cluster._raw_json["status"] == status @@ -328,6 +353,19 @@ def test_lke_cluster_labels_and_taints(lke_cluster_with_labels_and_taints): assert LKENodePoolTaint.from_json(updated_taints[1]) in pool.taints +@pytest.mark.flaky(reruns=3, reruns_delay=2) +def test_lke_cluster_with_apl(lke_cluster_with_apl): + assert lke_cluster_with_apl.apl_enabled == True + assert ( + lke_cluster_with_apl.apl_console_url + == f"https://console.lke{lke_cluster_with_apl.id}.akamai-apl.net" + ) + assert ( + lke_cluster_with_apl.apl_health_check_url + == f"https://auth.lke{lke_cluster_with_apl.id}.akamai-apl.net/ready" + ) + + def test_lke_types(test_linode_client): types = test_linode_client.lke.types() diff --git a/test/unit/objects/lke_test.py b/test/unit/objects/lke_test.py index a8aaa0340..c394e2f9a 100644 --- a/test/unit/objects/lke_test.py +++ b/test/unit/objects/lke_test.py @@ -381,6 +381,7 @@ def test_cluster_create_with_apl(self): assert ( cluster.apl_console_url == "https://console.lke18881.akamai-apl.net" ) + assert ( cluster.apl_health_check_url == "https://auth.lke18881.akamai-apl.net/ready" From 9e6af6281b3584c91a2b6f2cb0902a3dac4476f7 Mon Sep 17 00:00:00 2001 From: Lena Garber Date: Wed, 15 Jan 2025 15:50:27 -0500 Subject: [PATCH 4/8] Fix integration test --- test/integration/models/lke/test_lke.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/integration/models/lke/test_lke.py b/test/integration/models/lke/test_lke.py index 015334c36..f2fb3f2e5 100644 --- a/test/integration/models/lke/test_lke.py +++ b/test/integration/models/lke/test_lke.py @@ -116,7 +116,8 @@ def lke_cluster_with_apl(test_linode_client): region = get_region(test_linode_client, {"Kubernetes", "Disk Encryption"}) - node_pools = test_linode_client.lke.node_pool("g6-dedicated-2", 3) + # NOTE: g6-dedicated-4 is the minimum APL-compatible Linode type + node_pools = test_linode_client.lke.node_pool("g6-dedicated-4", 3) label = get_test_label() + "_cluster" cluster = test_linode_client.lke.cluster_create( From 355193ccc831066942637d49f520973e8e875be3 Mon Sep 17 00:00:00 2001 From: Lena Garber Date: Thu, 16 Jan 2025 12:05:35 -0500 Subject: [PATCH 5/8] Add beta notice --- linode_api4/groups/lke.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/linode_api4/groups/lke.py b/linode_api4/groups/lke.py index af539711d..8b2d1e7d0 100644 --- a/linode_api4/groups/lke.py +++ b/linode_api4/groups/lke.py @@ -104,6 +104,8 @@ def cluster_create( :param control_plane: The control plane configuration of this LKE cluster. :type control_plane: Dict[str, Any] or LKEClusterControlPlaneRequest :param apl_enabled: Whether this cluster should use APL. + note:: This endpoint is in beta and may only + function if base_url is set to `https://api.linode.com/v4beta`. :type apl_enabled: bool :param kwargs: Any other arguments to pass along to the API. See the API docs for possible values. From 7c0d94ae274fdba862b86502b3b17455e4ed170e Mon Sep 17 00:00:00 2001 From: Lena Garber Date: Thu, 16 Jan 2025 12:08:24 -0500 Subject: [PATCH 6/8] Fix note --- linode_api4/groups/lke.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linode_api4/groups/lke.py b/linode_api4/groups/lke.py index 8b2d1e7d0..d64d45536 100644 --- a/linode_api4/groups/lke.py +++ b/linode_api4/groups/lke.py @@ -104,7 +104,7 @@ def cluster_create( :param control_plane: The control plane configuration of this LKE cluster. :type control_plane: Dict[str, Any] or LKEClusterControlPlaneRequest :param apl_enabled: Whether this cluster should use APL. - note:: This endpoint is in beta and may only + NOTE: This endpoint is in beta and may only function if base_url is set to `https://api.linode.com/v4beta`. :type apl_enabled: bool :param kwargs: Any other arguments to pass along to the API. See the API From 06898ebea4fbbdd4cfda81e7713c80861b44f037 Mon Sep 17 00:00:00 2001 From: Lena Garber Date: Thu, 30 Jan 2025 11:29:24 -0500 Subject: [PATCH 7/8] Make apl_enabled immutable --- linode_api4/objects/lke.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linode_api4/objects/lke.py b/linode_api4/objects/lke.py index bdea1c1ca..e675eae8e 100644 --- a/linode_api4/objects/lke.py +++ b/linode_api4/objects/lke.py @@ -257,7 +257,7 @@ class LKECluster(Base): "k8s_version": Property(slug_relationship=KubeVersion, mutable=True), "pools": Property(derived_class=LKENodePool), "control_plane": Property(mutable=True), - "apl_enabled": Property(mutable=True), + "apl_enabled": Property(), } def invalidate(self): From 429bfda84de616e9a6609a4b907eb3cd0fe19f1d Mon Sep 17 00:00:00 2001 From: Lena Garber Date: Thu, 30 Jan 2025 11:32:32 -0500 Subject: [PATCH 8/8] make format --- linode_api4/objects/linode.py | 2 +- test/integration/models/image/test_image.py | 4 ++-- test/unit/objects/image_test.py | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/linode_api4/objects/linode.py b/linode_api4/objects/linode.py index 776e1f988..03e3f6672 100644 --- a/linode_api4/objects/linode.py +++ b/linode_api4/objects/linode.py @@ -1920,7 +1920,7 @@ def _serialize(self): def _expand_placement_group_assignment( pg: Union[ InstancePlacementGroupAssignment, "PlacementGroup", Dict[str, Any], int - ] + ], ) -> Optional[Dict[str, Any]]: """ Expands the placement group argument into a dict for use in an API request body. diff --git a/test/integration/models/image/test_image.py b/test/integration/models/image/test_image.py index 94c819709..9124ddf97 100644 --- a/test/integration/models/image/test_image.py +++ b/test/integration/models/image/test_image.py @@ -32,8 +32,8 @@ def image_upload_url(test_linode_client): @pytest.fixture(scope="session") def test_uploaded_image(test_linode_client): test_image_content = ( - b"\x1F\x8B\x08\x08\xBD\x5C\x91\x60\x00\x03\x74\x65\x73\x74\x2E\x69" - b"\x6D\x67\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x1f\x8b\x08\x08\xbd\x5c\x91\x60\x00\x03\x74\x65\x73\x74\x2e\x69" + b"\x6d\x67\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00" ) label = get_test_label() + "_image" diff --git a/test/unit/objects/image_test.py b/test/unit/objects/image_test.py index 5d1ce42d5..f479d021f 100644 --- a/test/unit/objects/image_test.py +++ b/test/unit/objects/image_test.py @@ -8,8 +8,8 @@ # A minimal gzipped image that will be accepted by the API TEST_IMAGE_CONTENT = ( - b"\x1F\x8B\x08\x08\xBD\x5C\x91\x60\x00\x03\x74\x65\x73\x74\x2E\x69" - b"\x6D\x67\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x1f\x8b\x08\x08\xbd\x5c\x91\x60\x00\x03\x74\x65\x73\x74\x2e\x69" + b"\x6d\x67\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00" )