Skip to content

Commit 2925f6b

Browse files
authored
[SDK] Add cluster metadata update support (#150)
* [SDK] Add cluster metadata update support Expose PUT /clusters/{id}/metadata so org admins can toggle deployment_creation_disabled, matching platform#4273. * fix lint
1 parent ad8e09b commit 2925f6b

5 files changed

Lines changed: 85 additions & 5 deletions

File tree

centml/sdk/api.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
CreateDynamoDeploymentRequest,
1111
CreateJobDeploymentRequest,
1212
CreateHardwareInstanceRequest,
13+
UpdateClusterMetadataRequest,
1314
ApiException,
1415
InviteUserRequest,
1516
Metric,
@@ -127,6 +128,9 @@ def resume(self, id):
127128
def get_clusters(self):
128129
return self._api.get_clusters_clusters_get()
129130

131+
def update_cluster_metadata(self, cluster_id: int, request: UpdateClusterMetadataRequest):
132+
return self._api.update_cluster_metadata_clusters_cluster_id_metadata_put(cluster_id, request)
133+
130134
def get_hardware_instances(self, cluster_id=None):
131135
return self._api.get_hardware_instances_hardware_instances_get(
132136
cluster_id=cluster_id if cluster_id else None

examples/sdk/get_clusters.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
- Cluster Name (Prometheus-compatible identifier)
88
- Display Name (human-readable)
99
- Region
10+
- Whether new deployment creation is disabled on the cluster
1011
"""
1112

1213
import click
@@ -30,10 +31,11 @@ def display_clusters(clusters):
3031

3132
for cluster in sorted(clusters, key=lambda x: x.id):
3233
region = cluster.region if cluster.region else "N/A"
33-
click.echo(f"ID: {cluster.id}")
34-
click.echo(f"Cluster Name: {cluster.cluster_name}")
35-
click.echo(f"Display Name: {cluster.display_name}")
36-
click.echo(f"Region: {region}")
34+
click.echo(f"ID: {cluster.id}")
35+
click.echo(f"Cluster Name: {cluster.cluster_name}")
36+
click.echo(f"Display Name: {cluster.display_name}")
37+
click.echo(f"Region: {region}")
38+
click.echo(f"Deployment Creation Disabled: {cluster.deployment_creation_disabled}")
3739
click.echo("-" * 40)
3840

3941

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Example showing how to manage cluster metadata with the CentML SDK.
4+
5+
Covers listing clusters (including deployment_creation_disabled) and updating
6+
that flag via PUT /clusters/{cluster_id}/metadata. Running this script lists
7+
clusters you can access; the update helper shows the call pattern and is not
8+
invoked automatically.
9+
10+
This uses the centml CLI authentication, so make sure you are logged in to the
11+
centml CLI before running it. Updating cluster metadata requires admin
12+
privileges (PERM_ADMIN_MANAGE_HARDWARE) and only works on org-owned clusters
13+
(global clusters return 404).
14+
"""
15+
16+
from centml.sdk import UpdateClusterMetadataRequest
17+
from centml.sdk.api import get_centml_client
18+
19+
20+
def list_clusters():
21+
"""List accessible clusters and whether new deployments are disabled."""
22+
with get_centml_client() as client:
23+
clusters = client.get_clusters().results
24+
25+
if not clusters:
26+
print("No clusters found.")
27+
return
28+
29+
print(f"\nFound {len(clusters)} cluster(s)\n")
30+
for cluster in sorted(clusters, key=lambda x: x.id):
31+
region = cluster.region if cluster.region else "N/A"
32+
print(f"ID: {cluster.id}")
33+
print(f"Cluster Name: {cluster.cluster_name}")
34+
print(f"Display Name: {cluster.display_name}")
35+
print(f"Region: {region}")
36+
print(f"Deployment Creation Disabled: {cluster.deployment_creation_disabled}")
37+
print("-" * 40)
38+
39+
40+
def update_cluster_metadata(cluster_id: int, deployment_creation_disabled: bool):
41+
"""Toggle whether new deployments can be created on an org-owned cluster."""
42+
request = UpdateClusterMetadataRequest(deployment_creation_disabled=deployment_creation_disabled)
43+
with get_centml_client() as client:
44+
cluster = client.update_cluster_metadata(cluster_id, request)
45+
print(
46+
f"Updated cluster {cluster.id} ({cluster.display_name}): "
47+
f"deployment_creation_disabled={cluster.deployment_creation_disabled}"
48+
)
49+
return cluster
50+
51+
52+
if __name__ == "__main__":
53+
list_clusters()
54+
# Example (requires admin privileges on an org-owned cluster):
55+
# update_cluster_metadata(cluster_id=1, deployment_creation_disabled=True)

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@ pyjwt>=2.8.0
55
cryptography==48.0.1
66
websockets>=16.0
77
pyte>=0.8.0
8-
platform-api-python-client==4.23.1
8+
platform-api-python-client==4.25.0
99
click>=8.4.1

tests/test_sdk_api.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
CreateHardwareInstanceRequest,
88
CreateJobDeploymentRequest,
99
DeploymentType,
10+
UpdateClusterMetadataRequest,
1011
)
1112

1213
from centml.sdk import ApiException
@@ -164,6 +165,24 @@ def test_get_centml_client_uses_authenticated_generated_client():
164165
external_api_cls.assert_called_once_with(generated_api_client)
165166

166167

168+
def test_generated_client_exposes_cluster_metadata_contract():
169+
assert hasattr(platform_api_python_client.EXTERNALApi, "update_cluster_metadata_clusters_cluster_id_metadata_put")
170+
assert hasattr(platform_api_python_client, "UpdateClusterMetadataRequest")
171+
172+
173+
def test_update_cluster_metadata_delegates_to_platform_client():
174+
api = MagicMock()
175+
expected_response = MagicMock()
176+
api.update_cluster_metadata_clusters_cluster_id_metadata_put.return_value = expected_response
177+
request = UpdateClusterMetadataRequest(deployment_creation_disabled=True)
178+
client = CentMLClient(api)
179+
180+
response = client.update_cluster_metadata(42, request)
181+
182+
assert response is expected_response
183+
api.update_cluster_metadata_clusters_cluster_id_metadata_put.assert_called_once_with(42, request)
184+
185+
167186
def test_get_hardware_instances_returns_results():
168187
api = MagicMock()
169188
expected_results = [SimpleNamespace(id=1), SimpleNamespace(id=2)]

0 commit comments

Comments
 (0)