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

Commit 39131d7

Browse files
add start and end time options for get exporters CLI
add "--with start_time" and "--with end_time" to "get exporters" CLI output. They imply "--with leases". Default "--with leases" behavior was changed to only show the lease status
1 parent 17cf5d2 commit 39131d7

6 files changed

Lines changed: 217 additions & 33 deletions

File tree

packages/jumpstarter-cli/jumpstarter_cli/get.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,27 @@ def get():
2121
@opt_output_all
2222
@opt_comma_separated(
2323
"with",
24-
{"leases", "online"},
25-
help_text="Include fields: leases, online (comma-separated or repeated)"
24+
{"leases", "online", "start_time", "end_time"},
25+
help_text="Include fields: leases, online, start_time, end_time (comma-separated or repeated)"
2626
)
2727
@handle_exceptions_with_reauthentication(relogin_client)
2828
def get_exporters(config, selector: str | None, output: OutputType, with_options: list[str]):
2929
"""
3030
Display one or many exporters
3131
"""
3232

33-
include_leases = "leases" in with_options
33+
include_start_time = "start_time" in with_options
34+
include_end_time = "end_time" in with_options
35+
# Implicitly enable leases if start_time or end_time is specified
36+
include_leases = "leases" in with_options or include_start_time or include_end_time
3437
include_online = "online" in with_options
35-
exporters = config.list_exporters(filter=selector, include_leases=include_leases, include_online=include_online)
38+
exporters = config.list_exporters(
39+
filter=selector,
40+
include_leases=include_leases,
41+
include_online=include_online,
42+
include_start_time=include_start_time,
43+
include_end_time=include_end_time,
44+
)
3645

3746
model_print(exporters, output)
3847

packages/jumpstarter-cli/jumpstarter_cli/get_test.py

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ class TestParseWith:
1414
@property
1515
def allowed_values(self):
1616
"""Allowed values for --with option"""
17-
return {"leases", "online"}
17+
return {"leases", "online", "start_time", "end_time"}
1818

1919
def test_single_option(self):
2020
"""Test parsing a single option"""
@@ -40,13 +40,19 @@ def test_invalid_options_raise_error(self):
4040
"""Test that invalid options raise click.BadParameter"""
4141
with pytest.raises(
4242
click.BadParameter,
43-
match="Invalid value\\(s\\) \\['unknown', 'invalid'\\]. Allowed values are: leases, online"
43+
match=(
44+
"Invalid value\\(s\\) \\['unknown', 'invalid'\\]. "
45+
"Allowed values are: end_time, leases, online, start_time"
46+
),
4447
):
4548
parse_comma_separated(None, None, "unknown,online,invalid", self.allowed_values)
4649

4750
with pytest.raises(
4851
click.BadParameter,
49-
match="Invalid value\\(s\\) \\['invalid'\\]. Allowed values are: leases, online"
52+
match=(
53+
"Invalid value\\(s\\) \\['invalid'\\]. "
54+
"Allowed values are: end_time, leases, online, start_time"
55+
),
5056
):
5157
parse_comma_separated(None, None, "online,invalid", self.allowed_values)
5258

@@ -180,6 +186,51 @@ def test_with_options_parsing_empty(self):
180186
assert include_leases is False
181187
assert include_online is False
182188

189+
def test_with_options_start_time_implies_leases(self):
190+
"""Test that 'start_time' in with_options implicitly enables leases"""
191+
with_options = ("start_time",)
192+
193+
include_start_time = "start_time" in with_options
194+
include_end_time = "end_time" in with_options
195+
# Implicitly enable leases if start_time or end_time is specified
196+
include_leases = "leases" in with_options or include_start_time or include_end_time
197+
include_online = "online" in with_options
198+
199+
assert include_leases is True
200+
assert include_start_time is True
201+
assert include_end_time is False
202+
assert include_online is False
203+
204+
def test_with_options_end_time_implies_leases(self):
205+
"""Test that 'end_time' in with_options implicitly enables leases"""
206+
with_options = ("end_time",)
207+
208+
include_start_time = "start_time" in with_options
209+
include_end_time = "end_time" in with_options
210+
# Implicitly enable leases if start_time or end_time is specified
211+
include_leases = "leases" in with_options or include_start_time or include_end_time
212+
include_online = "online" in with_options
213+
214+
assert include_leases is True
215+
assert include_start_time is False
216+
assert include_end_time is True
217+
assert include_online is False
218+
219+
def test_with_options_both_times_implies_leases(self):
220+
"""Test that both time options implicitly enable leases"""
221+
with_options = ("start_time", "end_time")
222+
223+
include_start_time = "start_time" in with_options
224+
include_end_time = "end_time" in with_options
225+
# Implicitly enable leases if start_time or end_time is specified
226+
include_leases = "leases" in with_options or include_start_time or include_end_time
227+
include_online = "online" in with_options
228+
229+
assert include_leases is True
230+
assert include_start_time is True
231+
assert include_end_time is True
232+
assert include_online is False
233+
183234
def test_with_options_parsing_unknown(self):
184235
"""Test that the parse_with function now validates and rejects unknown options"""
185236
# This test verifies that the new parse_with function would reject unknown options

packages/jumpstarter-cli/jumpstarter_cli/login.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ async def relogin_client(config: ClientConfigV1Alpha1):
149149
try:
150150
oidc = Config(issuer=issuer, client_id=client_id)
151151
tokens = await oidc.authorization_code_grant()
152+
print(tokens)
152153
config.token = tokens["access_token"]
153154
ClientConfigV1Alpha1.save(config) # ty: ignore[invalid-argument-type]
154155
except Exception as e:

