Skip to content
This repository was archived by the owner on Jan 23, 2026. It is now read-only.

Commit 4447987

Browse files
committed
Implement wrapper for Lease related methods
1 parent 54a4efe commit 4447987

1 file changed

Lines changed: 161 additions & 6 deletions

File tree

  • packages/jumpstarter/jumpstarter/client

packages/jumpstarter/jumpstarter/client/grpc.py

Lines changed: 161 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,38 @@
11
from __future__ import annotations
22

33
from dataclasses import dataclass, field
4+
from datetime import timedelta
45

56
import yaml
7+
from google.protobuf import duration_pb2, field_mask_pb2, json_format
68
from grpc.aio import Channel
7-
from jumpstarter_protocol import client_pb2, client_pb2_grpc
8-
from pydantic import BaseModel
9+
from jumpstarter_protocol import client_pb2, client_pb2_grpc, kubernetes_pb2
10+
from pydantic import BaseModel, ConfigDict, Field, field_serializer
911

1012

11-
def parse_exporter_identifier(identifier: str) -> (str, str):
13+
def parse_identifier(identifier: str, kind: str) -> (str, str):
1214
segments = identifier.split("/")
1315
if len(segments) != 4:
1416
raise ValueError("incorrect number of segments in identifier, expecting 4, got {}".format(len(segments)))
1517
if segments[0] != "namespaces":
1618
raise ValueError("incorrect first segment in identifier, expecting namespaces, got {}".format(segments[0]))
17-
if segments[2] != "exporters":
18-
raise ValueError("incorrect third segment in identifier, expecting exporters, got {}".format(segments[2]))
19+
if segments[2] != kind:
20+
raise ValueError("incorrect third segment in identifier, expecting {}, got {}".format(kind, segments[2]))
1921
return segments[1], segments[3]
2022

2123

24+
def parse_client_identifier(identifier: str) -> (str, str):
25+
return parse_identifier(identifier, "clients")
26+
27+
28+
def parse_exporter_identifier(identifier: str) -> (str, str):
29+
return parse_identifier(identifier, "exporters")
30+
31+
32+
def parse_lease_identifier(identifier: str) -> (str, str):
33+
return parse_identifier(identifier, "leases")
34+
35+
2236
class Exporter(BaseModel):
2337
namespace: str
2438
name: str
@@ -30,9 +44,54 @@ def from_protobuf(cls, data: client_pb2.Exporter) -> Exporter:
3044
return cls(namespace=namespace, name=name, labels=data.labels)
3145

3246

47+
class Lease(BaseModel):
48+
namespace: str
49+
name: str
50+
selector: str
51+
duration: timedelta
52+
client: str
53+
exporter: str
54+
conditions: list[kubernetes_pb2.Condition]
55+
56+
model_config = ConfigDict(
57+
arbitrary_types_allowed=True,
58+
ser_json_timedelta="float",
59+
)
60+
61+
@field_serializer("conditions")
62+
def serialize_conditions(self, conditions: list[kubernetes_pb2.Condition], _info):
63+
return [json_format.MessageToDict(condition) for condition in conditions]
64+
65+
@classmethod
66+
def from_protobuf(cls, data: client_pb2.Lease) -> Lease:
67+
namespace, name = parse_lease_identifier(data.name)
68+
69+
_, client = parse_client_identifier(data.client)
70+
if data.exporter != "":
71+
_, exporter = parse_exporter_identifier(data.exporter)
72+
else:
73+
exporter = ""
74+
75+
return cls(
76+
namespace=namespace,
77+
name=name,
78+
selector=data.selector,
79+
duration=data.duration.ToTimedelta(),
80+
client=client,
81+
exporter=exporter,
82+
conditions=data.conditions,
83+
)
84+
85+
def dump_json(self):
86+
return self.model_dump_json(indent=4, by_alias=True)
87+
88+
def dump_yaml(self):
89+
return yaml.safe_dump(self.model_dump(mode="json", by_alias=True), indent=2)
90+
91+
3392
class ExporterList(BaseModel):
3493
exporters: list[Exporter]
35-
next_page_token: str | None
94+
next_page_token: str | None = Field(exclude=True)
3695

