Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Support for LKE Enterprise #521

Merged
merged 2 commits into from
Mar 31, 2025
Merged
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
1 change: 1 addition & 0 deletions linode_api4/groups/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from .image import *
from .linode import *
from .lke import *
from .lke_tier import *
from .longview import *
from .networking import *
from .nodebalancer import *
Expand Down
26 changes: 24 additions & 2 deletions linode_api4/groups/lke.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from typing import Any, Dict, Union
from typing import Any, Dict, Optional, Union

from linode_api4.errors import UnexpectedResponseError
from linode_api4.groups import Group
from linode_api4.groups.lke_tier import LKETierGroup
from linode_api4.objects import (
KubeVersion,
LKECluster,
Expand Down Expand Up @@ -67,6 +68,7 @@ def cluster_create(
LKEClusterControlPlaneOptions, Dict[str, Any]
] = None,
apl_enabled: bool = False,
tier: Optional[str] = None,
**kwargs,
):
"""
Expand Down Expand Up @@ -104,9 +106,13 @@ 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 field is in beta and may only
function if base_url is set to `https://api.linode.com/v4beta`.
:type apl_enabled: bool
:param tier: The tier of LKE cluster to create.
NOTE: This field is in beta and may only
function if base_url is set to `https://api.linode.com/v4beta`.
:type tier: str
:param kwargs: Any other arguments to pass along to the API. See the API
docs for possible values.

Expand All @@ -122,6 +128,7 @@ def cluster_create(
node_pools if isinstance(node_pools, list) else [node_pools]
),
"control_plane": control_plane,
"tier": tier,
}
params.update(kwargs)

Expand Down Expand Up @@ -183,3 +190,18 @@ def types(self, *filters):
return self.client._get_and_filter(
LKEType, *filters, endpoint="/lke/types"
)

def tier(self, id: str) -> LKETierGroup:
"""
Returns an object representing the LKE tier API path.

NOTE: LKE tiers may not currently be available to all users.

:param id: The ID of the tier.
:type id: str

:returns: An object representing the LKE tier API path.
:rtype: LKETier
"""

return LKETierGroup(self.client, id)
40 changes: 40 additions & 0 deletions linode_api4/groups/lke_tier.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
from linode_api4.groups import Group
from linode_api4.objects import TieredKubeVersion


class LKETierGroup(Group):
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a new pattern since the LKE tier path is the first of its kind. Let me know if you have any feedback!

"""
Encapsulates methods related to a specific LKE tier. This
should not be instantiated on its own, but should instead be used through
an instance of :any:`LinodeClient`::

client = LinodeClient(token)
instances = client.lke.tier("standard") # use the LKETierGroup

This group contains all features beneath the `/lke/tiers/{tier}` group in the API v4.
"""

def __init__(self, client: "LinodeClient", tier: str):
super().__init__(client)
self.tier = tier

def versions(self, *filters):
"""
Returns a paginated list of versions for this tier matching the given filters.

API Documentation: Not Yet Available

:param filters: Any number of filters to apply to this query.
See :doc:`Filtering Collections</linode_api4/objects/filtering>`
for more details on filtering.

:returns: A paginated list of kube versions that match the query.
:rtype: PaginatedList of TieredKubeVersion
"""

return self.client._get_and_filter(
TieredKubeVersion,
endpoint=f"/lke/tiers/{self.tier}/versions",
parent_id=self.tier,
*filters,
)
17 changes: 14 additions & 3 deletions linode_api4/linode_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -455,7 +455,13 @@ def volume_create(self, label, region=None, linode=None, size=20, **kwargs):
)

# helper functions
def _get_and_filter(self, obj_type, *filters, endpoint=None):
def _get_and_filter(
self,
obj_type,
*filters,
endpoint=None,
parent_id=None,
):
parsed_filters = None
if filters:
if len(filters) > 1:
Expand All @@ -467,8 +473,13 @@ def _get_and_filter(self, obj_type, *filters, endpoint=None):

# Use sepcified endpoint
if endpoint:
return self._get_objects(endpoint, obj_type, filters=parsed_filters)
return self._get_objects(
endpoint, obj_type, parent_id=parent_id, filters=parsed_filters
)
else:
return self._get_objects(
obj_type.api_list(), obj_type, filters=parsed_filters
obj_type.api_list(),
obj_type,
parent_id=parent_id,
filters=parsed_filters,
)
50 changes: 48 additions & 2 deletions linode_api4/objects/lke.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
Region,
Type,
)
from linode_api4.objects.base import _flatten_request_body_recursive
from linode_api4.util import drop_null_keys


Expand Down Expand Up @@ -49,6 +50,26 @@ class KubeVersion(Base):
}


class TieredKubeVersion(DerivedBase):
"""
A TieredKubeVersion is a version of Kubernetes that is specific to a certain LKE tier.

NOTE: LKE tiers may not currently be available to all users.