packages/jumpstarter/jumpstarter/client/grpc.py

Lines changed: 45 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
class WithOptions:
2121
show_online: bool = False
2222
show_leases: bool = False
23+
show_start_time: bool = False
24+
show_end_time: bool = False
2325

2426

2527
def add_display_columns(table, options: WithOptions = None):
@@ -32,10 +34,15 @@ def add_display_columns(table, options: WithOptions = None):
3234
if options.show_leases:
3335
table.add_column("LEASED BY")
3436
table.add_column("LEASE STATUS")
35-
table.add_column("START TIME")
37+
if options.show_start_time:
38+
table.add_column("START TIME")
39+
if options.show_end_time:
40+
table.add_column("END TIME")
3641

3742

38-
def add_exporter_row(table, exporter, options: WithOptions = None, lease_info: tuple[str, str, str] | None = None):
43+
def add_exporter_row(
44+
table, exporter, options: WithOptions = None, lease_info: tuple[str, str, str, str] | None = None
45+
):
3946
if options is None:
4047
options = WithOptions()
4148
row_data = []
@@ -45,10 +52,15 @@ def add_exporter_row(table, exporter, options: WithOptions = None, lease_info: t
4552
row_data.append(",".join(("{}={}".format(k, v) for k, v in sorted(exporter.labels.items()))))
4653
if options.show_leases:
4754
if lease_info:
48-
lease_client, lease_status, start_time = lease_info
55+
lease_client, lease_status, start_time, end_time = lease_info
4956
else:
50-
lease_client, lease_status, start_time = "", "Available", ""
51-
row_data.extend([lease_client, lease_status, start_time])
57+
lease_client, lease_status, start_time, end_time = "", "Available", "", ""
58+
lease_row = [lease_client, lease_status]
59+
if options.show_start_time:
60+
lease_row.append(start_time)
61+
if options.show_end_time:
62+
lease_row.append(end_time)
63+
row_data.extend(lease_row)
5264

5365
table.add_row(*row_data)
5466

@@ -98,11 +110,14 @@ def rich_add_rows(self, table, options: WithOptions = None):
98110
lease_client = self.lease.client
99111
lease_status = self.lease.get_status()
100112
start_time = ""
101-
if self.lease.effective_begin_time:
113+
if options.show_start_time and self.lease.effective_begin_time:
102114
start_time = self.lease.effective_begin_time.strftime("%Y-%m-%d %H:%M:%S")
103-
lease_info = (lease_client, lease_status, start_time)
115+
end_time = ""
116+
if options.show_end_time and self.lease.effective_end_time:
117+
end_time = self.lease.effective_end_time.strftime("%Y-%m-%d %H:%M:%S")
118+
lease_info = (lease_client, lease_status, start_time, end_time)
104119
elif options and options.show_leases:
105-
lease_info = ("", "Available", "")
120+
lease_info = ("", "Available", "", "")
106121
add_exporter_row(table, self, options, lease_info)
107122

108123
def rich_add_names(self, names):
@@ -118,6 +133,7 @@ class Lease(BaseModel):
118133
exporter: str
119134
conditions: list[kubernetes_pb2.Condition]
120135
effective_begin_time: datetime | None = None
136+
effective_end_time: datetime | None = None
121137

122138
model_config = ConfigDict(
123139
arbitrary_types_allowed=True,
@@ -144,6 +160,12 @@ def from_protobuf(cls, data: client_pb2.Lease) -> Lease:
144160
tzinfo=datetime.now().astimezone().tzinfo,
145161
)
146162

163+
effective_end_time = None
164+
if data.effective_end_time:
165+
effective_end_time = data.effective_end_time.ToDatetime(
166+
tzinfo=datetime.now().astimezone().tzinfo,
167+
)
168+
147169
return cls(
148170
namespace=namespace,
149171
name=name,
@@ -152,6 +174,7 @@ def from_protobuf(cls, data: client_pb2.Lease) -> Lease:
152174
client=client,
153175
exporter=exporter,
154176
effective_begin_time=effective_begin_time,
177+
effective_end_time=effective_end_time,
155178
conditions=data.conditions,
156179
)
157180

@@ -197,6 +220,8 @@ class ExporterList(BaseModel):
197220
next_page_token: str | None = Field(exclude=True)
198221
include_online: bool = Field(default=False, exclude=True)
199222
include_leases: bool = Field(default=False, exclude=True)
223+
include_start_time: bool = Field(default=False, exclude=True)
224+
include_end_time: bool = Field(default=False, exclude=True)
200225

201226
@classmethod
202227
def from_protobuf(cls, data: client_pb2.ListExportersResponse) -> ExporterList:
@@ -206,11 +231,21 @@ def from_protobuf(cls, data: client_pb2.ListExportersResponse) -> ExporterList:
206231
)
207232

208233
def rich_add_columns(self, table):
209-
options = WithOptions(show_online=self.include_online, show_leases=self.include_leases)
234+
options = WithOptions(
235+
show_online=self.include_online,
236+
show_leases=self.include_leases,
237+
show_start_time=self.include_start_time,
238+
show_end_time=self.include_end_time,
239+
)
210240
Exporter.rich_add_columns(table, options)
211241

212242
def rich_add_rows(self, table):
213-
options = WithOptions(show_online=self.include_online, show_leases=self.include_leases)
243+
options = WithOptions(
244+
show_online=self.include_online,
245+
show_leases=self.include_leases,
246+
show_start_time=self.include_start_time,
247+
show_end_time=self.include_end_time,
248+
)
214249
for exporter in self.exporters:
215250
exporter.rich_add_rows(table, options)
216251

0 commit comments

Comments
 (0)