3796
@classmethod
3897
def from_protobuf(cls, data: client_pb2.ListExportersResponse) -> ExporterList:
@@ -48,6 +107,24 @@ def dump_yaml(self):
48107
return yaml.safe_dump(self.model_dump(mode="json", by_alias=True), indent=2)
49108

50109

110+
class LeaseList(BaseModel):
111+
leases: list[Lease]
112+
next_page_token: str | None = Field(exclude=True)
113+
114+
@classmethod
115+
def from_protobuf(cls, data: client_pb2.ListLeasesResponse) -> LeaseList:
116+
return cls(
117+
leases=list(map(Lease.from_protobuf, data.leases)),
118+
next_page_token=data.next_page_token,
119+
)
120+
121+
def dump_json(self):
122+
return self.model_dump_json(indent=4, by_alias=True)
123+
124+
def dump_yaml(self):
125+
return yaml.safe_dump(self.model_dump(mode="json", by_alias=True), indent=2)
126+
127+
51128
@dataclass(kw_only=True, slots=True)
52129
class ClientService:
53130
channel: Channel
@@ -81,3 +158,81 @@ async def ListExporters(
81158
)
82159
)
83160
return ExporterList.from_protobuf(exporters)
161+
162+
async def GetLease(self, *, namespace: str, name: str):
163+
lease = await self.stub.GetLease(
164+
client_pb2.GetLeaseRequest(
165+
name="namespaces/{}/leases/{}".format(namespace, name),
166+
)
167+
)
168+
return Lease.from_protobuf(lease)
169+
170+
async def ListLeases(
171+
self,
172+
*,
173+
namespace: str,
174+
page_size: int | None = None,
175+
page_token: str | None = None,
176+
filter: str | None = None,
177+
):
178+
leases = await self.stub.ListLeases(
179+
client_pb2.ListLeasesRequest(
180+
parent="namespaces/{}".format(namespace),
181+
page_size=page_size,
182+
page_token=page_token,
183+
filter=filter,
184+
)
185+
)
186+
return LeaseList.from_protobuf(leases)
187+
188+
async def CreateLease(
189+
self,
190+
*,
191+
namespace: str,
192+
selector: str,
193+
duration: timedelta,
194+
):
195+
duration_pb = duration_pb2.Duration()
196+
duration_pb.FromTimedelta(duration)
197+
198+
lease = await self.stub.CreateLease(
199+
client_pb2.CreateLeaseRequest(
200+
parent="namespaces/{}".format(namespace),
201+
lease=client_pb2.Lease(
202+
duration=duration_pb,
203+
selector=selector,
204+
),
205+
)
206+
)
207+
return Lease.from_protobuf(lease)
208+
209+
async def UpdateLease(
210+
self,
211+
*,
212+
namespace: str,
213+
name: str,
214+
duration: timedelta,
215+
):
216+
duration_pb = duration_pb2.Duration()
217+
duration_pb.FromTimedelta(duration)
218+
219+
update_mask = field_mask_pb2.FieldMask()
220+
update_mask.FromJsonString("duration")
221+
222+
lease = await self.stub.UpdateLease(
223+
client_pb2.UpdateLeaseRequest(
224+
lease=client_pb2.Lease(
225+
name="namespaces/{}/leases/{}".format(namespace, name),
226+
duration=duration_pb,
227+
),
228+
update_mask=update_mask,
229+
)
230+
)
231+
return Lease.from_protobuf(lease)
232+
233+
async def DeleteLease(self, *, namespace: str, name: str):
234+
await self.stub.DeleteLease(
235+
client_pb2.DeleteLeaseRequest(
236+
name="namespaces/{}/leases/{}".format(namespace, name),
237+
)
238+
)

0 commit comments

Comments
 (0)