API Documentation: https://techdocs.akamai.com/linode-api/reference/get-lke-version
"""

api_endpoint = "/lke/tiers/{tier}/versions/{id}"
parent_id_name = "tier"
id_attribute = "id"
derived_url_path = "versions"

properties = {
"id": Property(identifier=True),
"tier": Property(identifier=True),
}


@dataclass
class LKENodePoolTaint(JSONObject):
"""
Expand Down Expand Up @@ -154,6 +175,8 @@ class LKENodePool(DerivedBase):
An LKE Node Pool describes a pool of Linode Instances that exist within an
LKE Cluster.

NOTE: The k8s_version and update_strategy fields are only available for LKE Enterprise clusters.

API Documentation: https://techdocs.akamai.com/linode-api/reference/get-lke-node-pool
"""

Expand All @@ -175,6 +198,12 @@ class LKENodePool(DerivedBase):
"tags": Property(mutable=True, unordered=True),
"labels": Property(mutable=True),
"taints": Property(mutable=True),
# Enterprise-specific properties
# Ideally we would use slug_relationship=TieredKubeVersion here, but
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any ideas on workarounds for this?

# it isn't possible without an extra request because the tier is not
# directly exposed in the node pool response.
"k8s_version": Property(mutable=True),
"update_strategy": Property(mutable=True),
}

def _parse_raw_node(
Expand Down Expand Up @@ -255,6 +284,7 @@ class LKECluster(Base):
"pools": Property(derived_class=LKENodePool),
"control_plane": Property(mutable=True),
"apl_enabled": Property(),
"tier": Property(),
}

def invalidate(self):
Expand Down Expand Up @@ -385,6 +415,10 @@ def node_pool_create(
node_count: int,
labels: Optional[Dict[str, str]] = None,
taints: List[Union[LKENodePoolTaint, Dict[str, Any]]] = None,
k8s_version: Optional[
Union[str, KubeVersion, TieredKubeVersion]
] = None,
update_strategy: Optional[str] = None,
**kwargs,
):
"""
Expand All @@ -399,7 +433,13 @@ def node_pool_create(
:param labels: A dict mapping labels to their values to apply to this pool.
:type labels: Dict[str, str]
:param taints: A list of taints to apply to this pool.
:type taints: List of :any:`LKENodePoolTaint` or dict
:type taints: List of :any:`LKENodePoolTaint` or dict.
:param k8s_version: The Kubernetes version to use for this pool.
NOTE: This field is specific to enterprise clusters.
:type k8s_version: str, KubeVersion, or TieredKubeVersion
:param update_strategy: The strategy to use when updating this node pool.
NOTE: This field is specific to enterprise clusters.
:type update_strategy: str
:param kwargs: Any other arguments to pass to the API. See the API docs
for possible values.

Expand All @@ -409,6 +449,10 @@ def node_pool_create(
params = {
"type": node_type,
"count": node_count,
"labels": labels,
"taints": taints,
"k8s_version": k8s_version,
"update_strategy": update_strategy,
}

if labels is not None:
Expand All @@ -420,7 +464,9 @@ def node_pool_create(
params.update(kwargs)

result = self._client.post(
"{}/pools".format(LKECluster.api_endpoint), model=self, data=params
"{}/pools".format(LKECluster.api_endpoint),
model=self,
data=drop_null_keys(_flatten_request_body_recursive(params)),
)
self.invalidate()

Expand Down
1 change: 1 addition & 0 deletions test/fixtures/lke_clusters_18881.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"label": "example-cluster",
"region": "ap-west",
"k8s_version": "1.19",
"tier": "standard",
"tags": [],
"control_plane": {
"high_availability": true
Expand Down
14 changes: 14 additions & 0 deletions test/fixtures/lke_clusters_18882.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"id": 18881,
"status": "ready",
"created": "2021-02-10T23:54:21",
"updated": "2021-02-10T23:54:21",
"label": "example-cluster-2",
"region": "ap-west",
"k8s_version": "1.31.1+lke1",
"tier": "enterprise",
"tags": [],
"control_plane": {
"high_availability": true
}
}
18 changes: 18 additions & 0 deletions test/fixtures/lke_clusters_18882_pools_789.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"id": 789,
"type": "g6-standard-2",
"count": 3,
"nodes": [],
"disks": [],
"autoscaler": {
"enabled": false,
"min": 3,
"max": 3
},
"labels": {},
"taints": [],
"tags": [],
"disk_encryption": "enabled",
"k8s_version": "1.31.1+lke1",
"update_strategy": "rolling_update"
}
19 changes: 19 additions & 0 deletions test/fixtures/lke_tiers_standard_versions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"data": [
{
"id": "1.32",
"tier": "standard"
},
{
"id": "1.31",
"tier": "standard"
},
{
"id": "1.30",
"tier": "standard"
}
],
"page": 1,
"pages": 1,
"results": 3
}
Loading