From 3f9449bf6e46268a98853b9c35817e702b16592d Mon Sep 17 00:00:00 2001 From: Michal Skrivanek Date: Fri, 10 Oct 2025 09:52:08 +0200 Subject: [PATCH 1/8] improve client's lease monitoring we monitor lease status too frequently (every 5s) which doesn't correspond to the default grpc keepalive which we just made longer. Let's lighten the load a bit and poll less frequent, once in 30s. Changed the info message about lease expiring soon to more friendly "ending in 5 minutes". Also, use effective_begin_time + effective_duration for end time calculation. Once scheduled leases are implemented these may differ from begin_time and duration. --- .../jumpstarter/jumpstarter/client/grpc.py | 8 +++++++- .../jumpstarter/jumpstarter/client/lease.py | 20 +++++++++---------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/packages/jumpstarter/jumpstarter/client/grpc.py b/packages/jumpstarter/jumpstarter/client/grpc.py index 97763e2f8..494da9392 100644 --- a/packages/jumpstarter/jumpstarter/client/grpc.py +++ b/packages/jumpstarter/jumpstarter/client/grpc.py @@ -114,6 +114,7 @@ class Lease(BaseModel): name: str selector: str duration: timedelta + effective_duration: timedelta | None = None client: str exporter: str conditions: list[kubernetes_pb2.Condition] @@ -138,8 +139,12 @@ def from_protobuf(cls, data: client_pb2.Lease) -> Lease: else: exporter = "" + effective_duration = None + if data.HasField("effective_duration"): + effective_duration = data.effective_duration.ToTimedelta() + effective_begin_time = None - if data.effective_begin_time: + if data.HasField("effective_begin_time"): effective_begin_time = data.effective_begin_time.ToDatetime( tzinfo=datetime.now().astimezone().tzinfo, ) @@ -149,6 +154,7 @@ def from_protobuf(cls, data: client_pb2.Lease) -> Lease: name=name, selector=data.selector, duration=data.duration.ToTimedelta(), + effective_duration=effective_duration, client=client, exporter=exporter, effective_begin_time=effective_begin_time, diff --git a/packages/jumpstarter/jumpstarter/client/lease.py b/packages/jumpstarter/jumpstarter/client/lease.py index 835019887..426e81143 100644 --- a/packages/jumpstarter/jumpstarter/client/lease.py +++ b/packages/jumpstarter/jumpstarter/client/lease.py @@ -248,23 +248,21 @@ async def _wait_for_ready_connection(self, path: str): @asynccontextmanager async def monitor_async(self, threshold: timedelta = timedelta(minutes=5)): async def _monitor(): + check_interval = 30 # seconds - check periodically for external lease changes while True: lease = await self.get() - # TODO: use effective_end_time as the authoritative source for lease end time - if lease.effective_begin_time: - end_time = lease.effective_begin_time + lease.duration - remain = end_time - datetime.now(tz=datetime.now().astimezone().tzinfo) + if lease.effective_begin_time and lease.effective_duration: + end_time = lease.effective_begin_time + lease.effective_duration + remain = end_time - datetime.now().astimezone() if remain < timedelta(0): # lease already expired, stopping monitor logger.info("Lease {} ended at {}".format(self.name, end_time)) break - elif remain < threshold: - # lease expiring soon, check again on expected expiration time in case it's extended - logger.info("Lease {} ending soon in {} at {}".format(self.name, remain, end_time)) - await sleep(threshold.total_seconds()) - else: - # lease still active, check again in 5 seconds - await sleep(5) + # Log once when entering the threshold window + if threshold - timedelta(seconds=check_interval) <= remain < threshold: + logger.info("Lease {} ending in {} minutes at {}".format( + self.name, int((remain.total_seconds() + 30) // 60), end_time)) + await sleep(min(remain.total_seconds(), check_interval)) else: await sleep(1) From 68f44f6635b5f43547b355e2c67fd6de733026cc Mon Sep 17 00:00:00 2001 From: Michal Skrivanek Date: Fri, 10 Oct 2025 17:12:00 +0200 Subject: [PATCH 2/8] support creating and updating lease's begin_time this allows to create scheduled leases that will start at the requested time. At begin_time the lease will start acquisition that may be delayed or fail if the selected exporter is not available, following the lease acquisition timeout setting. --- .../jumpstarter-cli/jumpstarter_cli/common.py | 30 ++++++++++- .../jumpstarter-cli/jumpstarter_cli/create.py | 9 ++-- .../jumpstarter-cli/jumpstarter_cli/update.py | 18 +++++-- .../jumpstarter/jumpstarter/client/grpc.py | 50 ++++++++++++++----- .../jumpstarter/jumpstarter/config/client.py | 11 ++-- 5 files changed, 91 insertions(+), 27 deletions(-) diff --git a/packages/jumpstarter-cli/jumpstarter_cli/common.py b/packages/jumpstarter-cli/jumpstarter_cli/common.py index 3f17f648e..a9a7f6994 100644 --- a/packages/jumpstarter-cli/jumpstarter_cli/common.py +++ b/packages/jumpstarter-cli/jumpstarter_cli/common.py @@ -1,4 +1,4 @@ -from datetime import timedelta +from datetime import datetime, timedelta from functools import partial import click @@ -44,3 +44,31 @@ def convert(self, value, param, ctx): See https://docs.rs/speedate/latest/speedate/ for details """, ) + + +class DateTimeParamType(click.ParamType): + name = "datetime" + + def convert(self, value, param, ctx): + if isinstance(value, datetime): + return value + + try: + return TypeAdapter(datetime).validate_python(value) + except ValueError: + self.fail(f"{value!r} is not a valid datetime", param, ctx) + + +DATETIME = DateTimeParamType() + +opt_begin_time = click.option( + "--begin-time", + "begin_time", + type=DATETIME, + default=None, + help=""" +Begin time for the lease in ISO 8601 format (e.g., 2024-01-01T12:00:00 or 2024-01-01T12:00:00Z). +If not specified, the lease tries to be acquired immediately. The lease duration always starts +at the actual time of acquisition. +""", +) diff --git a/packages/jumpstarter-cli/jumpstarter_cli/create.py b/packages/jumpstarter-cli/jumpstarter_cli/create.py index 0c5a64038..0dff98b53 100644 --- a/packages/jumpstarter-cli/jumpstarter_cli/create.py +++ b/packages/jumpstarter-cli/jumpstarter_cli/create.py @@ -1,4 +1,4 @@ -from datetime import timedelta +from datetime import datetime, timedelta import click from jumpstarter_cli_common.config import opt_config @@ -6,7 +6,7 @@ from jumpstarter_cli_common.opt import OutputType, opt_output_all from jumpstarter_cli_common.print import model_print -from .common import opt_duration_partial, opt_selector +from .common import opt_begin_time, opt_duration_partial, opt_selector from .login import relogin_client @@ -21,9 +21,10 @@ def create(): @opt_config(exporter=False) @opt_selector @opt_duration_partial(required=True) +@opt_begin_time @opt_output_all @handle_exceptions_with_reauthentication(relogin_client) -def create_lease(config, selector: str, duration: timedelta, output: OutputType): +def create_lease(config, selector: str, duration: timedelta, begin_time: datetime | None, output: OutputType): """ Create a lease @@ -49,6 +50,6 @@ def create_lease(config, selector: str, duration: timedelta, output: OutputType) """ - lease = config.create_lease(selector=selector, duration=duration) + lease = config.create_lease(selector=selector, duration=duration, begin_time=begin_time) model_print(lease, output) diff --git a/packages/jumpstarter-cli/jumpstarter_cli/update.py b/packages/jumpstarter-cli/jumpstarter_cli/update.py index d753a91ae..7b933f792 100644 --- a/packages/jumpstarter-cli/jumpstarter_cli/update.py +++ b/packages/jumpstarter-cli/jumpstarter_cli/update.py @@ -1,4 +1,4 @@ -from datetime import timedelta +from datetime import datetime, timedelta import click from jumpstarter_cli_common.config import opt_config @@ -6,7 +6,7 @@ from jumpstarter_cli_common.opt import OutputType, opt_output_all from jumpstarter_cli_common.print import model_print -from .common import opt_duration_partial +from .common import opt_begin_time, opt_duration_partial from .login import relogin_client @@ -20,14 +20,22 @@ def update(): @update.command(name="lease") @opt_config(exporter=False) @click.argument("name") -@opt_duration_partial(required=True) +@opt_duration_partial(required=False) +@opt_begin_time @opt_output_all @handle_exceptions_with_reauthentication(relogin_client) -def update_lease(config, name: str, duration: timedelta, output: OutputType): +def update_lease(config, name: str, duration: timedelta | None, begin_time: datetime | None, output: OutputType): """ Update a lease + + Update the duration and/or begin time of an existing lease. + At least one of --duration or --begin-time must be specified. + Updating the begin time of an already active lease is not allowed. """ - lease = config.update_lease(name, duration) + if duration is None and begin_time is None: + raise click.UsageError("At least one of --duration or --begin-time must be specified") + + lease = config.update_lease(name, duration=duration, begin_time=begin_time) model_print(lease, output) diff --git a/packages/jumpstarter/jumpstarter/client/grpc.py b/packages/jumpstarter/jumpstarter/client/grpc.py index 494da9392..13655d06b 100644 --- a/packages/jumpstarter/jumpstarter/client/grpc.py +++ b/packages/jumpstarter/jumpstarter/client/grpc.py @@ -7,7 +7,7 @@ from types import SimpleNamespace from typing import Any -from google.protobuf import duration_pb2, field_mask_pb2, json_format +from google.protobuf import duration_pb2, field_mask_pb2, json_format, timestamp_pb2 from grpc import ChannelConnectivity from grpc.aio import Channel from jumpstarter_protocol import client_pb2, client_pb2_grpc, jumpstarter_pb2_grpc, kubernetes_pb2, router_pb2_grpc @@ -346,18 +346,26 @@ async def CreateLease( *, selector: str, duration: timedelta, + begin_time: datetime | None = None, ): duration_pb = duration_pb2.Duration() duration_pb.FromTimedelta(duration) + lease_pb = client_pb2.Lease( + duration=duration_pb, + selector=selector, + ) + + if begin_time: + timestamp_pb = timestamp_pb2.Timestamp() + timestamp_pb.FromDatetime(begin_time) + lease_pb.begin_time.CopyFrom(timestamp_pb) + with translate_grpc_exceptions(): lease = await self.stub.CreateLease( client_pb2.CreateLeaseRequest( parent="namespaces/{}".format(self.namespace), - lease=client_pb2.Lease( - duration=duration_pb, - selector=selector, - ), + lease=lease_pb, ) ) return Lease.from_protobuf(lease) @@ -366,21 +374,37 @@ async def UpdateLease( self, *, name: str, - duration: timedelta, + duration: timedelta | None = None, + begin_time: datetime | None = None, ): - duration_pb = duration_pb2.Duration() - duration_pb.FromTimedelta(duration) + lease_pb = client_pb2.Lease( + name="namespaces/{}/leases/{}".format(self.namespace, name), + ) + + update_fields = [] + + if duration is not None: + duration_pb = duration_pb2.Duration() + duration_pb.FromTimedelta(duration) + lease_pb.duration.CopyFrom(duration_pb) + update_fields.append("duration") + + if begin_time is not None: + timestamp_pb = timestamp_pb2.Timestamp() + timestamp_pb.FromDatetime(begin_time) + lease_pb.begin_time.CopyFrom(timestamp_pb) + update_fields.append("begin_time") + + if not update_fields: + raise ValueError("At least one of duration or begin_time must be provided") update_mask = field_mask_pb2.FieldMask() - update_mask.FromJsonString("duration") + update_mask.FromJsonString(",".join(update_fields)) with translate_grpc_exceptions(): lease = await self.stub.UpdateLease( client_pb2.UpdateLeaseRequest( - lease=client_pb2.Lease( - name="namespaces/{}/leases/{}".format(self.namespace, name), - duration=duration_pb, - ), + lease=lease_pb, update_mask=update_mask, ) ) diff --git a/packages/jumpstarter/jumpstarter/config/client.py b/packages/jumpstarter/jumpstarter/config/client.py index fc4c3d1c2..155f407e4 100644 --- a/packages/jumpstarter/jumpstarter/config/client.py +++ b/packages/jumpstarter/jumpstarter/config/client.py @@ -3,7 +3,7 @@ import asyncio import os from contextlib import asynccontextmanager, contextmanager -from datetime import timedelta +from datetime import datetime, timedelta from functools import wraps from pathlib import Path from typing import Annotated, ClassVar, Literal, Optional, Self @@ -200,11 +200,13 @@ async def create_lease( self, selector: str, duration: timedelta, + begin_time: datetime | None = None, ): svc = ClientService(channel=await self.channel(), namespace=self.metadata.namespace) return await svc.CreateLease( selector=selector, duration=duration, + begin_time=begin_time, ) @_blocking_compat @@ -237,11 +239,12 @@ async def list_leases( @_handle_connection_error async def update_lease( self, - name, - duration: timedelta, + name: str, + duration: timedelta | None = None, + begin_time: datetime | None = None, ): svc = ClientService(channel=await self.channel(), namespace=self.metadata.namespace) - return await svc.UpdateLease(name=name, duration=duration) + return await svc.UpdateLease(name=name, duration=duration, begin_time=begin_time) @asynccontextmanager async def lease_async( From f1a1aec078d851fece65d735960cec52059bac01 Mon Sep 17 00:00:00 2001 From: Michal Skrivanek Date: Fri, 10 Oct 2025 17:44:54 +0200 Subject: [PATCH 3/8] show lease expected release time instead of start time "jmp get exporters --with leases" shows lease status and when is the lease expiring. That's more useful that current start time which doesn't tell you for how long it may be held. "jmp get leases" now shows lease begin time (the time it was acquired) in addition to duration. future/scheduled leases show their expected begin time and duration, currently active leases show actual begin time and actual duration so far. --- .../jumpstarter/jumpstarter/client/grpc.py | 47 ++++++++--- .../jumpstarter/client/grpc_test.py | 78 +++++++++++++++---- 2 files changed, 101 insertions(+), 24 deletions(-) diff --git a/packages/jumpstarter/jumpstarter/client/grpc.py b/packages/jumpstarter/jumpstarter/client/grpc.py index 13655d06b..f2c856bfb 100644 --- a/packages/jumpstarter/jumpstarter/client/grpc.py +++ b/packages/jumpstarter/jumpstarter/client/grpc.py @@ -32,7 +32,7 @@ def add_display_columns(table, options: WithOptions = None): if options.show_leases: table.add_column("LEASED BY") table.add_column("LEASE STATUS") - table.add_column("START TIME") + table.add_column("EXPECTED RELEASE") def add_exporter_row(table, exporter, options: WithOptions = None, lease_info: tuple[str, str, str] | None = None): @@ -45,10 +45,10 @@ def add_exporter_row(table, exporter, options: WithOptions = None, lease_info: t row_data.append(",".join(("{}={}".format(k, v) for k, v in sorted(exporter.labels.items())))) if options.show_leases: if lease_info: - lease_client, lease_status, start_time = lease_info + lease_client, lease_status, expected_release = lease_info else: - lease_client, lease_status, start_time = "", "Available", "" - row_data.extend([lease_client, lease_status, start_time]) + lease_client, lease_status, expected_release = "", "Available", "" + row_data.extend([lease_client, lease_status, expected_release]) table.add_row(*row_data) @@ -97,10 +97,15 @@ def rich_add_rows(self, table, options: WithOptions = None): if options and options.show_leases and self.lease: lease_client = self.lease.client lease_status = self.lease.get_status() - start_time = "" - if self.lease.effective_begin_time: - start_time = self.lease.effective_begin_time.strftime("%Y-%m-%d %H:%M:%S") - lease_info = (lease_client, lease_status, start_time) + expected_release = "" + if self.lease.effective_begin_time and self.lease.effective_duration: + release_time = self.lease.effective_begin_time + self.lease.effective_duration + expected_release = release_time.strftime("%Y-%m-%d %H:%M:%S") + elif self.lease.begin_time: + dur = self.lease.effective_duration or self.lease.duration + release_time = self.lease.begin_time + dur + expected_release = release_time.strftime("%Y-%m-%d %H:%M:%S") + lease_info = (lease_client, lease_status, expected_release) elif options and options.show_leases: lease_info = ("", "Available", "") add_exporter_row(table, self, options, lease_info) @@ -115,6 +120,7 @@ class Lease(BaseModel): selector: str duration: timedelta effective_duration: timedelta | None = None + begin_time: datetime | None = None client: str exporter: str conditions: list[kubernetes_pb2.Condition] @@ -143,6 +149,12 @@ def from_protobuf(cls, data: client_pb2.Lease) -> Lease: if data.HasField("effective_duration"): effective_duration = data.effective_duration.ToTimedelta() + begin_time = None + if data.HasField("begin_time"): + begin_time = data.begin_time.ToDatetime( + tzinfo=datetime.now().astimezone().tzinfo, + ) + effective_begin_time = None if data.HasField("effective_begin_time"): effective_begin_time = data.effective_begin_time.ToDatetime( @@ -155,6 +167,7 @@ def from_protobuf(cls, data: client_pb2.Lease) -> Lease: selector=data.selector, duration=data.duration.ToTimedelta(), effective_duration=effective_duration, + begin_time=begin_time, client=client, exporter=exporter, effective_begin_time=effective_begin_time, @@ -165,15 +178,31 @@ def from_protobuf(cls, data: client_pb2.Lease) -> Lease: def rich_add_columns(cls, table): table.add_column("NAME", no_wrap=True) table.add_column("SELECTOR") + table.add_column("BEGIN TIME") table.add_column("DURATION") table.add_column("CLIENT") table.add_column("EXPORTER") def rich_add_rows(self, table): + # Show effective_begin_time if active, otherwise show scheduled begin_time + begin_time = "" + if self.effective_begin_time: + begin_time = self.effective_begin_time.strftime("%Y-%m-%d %H:%M:%S") + elif self.begin_time: + begin_time = self.begin_time.strftime("%Y-%m-%d %H:%M:%S") + + # Show effective_duration if available, otherwise show requested duration + duration = "" + if self.effective_duration: + duration = str(self.effective_duration) + elif self.duration: + duration = str(self.duration) + table.add_row( self.name, self.selector, - str(self.duration), + begin_time, + duration, self.client, self.exporter, ) diff --git a/packages/jumpstarter/jumpstarter/client/grpc_test.py b/packages/jumpstarter/jumpstarter/client/grpc_test.py index f5f069ad9..276707234 100644 --- a/packages/jumpstarter/jumpstarter/client/grpc_test.py +++ b/packages/jumpstarter/jumpstarter/client/grpc_test.py @@ -1,4 +1,4 @@ -from datetime import datetime +from datetime import datetime, timedelta from io import StringIO from unittest.mock import Mock @@ -48,7 +48,7 @@ def test_with_leases_columns(self): add_display_columns(table, options) columns = [col.header for col in table.columns] - assert columns == ["NAME", "LABELS", "LEASED BY", "LEASE STATUS", "START TIME"] + assert columns == ["NAME", "LABELS", "LEASED BY", "LEASE STATUS", "EXPECTED RELEASE"] def test_with_all_columns(self): table = Table() @@ -56,7 +56,7 @@ def test_with_all_columns(self): add_display_columns(table, options) columns = [col.header for col in table.columns] - assert columns == ["NAME", "ONLINE", "LABELS", "LEASED BY", "LEASE STATUS", "START TIME"] + assert columns == ["NAME", "ONLINE", "LABELS", "LEASED BY", "LEASE STATUS", "EXPECTED RELEASE"] class TestAddExporterRow: @@ -91,7 +91,7 @@ def test_row_with_lease_info(self): add_exporter_row(table, exporter, options, lease_info) assert len(table.rows) == 1 - assert len(table.columns) == 5 # NAME, LABELS, LEASED BY, LEASE STATUS, START TIME + assert len(table.columns) == 5 # NAME, LABELS, LEASED BY, LEASE STATUS, EXPECTED RELEASE def test_row_with_lease_info_available(self): table = Table() @@ -115,15 +115,21 @@ def test_row_with_all_options(self): add_exporter_row(table, exporter, options, lease_info) assert len(table.rows) == 1 - assert len(table.columns) == 6 # NAME, ONLINE, LABELS, LEASED BY, LEASE STATUS, START TIME + assert len(table.columns) == 6 # NAME, ONLINE, LABELS, LEASED BY, LEASE STATUS, EXPECTED RELEASE class TestExporterList: - def create_test_lease(self, client="test-client", status="Active"): + def create_test_lease(self, client="test-client", status="Active", + effective_begin_time=datetime(2023, 1, 1, 10, 0, 0), + effective_duration=timedelta(hours=1), + begin_time=None, duration=timedelta(hours=1)): lease = Mock(spec=Lease) lease.client = client lease.get_status.return_value = status - lease.effective_begin_time = datetime(2023, 1, 1, 10, 0, 0) + lease.effective_begin_time = effective_begin_time + lease.effective_duration = effective_duration + lease.begin_time = begin_time + lease.duration = duration return lease def test_exporter_without_lease(self): @@ -175,7 +181,7 @@ def test_exporter_with_lease_display(self): exporter.rich_add_rows(table, options) assert len(table.rows) == 1 - assert len(table.columns) == 5 # NAME, LABELS, LEASED BY, LEASE STATUS, START TIME + assert len(table.columns) == 5 # NAME, LABELS, LEASED BY, LEASE STATUS, EXPECTED RELEASE # Test actual table content by rendering it console = Console(file=StringIO(), width=120) @@ -187,7 +193,7 @@ def test_exporter_with_lease_display(self): assert "type=device" in output assert "test-client" in output assert "Active" in output - assert "2023-01-01 10:00:00" in output + assert "2023-01-01 11:00:00" in output # Expected release: begin_time (10:00:00) + duration (1h) def test_exporter_without_lease_but_show_leases(self): exporter = Exporter( @@ -203,7 +209,7 @@ def test_exporter_without_lease_but_show_leases(self): exporter.rich_add_rows(table, options) assert len(table.rows) == 1 - assert len(table.columns) == 5 # NAME, LABELS, LEASED BY, LEASE STATUS, START TIME + assert len(table.columns) == 5 # NAME, LABELS, LEASED BY, LEASE STATUS, EXPECTED RELEASE # Test actual table content by rendering it console = Console(file=StringIO(), width=120) @@ -285,7 +291,7 @@ def test_exporter_all_features_display(self): exporter_offline_no_lease.rich_add_rows(table, options) assert len(table.rows) == 2 - assert len(table.columns) == 6 # NAME, ONLINE, LABELS, LEASED BY, LEASE STATUS, START TIME + assert len(table.columns) == 6 # NAME, ONLINE, LABELS, LEASED BY, LEASE STATUS, EXPECTED RELEASE # Test actual table content by rendering it console = Console(file=StringIO(), width=150) @@ -302,7 +308,7 @@ def test_exporter_all_features_display(self): assert "full-test-client" in output # Lease client assert "Active" in output # Lease status assert "Available" in output # Available status for no lease - assert "2023-01-01 10:00:00" in output # Lease start time + assert "2023-01-01 11:00:00" in output # Expected release time (begin_time + duration) def test_exporter_lease_info_extraction(self): """Test that lease information is correctly extracted from lease objects""" @@ -325,10 +331,13 @@ def test_exporter_lease_info_extraction(self): if options.show_leases and exporter.lease: lease_client = exporter.lease.client lease_status = exporter.lease.get_status() - start_time = exporter.lease.effective_begin_time.strftime("%Y-%m-%d %H:%M:%S") - lease_info = (lease_client, lease_status, start_time) + expected_release = "" + if exporter.lease.effective_begin_time and exporter.lease.effective_duration: + release_time = exporter.lease.effective_begin_time + exporter.lease.effective_duration + expected_release = release_time.strftime("%Y-%m-%d %H:%M:%S") + lease_info = (lease_client, lease_status, expected_release) - assert lease_info == ("my-client", "Expired", "2023-01-01 10:00:00") + assert lease_info == ("my-client", "Expired", "2023-01-01 11:00:00") def test_exporter_no_lease_info_extraction(self): """Test that default lease information is used when no lease exists""" @@ -351,4 +360,43 @@ def test_exporter_no_lease_info_extraction(self): lease_info = ("", "Available", "") assert lease_info == ("", "Available", "") + def test_exporter_scheduled_lease_expected_release(self): + """Test that scheduled leases show expected release time""" + lease = self.create_test_lease( + client="my-client", + status="Scheduled", + effective_begin_time=None, # Not started yet + effective_duration=None, # Not started yet + begin_time=datetime(2023, 1, 1, 10, 0, 0), + duration=timedelta(hours=1) + ) + exporter = Exporter( + namespace="default", + name="test-exporter", + labels={"type": "device"}, + online=True, + lease=lease + ) + + # Test the table display with scheduled lease + table = Table() + options = WithOptions(show_leases=True) + Exporter.rich_add_columns(table, options) + exporter.rich_add_rows(table, options) + + # Should have 5 columns: NAME, LABELS, LEASED BY, LEASE STATUS, EXPECTED RELEASE + assert len(table.columns) == 5 + assert len(table.rows) == 1 + + # Test actual table content by rendering it + console = Console(file=StringIO(), width=120) + console.print(table) + output = console.file.getvalue() + + # Verify the scheduled lease displays expected release time + assert "test-exporter" in output + assert "my-client" in output + assert "Scheduled" in output + assert "2023-01-01 11:00:00" in output # begin_time (10:00) + duration (1h) + From 97b31dc697b7c15c61b9bd44107b76abc4d3098d Mon Sep 17 00:00:00 2001 From: Michal Skrivanek Date: Mon, 13 Oct 2025 16:30:07 +0200 Subject: [PATCH 4/8] add pydantic validation and normalize durations to always include timezone info --- .../jumpstarter-cli/jumpstarter_cli/common.py | 22 +++-- .../jumpstarter_cli/common_test.py | 90 +++++++++++++++++++ 2 files changed, 104 insertions(+), 8 deletions(-) create mode 100644 packages/jumpstarter-cli/jumpstarter_cli/common_test.py diff --git a/packages/jumpstarter-cli/jumpstarter_cli/common.py b/packages/jumpstarter-cli/jumpstarter_cli/common.py index a9a7f6994..cd0968d3c 100644 --- a/packages/jumpstarter-cli/jumpstarter_cli/common.py +++ b/packages/jumpstarter-cli/jumpstarter_cli/common.py @@ -2,7 +2,7 @@ from functools import partial import click -from pydantic import TypeAdapter +from pydantic import TypeAdapter, ValidationError opt_selector = click.option( "-l", @@ -21,7 +21,7 @@ def convert(self, value, param, ctx): try: return TypeAdapter(timedelta).validate_python(value) - except ValueError: + except (ValueError, ValidationError): self.fail(f"{value!r} is not a valid duration", param, ctx) @@ -51,12 +51,18 @@ class DateTimeParamType(click.ParamType): def convert(self, value, param, ctx): if isinstance(value, datetime): - return value - - try: - return TypeAdapter(datetime).validate_python(value) - except ValueError: - self.fail(f"{value!r} is not a valid datetime", param, ctx) + dt = value + else: + try: + dt = TypeAdapter(datetime).validate_python(value) + except (ValueError, ValidationError): + self.fail(f"{value!r} is not a valid datetime", param, ctx) + + # Normalize naive datetimes to local timezone + if dt.tzinfo is None: + dt = dt.astimezone() + + return dt DATETIME = DateTimeParamType() diff --git a/packages/jumpstarter-cli/jumpstarter_cli/common_test.py b/packages/jumpstarter-cli/jumpstarter_cli/common_test.py new file mode 100644 index 000000000..0e94b5d55 --- /dev/null +++ b/packages/jumpstarter-cli/jumpstarter_cli/common_test.py @@ -0,0 +1,90 @@ +from datetime import datetime, timedelta, timezone + +import click +import pytest + +from jumpstarter_cli.common import DATETIME, DURATION, DateTimeParamType, DurationParamType + + +class TestDateTimeParamType: + """Test DateTimeParamType parameter parsing and normalization.""" + + def test_parse_iso8601_with_timezone(self): + """Test parsing ISO 8601 datetime with timezone.""" + dt = DATETIME.convert("2024-01-01T12:00:00Z", None, None) + assert dt.year == 2024 + assert dt.month == 1 + assert dt.day == 1 + assert dt.hour == 12 + assert dt.minute == 0 + assert dt.second == 0 + assert dt.tzinfo is not None + assert dt.tzinfo == timezone.utc + + def test_parse_iso8601_naive_gets_normalized(self): + """Test that naive datetime gets normalized to local timezone.""" + dt = DATETIME.convert("2024-01-01T12:00:00", None, None) + assert dt.year == 2024 + assert dt.month == 1 + assert dt.day == 1 + assert dt.hour == 12 + assert dt.minute == 0 + assert dt.second == 0 + # Should have been normalized to local timezone + assert dt.tzinfo is not None + + def test_pass_through_datetime_object_with_timezone(self): + """Test that datetime object with timezone passes through.""" + input_dt = datetime(2024, 1, 1, 12, 0, 0, tzinfo=timezone.utc) + dt = DATETIME.convert(input_dt, None, None) + assert dt == input_dt + assert dt.tzinfo == timezone.utc + + def test_pass_through_datetime_object_naive_gets_normalized(self): + """Test that naive datetime object gets normalized.""" + input_dt = datetime(2024, 1, 1, 12, 0, 0) # Naive + dt = DATETIME.convert(input_dt, None, None) + assert dt.year == 2024 + assert dt.month == 1 + assert dt.day == 1 + assert dt.hour == 12 + # Should have been normalized to local timezone + assert dt.tzinfo is not None + + def test_invalid_datetime_raises_click_exception(self): + """Test that invalid datetime string raises click exception.""" + param_type = DateTimeParamType() + with pytest.raises(click.BadParameter, match="is not a valid datetime"): + param_type.convert("not-a-datetime", None, None) + + +class TestDurationParamType: + """Test DurationParamType parameter parsing.""" + + def test_parse_iso8601_duration(self): + """Test parsing ISO 8601 duration.""" + td = DURATION.convert("PT1H30M", None, None) + assert td == timedelta(hours=1, minutes=30) + + def test_parse_time_format(self): + """Test parsing HH:MM:SS format.""" + td = DURATION.convert("01:30:00", None, None) + assert td == timedelta(hours=1, minutes=30) + + def test_parse_days_and_time(self): + """Test parsing 'D days, HH:MM:SS' format.""" + td = DURATION.convert("2 days, 01:30:00", None, None) + assert td == timedelta(days=2, hours=1, minutes=30) + + def test_pass_through_timedelta_object(self): + """Test that timedelta object passes through.""" + input_td = timedelta(hours=1, minutes=30) + td = DURATION.convert(input_td, None, None) + assert td == input_td + + def test_invalid_duration_raises_click_exception(self): + """Test that invalid duration string raises click exception.""" + param_type = DurationParamType() + with pytest.raises(click.BadParameter, match="is not a valid duration"): + param_type.convert("not-a-duration", None, None) + From 3abd8860fb3993c87e57a07281efdee5c9426dec Mon Sep 17 00:00:00 2001 From: Michal Skrivanek Date: Tue, 14 Oct 2025 19:09:16 +0200 Subject: [PATCH 5/8] use effective_end_time for lease calculations when available this accounts for ended leases as well. If present it has the actual end time of the lease. change column to RELEASE TIME to make sense for ended leases as well --- .../jumpstarter/jumpstarter/client/grpc.py | 42 ++++++++++++------- .../jumpstarter/client/grpc_test.py | 38 +++++++++++------ .../jumpstarter/jumpstarter/client/lease.py | 5 ++- 3 files changed, 57 insertions(+), 28 deletions(-) diff --git a/packages/jumpstarter/jumpstarter/client/grpc.py b/packages/jumpstarter/jumpstarter/client/grpc.py index f2c856bfb..2b4e1ce40 100644 --- a/packages/jumpstarter/jumpstarter/client/grpc.py +++ b/packages/jumpstarter/jumpstarter/client/grpc.py @@ -32,7 +32,7 @@ def add_display_columns(table, options: WithOptions = None): if options.show_leases: table.add_column("LEASED BY") table.add_column("LEASE STATUS") - table.add_column("EXPECTED RELEASE") + table.add_column("RELEASE TIME") def add_exporter_row(table, exporter, options: WithOptions = None, lease_info: tuple[str, str, str] | None = None): @@ -97,15 +97,19 @@ def rich_add_rows(self, table, options: WithOptions = None): if options and options.show_leases and self.lease: lease_client = self.lease.client lease_status = self.lease.get_status() - expected_release = "" - if self.lease.effective_begin_time and self.lease.effective_duration: - release_time = self.lease.effective_begin_time + self.lease.effective_duration - expected_release = release_time.strftime("%Y-%m-%d %H:%M:%S") + release_time = "" + if self.lease.effective_end_time: + # Ended: use actual end time + release_time = self.lease.effective_end_time.strftime("%Y-%m-%d %H:%M:%S") + elif self.lease.effective_begin_time: + # Active: calculate expected end + release_time = self.lease.effective_begin_time + self.lease.duration + release_time = release_time.strftime("%Y-%m-%d %H:%M:%S") elif self.lease.begin_time: - dur = self.lease.effective_duration or self.lease.duration - release_time = self.lease.begin_time + dur - expected_release = release_time.strftime("%Y-%m-%d %H:%M:%S") - lease_info = (lease_client, lease_status, expected_release) + # Scheduled: calculate expected end + release_time = self.lease.begin_time + self.lease.duration + release_time = release_time.strftime("%Y-%m-%d %H:%M:%S") + lease_info = (lease_client, lease_status, release_time) elif options and options.show_leases: lease_info = ("", "Available", "") add_exporter_row(table, self, options, lease_info) @@ -125,6 +129,7 @@ class Lease(BaseModel): exporter: str conditions: list[kubernetes_pb2.Condition] effective_begin_time: datetime | None = None + effective_end_time: datetime | None = None model_config = ConfigDict( arbitrary_types_allowed=True, @@ -161,6 +166,12 @@ def from_protobuf(cls, data: client_pb2.Lease) -> Lease: tzinfo=datetime.now().astimezone().tzinfo, ) + effective_end_time = None + if data.HasField("effective_end_time"): + effective_end_time = data.effective_end_time.ToDatetime( + tzinfo=datetime.now().astimezone().tzinfo, + ) + return cls( namespace=namespace, name=name, @@ -171,6 +182,7 @@ def from_protobuf(cls, data: client_pb2.Lease) -> Lease: client=client, exporter=exporter, effective_begin_time=effective_begin_time, + effective_end_time=effective_end_time, conditions=data.conditions, ) @@ -191,12 +203,8 @@ def rich_add_rows(self, table): elif self.begin_time: begin_time = self.begin_time.strftime("%Y-%m-%d %H:%M:%S") - # Show effective_duration if available, otherwise show requested duration - duration = "" - if self.effective_duration: - duration = str(self.effective_duration) - elif self.duration: - duration = str(self.duration) + # Show actual duration for ended leases, requested duration otherwise + duration = str(self.effective_duration if self.effective_end_time else self.duration or "") table.add_row( self.name, @@ -212,6 +220,10 @@ def rich_add_names(self, names): def get_status(self) -> str: """Get the lease status based on conditions""" + # Check if lease has ended (effective_end_time is set) + if self.effective_end_time: + return "Ended" + if not self.conditions: return "Unknown" diff --git a/packages/jumpstarter/jumpstarter/client/grpc_test.py b/packages/jumpstarter/jumpstarter/client/grpc_test.py index 276707234..8146c4933 100644 --- a/packages/jumpstarter/jumpstarter/client/grpc_test.py +++ b/packages/jumpstarter/jumpstarter/client/grpc_test.py @@ -48,7 +48,7 @@ def test_with_leases_columns(self): add_display_columns(table, options) columns = [col.header for col in table.columns] - assert columns == ["NAME", "LABELS", "LEASED BY", "LEASE STATUS", "EXPECTED RELEASE"] + assert columns == ["NAME", "LABELS", "LEASED BY", "LEASE STATUS", "RELEASE TIME"] def test_with_all_columns(self): table = Table() @@ -56,7 +56,7 @@ def test_with_all_columns(self): add_display_columns(table, options) columns = [col.header for col in table.columns] - assert columns == ["NAME", "ONLINE", "LABELS", "LEASED BY", "LEASE STATUS", "EXPECTED RELEASE"] + assert columns == ["NAME", "ONLINE", "LABELS", "LEASED BY", "LEASE STATUS", "RELEASE TIME"] class TestAddExporterRow: @@ -91,7 +91,7 @@ def test_row_with_lease_info(self): add_exporter_row(table, exporter, options, lease_info) assert len(table.rows) == 1 - assert len(table.columns) == 5 # NAME, LABELS, LEASED BY, LEASE STATUS, EXPECTED RELEASE + assert len(table.columns) == 5 # NAME, LABELS, LEASED BY, LEASE STATUS, RELEASE TIME def test_row_with_lease_info_available(self): table = Table() @@ -115,14 +115,15 @@ def test_row_with_all_options(self): add_exporter_row(table, exporter, options, lease_info) assert len(table.rows) == 1 - assert len(table.columns) == 6 # NAME, ONLINE, LABELS, LEASED BY, LEASE STATUS, EXPECTED RELEASE + assert len(table.columns) == 6 # NAME, ONLINE, LABELS, LEASED BY, LEASE STATUS, RELEASE TIME class TestExporterList: def create_test_lease(self, client="test-client", status="Active", effective_begin_time=datetime(2023, 1, 1, 10, 0, 0), effective_duration=timedelta(hours=1), - begin_time=None, duration=timedelta(hours=1)): + begin_time=None, duration=timedelta(hours=1), + effective_end_time=None): lease = Mock(spec=Lease) lease.client = client lease.get_status.return_value = status @@ -130,6 +131,7 @@ def create_test_lease(self, client="test-client", status="Active", lease.effective_duration = effective_duration lease.begin_time = begin_time lease.duration = duration + lease.effective_end_time = effective_end_time return lease def test_exporter_without_lease(self): @@ -181,7 +183,7 @@ def test_exporter_with_lease_display(self): exporter.rich_add_rows(table, options) assert len(table.rows) == 1 - assert len(table.columns) == 5 # NAME, LABELS, LEASED BY, LEASE STATUS, EXPECTED RELEASE + assert len(table.columns) == 5 # NAME, LABELS, LEASED BY, LEASE STATUS, RELEASE TIME # Test actual table content by rendering it console = Console(file=StringIO(), width=120) @@ -209,7 +211,7 @@ def test_exporter_without_lease_but_show_leases(self): exporter.rich_add_rows(table, options) assert len(table.rows) == 1 - assert len(table.columns) == 5 # NAME, LABELS, LEASED BY, LEASE STATUS, EXPECTED RELEASE + assert len(table.columns) == 5 # NAME, LABELS, LEASED BY, LEASE STATUS, RELEASE TIME # Test actual table content by rendering it console = Console(file=StringIO(), width=120) @@ -291,7 +293,7 @@ def test_exporter_all_features_display(self): exporter_offline_no_lease.rich_add_rows(table, options) assert len(table.rows) == 2 - assert len(table.columns) == 6 # NAME, ONLINE, LABELS, LEASED BY, LEASE STATUS, EXPECTED RELEASE + assert len(table.columns) == 6 # NAME, ONLINE, LABELS, LEASED BY, LEASE STATUS, RELEASE TIME # Test actual table content by rendering it console = Console(file=StringIO(), width=150) @@ -312,7 +314,11 @@ def test_exporter_all_features_display(self): def test_exporter_lease_info_extraction(self): """Test that lease information is correctly extracted from lease objects""" - lease = self.create_test_lease(client="my-client", status="Expired") + lease = self.create_test_lease( + client="my-client", + status="Expired", + effective_end_time=datetime(2023, 1, 1, 11, 0, 0) # Ended after 1 hour + ) exporter = Exporter( namespace="default", name="test-exporter", @@ -332,8 +338,16 @@ def test_exporter_lease_info_extraction(self): lease_client = exporter.lease.client lease_status = exporter.lease.get_status() expected_release = "" - if exporter.lease.effective_begin_time and exporter.lease.effective_duration: - release_time = exporter.lease.effective_begin_time + exporter.lease.effective_duration + if exporter.lease.effective_end_time: + # Ended: use actual end time + expected_release = exporter.lease.effective_end_time.strftime("%Y-%m-%d %H:%M:%S") + elif exporter.lease.effective_begin_time: + # Active: calculate expected end + release_time = exporter.lease.effective_begin_time + exporter.lease.duration + expected_release = release_time.strftime("%Y-%m-%d %H:%M:%S") + elif exporter.lease.begin_time: + # Scheduled: calculate expected end + release_time = exporter.lease.begin_time + exporter.lease.duration expected_release = release_time.strftime("%Y-%m-%d %H:%M:%S") lease_info = (lease_client, lease_status, expected_release) @@ -384,7 +398,7 @@ def test_exporter_scheduled_lease_expected_release(self): Exporter.rich_add_columns(table, options) exporter.rich_add_rows(table, options) - # Should have 5 columns: NAME, LABELS, LEASED BY, LEASE STATUS, EXPECTED RELEASE + # Should have 5 columns: NAME, LABELS, LEASED BY, LEASE STATUS, RELEASE TIME assert len(table.columns) == 5 assert len(table.rows) == 1 diff --git a/packages/jumpstarter/jumpstarter/client/lease.py b/packages/jumpstarter/jumpstarter/client/lease.py index 426e81143..32c81fc6d 100644 --- a/packages/jumpstarter/jumpstarter/client/lease.py +++ b/packages/jumpstarter/jumpstarter/client/lease.py @@ -252,7 +252,10 @@ async def _monitor(): while True: lease = await self.get() if lease.effective_begin_time and lease.effective_duration: - end_time = lease.effective_begin_time + lease.effective_duration + if lease.effective_end_time: # already ended + end_time = lease.effective_end_time + else: + end_time = lease.effective_begin_time + lease.duration remain = end_time - datetime.now().astimezone() if remain < timedelta(0): # lease already expired, stopping monitor From 6d94924b3da234f83ab4afa13920896265e8a9ac Mon Sep 17 00:00:00 2001 From: Michal Skrivanek Date: Wed, 15 Oct 2025 13:09:28 +0200 Subject: [PATCH 6/8] add --all to "get leases" this indicates to the server that we want to see ended leases as well. Default behavior is to only get non-ended leases (pending or currently active) --- .../jumpstarter-cli/jumpstarter_cli/get.py | 11 +- .../jumpstarter_cli/get_test.py | 109 +++++++++++++++++- .../jumpstarter/jumpstarter/client/grpc.py | 2 + .../jumpstarter/jumpstarter/config/client.py | 2 + 4 files changed, 121 insertions(+), 3 deletions(-) diff --git a/packages/jumpstarter-cli/jumpstarter_cli/get.py b/packages/jumpstarter-cli/jumpstarter_cli/get.py index d62e6dee1..f7d1a041b 100644 --- a/packages/jumpstarter-cli/jumpstarter_cli/get.py +++ b/packages/jumpstarter-cli/jumpstarter_cli/get.py @@ -41,12 +41,19 @@ def get_exporters(config, selector: str | None, output: OutputType, with_options @opt_config(exporter=False) @opt_selector @opt_output_all +@click.option( + "--all", + "show_all", + is_flag=True, + default=False, + help="Show all leases including expired ones" +) @handle_exceptions_with_reauthentication(relogin_client) -def get_leases(config, selector: str | None, output: OutputType): +def get_leases(config, selector: str | None, output: OutputType, show_all: bool): """ Display one or many leases """ - leases = config.list_leases(filter=selector) + leases = config.list_leases(filter=selector, only_active=not show_all) model_print(leases, output) diff --git a/packages/jumpstarter-cli/jumpstarter_cli/get_test.py b/packages/jumpstarter-cli/jumpstarter_cli/get_test.py index d06a8b837..1713a8866 100644 --- a/packages/jumpstarter-cli/jumpstarter_cli/get_test.py +++ b/packages/jumpstarter-cli/jumpstarter_cli/get_test.py @@ -1,10 +1,11 @@ +from datetime import datetime, timedelta from unittest.mock import Mock import click import pytest from jumpstarter_cli_common.opt import parse_comma_separated -from jumpstarter.client.grpc import Exporter, ExporterList, Lease +from jumpstarter.client.grpc import Exporter, ExporterList, Lease, LeaseList from jumpstarter.config.client import ClientConfigV1Alpha1 @@ -239,3 +240,109 @@ def test_exporter_to_exporter_list_flow(self): assert exporter_list.exporters[1].name == "server-001" assert exporter_list.include_online is True assert exporter_list.include_leases is False + + +class TestGetLeasesLogic: + """Tests for get leases command logic (simulating server-side filtering)""" + + def create_test_lease(self, namespace="default", name="lease-1", status="In-Use", + effective_begin_time=None, effective_end_time=None, + duration=timedelta(hours=1)): + """Create a mock lease for testing""" + lease = Mock(spec=Lease) + lease.namespace = namespace + lease.name = name + lease.client = "test-client" + lease.exporter = "test-exporter" + lease.get_status.return_value = status + lease.effective_begin_time = effective_begin_time + lease.effective_end_time = effective_end_time + lease.duration = duration + lease.effective_duration = timedelta(minutes=30) if effective_begin_time else None + lease.begin_time = None + return lease + + def test_only_active_excludes_expired_leases(self): + """Test that server returns only active leases when only_active=True""" + # When only_active=True, server returns only active lease + active_lease = self.create_test_lease( + name="active-lease", + status="In-Use", + effective_begin_time=datetime(2023, 1, 1, 10, 0, 0) + ) + + leases_from_server = LeaseList(leases=[active_lease], next_page_token=None) + + assert len(leases_from_server.leases) == 1 + assert leases_from_server.leases[0].name == "active-lease" + assert leases_from_server.leases[0].get_status() == "In-Use" + + def test_show_all_includes_expired_leases(self): + """Test that server returns all leases including expired when only_active=False""" + # When only_active=False, server returns both active and expired + active_lease = self.create_test_lease( + name="active-lease", + status="In-Use", + effective_begin_time=datetime(2023, 1, 1, 10, 0, 0) + ) + expired_lease = self.create_test_lease( + name="expired-lease", + status="Expired", + effective_begin_time=datetime(2023, 1, 1, 8, 0, 0), + effective_end_time=datetime(2023, 1, 1, 9, 0, 0) + ) + + leases_from_server = LeaseList(leases=[active_lease, expired_lease], next_page_token=None) + + assert len(leases_from_server.leases) == 2 + assert leases_from_server.leases[0].name == "active-lease" + assert leases_from_server.leases[1].name == "expired-lease" + + def test_multiple_active_leases_returned(self): + """Test that server returns all active leases when only_active=True""" + # Server returns multiple active leases (different statuses but all non-expired) + lease1 = self.create_test_lease( + name="lease-1", + status="In-Use", + effective_begin_time=datetime(2023, 1, 1, 10, 0, 0) + ) + lease2 = self.create_test_lease( + name="lease-2", + status="Waiting", + effective_begin_time=datetime(2023, 1, 1, 11, 0, 0) + ) + lease3 = self.create_test_lease( + name="lease-3", + status="In-Use", + effective_begin_time=datetime(2023, 1, 1, 12, 0, 0) + ) + + leases_from_server = LeaseList(leases=[lease1, lease2, lease3], next_page_token=None) + + assert len(leases_from_server.leases) == 3 + assert all(lease.get_status() != "Expired" for lease in leases_from_server.leases) + + def test_all_expired_when_show_all(self): + """Test that server can return only expired leases when only_active=False""" + # When only_active=False and all leases happen to be expired + expired1 = self.create_test_lease( + name="expired-1", + status="Expired", + effective_end_time=datetime(2023, 1, 1, 8, 0, 0) + ) + expired2 = self.create_test_lease( + name="expired-2", + status="Expired", + effective_end_time=datetime(2023, 1, 1, 9, 0, 0) + ) + + leases_from_server = LeaseList(leases=[expired1, expired2], next_page_token=None) + + assert len(leases_from_server.leases) == 2 + assert all(lease.get_status() == "Expired" for lease in leases_from_server.leases) + + def test_empty_lease_list(self): + """Test that server can return empty lease list""" + leases_from_server = LeaseList(leases=[], next_page_token=None) + + assert len(leases_from_server.leases) == 0 diff --git a/packages/jumpstarter/jumpstarter/client/grpc.py b/packages/jumpstarter/jumpstarter/client/grpc.py index 2b4e1ce40..fc6f52527 100644 --- a/packages/jumpstarter/jumpstarter/client/grpc.py +++ b/packages/jumpstarter/jumpstarter/client/grpc.py @@ -370,6 +370,7 @@ async def ListLeases( page_size: int | None = None, page_token: str | None = None, filter: str | None = None, + only_active: bool = True, ): with translate_grpc_exceptions(): leases = await self.stub.ListLeases( @@ -378,6 +379,7 @@ async def ListLeases( page_size=page_size, page_token=page_token, filter=filter, + only_active=only_active, ) ) return LeaseList.from_protobuf(leases) diff --git a/packages/jumpstarter/jumpstarter/config/client.py b/packages/jumpstarter/jumpstarter/config/client.py index 155f407e4..8cb24c504 100644 --- a/packages/jumpstarter/jumpstarter/config/client.py +++ b/packages/jumpstarter/jumpstarter/config/client.py @@ -227,12 +227,14 @@ async def list_leases( page_size: int | None = None, page_token: str | None = None, filter: str | None = None, + only_active: bool = True, ): svc = ClientService(channel=await self.channel(), namespace=self.metadata.namespace) return await svc.ListLeases( page_size=page_size, page_token=page_token, filter=filter, + only_active=only_active, ) @_blocking_compat From 160db624353c4883b862470b004e3983d7984b19 Mon Sep 17 00:00:00 2001 From: Michal Skrivanek Date: Wed, 15 Oct 2025 13:14:25 +0200 Subject: [PATCH 7/8] rename and fix protobuf generation Makefile target Use "make protobuf-gen" to be consistent with controller. In python the imports need to be post-processed, unfortunately. --- Makefile | 16 +++++++++++++--- buf.gen.yaml | 1 + 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 49528a783..5fb033fe2 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ help: @echo "" @echo "Build targets:" @echo " build - Build all packages" - @echo " generate - Generate code from protobuf definitions" + @echo " protobuf-gen - Generate code from protobuf definitions" @echo " sync - Sync all packages and extras" @echo "" @echo "Documentation targets:" @@ -75,8 +75,18 @@ pkg-ty-all: $(addprefix pkg-ty-,$(PKG_TARGETS)) build: uv build --all --out-dir dist -generate: - buf generate +protobuf-gen: + podman run --volume "$(shell pwd):/workspace" --workdir /workspace docker.io/bufbuild/buf:latest generate + # Fix Python imports: convert absolute imports to relative imports + # In jumpstarter/client/v1: from jumpstarter.v1 import -> from ...v1 import + find packages/jumpstarter-protocol/jumpstarter_protocol/jumpstarter/client/v1 -name "*_pb2*.py" -type f -exec sed -i.bak \ + -e 's|^from jumpstarter\.v1 import|from ...v1 import|g' \ + -e 's|^from jumpstarter\.client\.v1 import|from . import|g' \ + {} \; -exec rm {}.bak \; + # In jumpstarter/v1: from jumpstarter.v1 import -> from . import + find packages/jumpstarter-protocol/jumpstarter_protocol/jumpstarter/v1 -name "*_pb2*.py" -type f -exec sed -i.bak \ + -e 's|^from jumpstarter\.v1 import|from . import|g' \ + {} \; -exec rm {}.bak \; sync: uv sync --all-packages --all-extras diff --git a/buf.gen.yaml b/buf.gen.yaml index a9d577519..fce4d534c 100644 --- a/buf.gen.yaml +++ b/buf.gen.yaml @@ -8,4 +8,5 @@ plugins: out: ./packages/jumpstarter-protocol/jumpstarter_protocol inputs: - git_repo: https://github.com/jumpstarter-dev/jumpstarter-protocol.git + branch: main subdir: proto From b2ce5907d29efde591516b8de390359bd3a38b32 Mon Sep 17 00:00:00 2001 From: Michal Skrivanek Date: Thu, 16 Oct 2025 10:32:23 +0200 Subject: [PATCH 8/8] Regenerate protobuf --- .../jumpstarter/client/v1/client_pb2.py | 51 ++++++++++--------- .../jumpstarter/v1/common_pb2.py | 5 +- .../jumpstarter/v1/jumpstarter_pb2.py | 5 +- .../jumpstarter/v1/kubernetes_pb2.py | 5 +- .../jumpstarter/v1/router_pb2.py | 5 +- 5 files changed, 38 insertions(+), 33 deletions(-) diff --git a/packages/jumpstarter-protocol/jumpstarter_protocol/jumpstarter/client/v1/client_pb2.py b/packages/jumpstarter-protocol/jumpstarter_protocol/jumpstarter/client/v1/client_pb2.py index be56f7f16..8cc530733 100644 --- a/packages/jumpstarter-protocol/jumpstarter_protocol/jumpstarter/client/v1/client_pb2.py +++ b/packages/jumpstarter-protocol/jumpstarter_protocol/jumpstarter/client/v1/client_pb2.py @@ -34,13 +34,14 @@ from ...v1 import common_pb2 as jumpstarter_dot_v1_dot_common__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\"jumpstarter/client/v1/client.proto\x12\x15jumpstarter.client.v1\x1a\x1cgoogle/api/annotations.proto\x1a\x17google/api/client.proto\x1a\x1fgoogle/api/field_behavior.proto\x1a\x19google/api/resource.proto\x1a\x1egoogle/protobuf/duration.proto\x1a\x1bgoogle/protobuf/empty.proto\x1a google/protobuf/field_mask.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\x1fjumpstarter/v1/kubernetes.proto\x1a\x1bjumpstarter/v1/common.proto\"\xe0\x02\n\x08\x45xporter\x12\x17\n\x04name\x18\x01 \x01(\tB\x03\xe0\x41\x08R\x04name\x12\x43\n\x06labels\x18\x02 \x03(\x0b\x32+.jumpstarter.client.v1.Exporter.LabelsEntryR\x06labels\x12\x1d\n\x06online\x18\x03 \x01(\x08\x42\x05\x18\x01\xe0\x41\x03R\x06online\x12;\n\x06status\x18\x04 \x01(\x0e\x32\x1e.jumpstarter.v1.ExporterStatusB\x03\xe0\x41\x03R\x06status\x1a\x39\n\x0bLabelsEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n\x05value\x18\x02 \x01(\tR\x05value:\x02\x38\x01:_\xea\x41\\\n\x18jumpstarter.dev/Exporter\x12+namespaces/{namespace}/exporters/{exporter}*\texporters2\x08\x65xporter\"\xed\x06\n\x05Lease\x12\x17\n\x04name\x18\x01 \x01(\tB\x03\xe0\x41\x08R\x04name\x12\"\n\x08selector\x18\x02 \x01(\tB\x06\xe0\x41\x02\xe0\x41\x05R\x08selector\x12:\n\x08\x64uration\x18\x03 \x01(\x0b\x32\x19.google.protobuf.DurationB\x03\xe0\x41\x02R\x08\x64uration\x12M\n\x12\x65\x66\x66\x65\x63tive_duration\x18\x04 \x01(\x0b\x32\x19.google.protobuf.DurationB\x03\xe0\x41\x03R\x11\x65\x66\x66\x65\x63tiveDuration\x12>\n\nbegin_time\x18\x05 \x01(\x0b\x32\x1a.google.protobuf.TimestampH\x00R\tbeginTime\x88\x01\x01\x12V\n\x14\x65\x66\x66\x65\x63tive_begin_time\x18\x06 \x01(\x0b\x32\x1a.google.protobuf.TimestampB\x03\xe0\x41\x03H\x01R\x12\x65\x66\x66\x65\x63tiveBeginTime\x88\x01\x01\x12:\n\x08\x65nd_time\x18\x07 \x01(\x0b\x32\x1a.google.protobuf.TimestampH\x02R\x07\x65ndTime\x88\x01\x01\x12R\n\x12\x65\x66\x66\x65\x63tive_end_time\x18\x08 \x01(\x0b\x32\x1a.google.protobuf.TimestampB\x03\xe0\x41\x03H\x03R\x10\x65\x66\x66\x65\x63tiveEndTime\x88\x01\x01\x12;\n\x06\x63lient\x18\t \x01(\tB\x1e\xe0\x41\x03\xfa\x41\x18\n\x16jumpstarter.dev/ClientH\x04R\x06\x63lient\x88\x01\x01\x12\x41\n\x08\x65xporter\x18\n \x01(\tB \xe0\x41\x03\xfa\x41\x1a\n\x18jumpstarter.dev/ExporterH\x05R\x08\x65xporter\x88\x01\x01\x12>\n\nconditions\x18\x0b \x03(\x0b\x32\x19.jumpstarter.v1.ConditionB\x03\xe0\x41\x03R\nconditions:P\xea\x41M\n\x15jumpstarter.dev/Lease\x12%namespaces/{namespace}/leases/{lease}*\x06leases2\x05leaseB\r\n\x0b_begin_timeB\x17\n\x15_effective_begin_timeB\x0b\n\t_end_timeB\x15\n\x13_effective_end_timeB\t\n\x07_clientB\x0b\n\t_exporter\"J\n\x12GetExporterRequest\x12\x34\n\x04name\x18\x01 \x01(\tB \xe0\x41\x02\xfa\x41\x1a\n\x18jumpstarter.dev/ExporterR\x04name\"\xb3\x01\n\x14ListExportersRequest\x12\x38\n\x06parent\x18\x01 \x01(\tB \xe0\x41\x02\xfa\x41\x1a\x12\x18jumpstarter.dev/ExporterR\x06parent\x12 \n\tpage_size\x18\x02 \x01(\x05\x42\x03\xe0\x41\x01R\x08pageSize\x12\"\n\npage_token\x18\x03 \x01(\tB\x03\xe0\x41\x01R\tpageToken\x12\x1b\n\x06\x66ilter\x18\x04 \x01(\tB\x03\xe0\x41\x01R\x06\x66ilter\"~\n\x15ListExportersResponse\x12=\n\texporters\x18\x01 \x03(\x0b\x32\x1f.jumpstarter.client.v1.ExporterR\texporters\x12&\n\x0fnext_page_token\x18\x02 \x01(\tR\rnextPageToken\"D\n\x0fGetLeaseRequest\x12\x31\n\x04name\x18\x01 \x01(\tB\x1d\xe0\x41\x02\xfa\x41\x17\n\x15jumpstarter.dev/LeaseR\x04name\"\xad\x01\n\x11ListLeasesRequest\x12\x35\n\x06parent\x18\x01 \x01(\tB\x1d\xe0\x41\x02\xfa\x41\x17\x12\x15jumpstarter.dev/LeaseR\x06parent\x12 \n\tpage_size\x18\x02 \x01(\x05\x42\x03\xe0\x41\x01R\x08pageSize\x12\"\n\npage_token\x18\x03 \x01(\tB\x03\xe0\x41\x01R\tpageToken\x12\x1b\n\x06\x66ilter\x18\x04 \x01(\tB\x03\xe0\x41\x01R\x06\x66ilter\"r\n\x12ListLeasesResponse\x12\x34\n\x06leases\x18\x01 \x03(\x0b\x32\x1c.jumpstarter.client.v1.LeaseR\x06leases\x12&\n\x0fnext_page_token\x18\x02 \x01(\tR\rnextPageToken\"\xa4\x01\n\x12\x43reateLeaseRequest\x12\x35\n\x06parent\x18\x01 \x01(\tB\x1d\xe0\x41\x02\xfa\x41\x17\x12\x15jumpstarter.dev/LeaseR\x06parent\x12\x1e\n\x08lease_id\x18\x02 \x01(\tB\x03\xe0\x41\x01R\x07leaseId\x12\x37\n\x05lease\x18\x03 \x01(\x0b\x32\x1c.jumpstarter.client.v1.LeaseB\x03\xe0\x41\x02R\x05lease\"\x8f\x01\n\x12UpdateLeaseRequest\x12\x37\n\x05lease\x18\x01 \x01(\x0b\x32\x1c.jumpstarter.client.v1.LeaseB\x03\xe0\x41\x02R\x05lease\x12@\n\x0bupdate_mask\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.FieldMaskB\x03\xe0\x41\x01R\nupdateMask\"G\n\x12\x44\x65leteLeaseRequest\x12\x31\n\x04name\x18\x01 \x01(\tB\x1d\xe0\x41\x02\xfa\x41\x17\n\x15jumpstarter.dev/LeaseR\x04name2\xa7\x08\n\rClientService\x12\x8d\x01\n\x0bGetExporter\x12).jumpstarter.client.v1.GetExporterRequest\x1a\x1f.jumpstarter.client.v1.Exporter\"2\xda\x41\x04name\x82\xd3\xe4\x93\x02%\x12#/v1/{name=namespaces/*/exporters/*}\x12\xa0\x01\n\rListExporters\x12+.jumpstarter.client.v1.ListExportersRequest\x1a,.jumpstarter.client.v1.ListExportersResponse\"4\xda\x41\x06parent\x82\xd3\xe4\x93\x02%\x12#/v1/{parent=namespaces/*}/exporters\x12\x81\x01\n\x08GetLease\x12&.jumpstarter.client.v1.GetLeaseRequest\x1a\x1c.jumpstarter.client.v1.Lease\"/\xda\x41\x04name\x82\xd3\xe4\x93\x02\"\x12 /v1/{name=namespaces/*/leases/*}\x12\x94\x01\n\nListLeases\x12(.jumpstarter.client.v1.ListLeasesRequest\x1a).jumpstarter.client.v1.ListLeasesResponse\"1\xda\x41\x06parent\x82\xd3\xe4\x93\x02\"\x12 /v1/{parent=namespaces/*}/leases\x12\x9f\x01\n\x0b\x43reateLease\x12).jumpstarter.client.v1.CreateLeaseRequest\x1a\x1c.jumpstarter.client.v1.Lease\"G\xda\x41\x15parent,lease,lease_id\x82\xd3\xe4\x93\x02)\" /v1/{parent=namespaces/*}/leases:\x05lease\x12\xa1\x01\n\x0bUpdateLease\x12).jumpstarter.client.v1.UpdateLeaseRequest\x1a\x1c.jumpstarter.client.v1.Lease\"I\xda\x41\x11lease,update_mask\x82\xd3\xe4\x93\x02/2&/v1/{lease.name=namespaces/*/leases/*}:\x05lease\x12\x81\x01\n\x0b\x44\x65leteLease\x12).jumpstarter.client.v1.DeleteLeaseRequest\x1a\x16.google.protobuf.Empty\"/\xda\x41\x04name\x82\xd3\xe4\x93\x02\"* /v1/{name=namespaces/*/leases/*}b\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\"jumpstarter/client/v1/client.proto\x12\x15jumpstarter.client.v1\x1a\x1cgoogle/api/annotations.proto\x1a\x17google/api/client.proto\x1a\x1fgoogle/api/field_behavior.proto\x1a\x19google/api/resource.proto\x1a\x1egoogle/protobuf/duration.proto\x1a\x1bgoogle/protobuf/empty.proto\x1a google/protobuf/field_mask.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\x1fjumpstarter/v1/kubernetes.proto\x1a\x1bjumpstarter/v1/common.proto\"\xe0\x02\n\x08\x45xporter\x12\x17\n\x04name\x18\x01 \x01(\tB\x03\xe0\x41\x08R\x04name\x12\x43\n\x06labels\x18\x02 \x03(\x0b\x32+.jumpstarter.client.v1.Exporter.LabelsEntryR\x06labels\x12\x1d\n\x06online\x18\x03 \x01(\x08\x42\x05\x18\x01\xe0\x41\x03R\x06online\x12;\n\x06status\x18\x04 \x01(\x0e\x32\x1e.jumpstarter.v1.ExporterStatusB\x03\xe0\x41\x03R\x06status\x1a\x39\n\x0bLabelsEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n\x05value\x18\x02 \x01(\tR\x05value:\x02\x38\x01:_\xea\x41\\\n\x18jumpstarter.dev/Exporter\x12+namespaces/{namespace}/exporters/{exporter}*\texporters2\x08\x65xporter\"\xfa\x06\n\x05Lease\x12\x17\n\x04name\x18\x01 \x01(\tB\x03\xe0\x41\x08R\x04name\x12\"\n\x08selector\x18\x02 \x01(\tB\x06\xe0\x41\x02\xe0\x41\x05R\x08selector\x12:\n\x08\x64uration\x18\x03 \x01(\x0b\x32\x19.google.protobuf.DurationH\x00R\x08\x64uration\x88\x01\x01\x12M\n\x12\x65\x66\x66\x65\x63tive_duration\x18\x04 \x01(\x0b\x32\x19.google.protobuf.DurationB\x03\xe0\x41\x03R\x11\x65\x66\x66\x65\x63tiveDuration\x12>\n\nbegin_time\x18\x05 \x01(\x0b\x32\x1a.google.protobuf.TimestampH\x01R\tbeginTime\x88\x01\x01\x12V\n\x14\x65\x66\x66\x65\x63tive_begin_time\x18\x06 \x01(\x0b\x32\x1a.google.protobuf.TimestampB\x03\xe0\x41\x03H\x02R\x12\x65\x66\x66\x65\x63tiveBeginTime\x88\x01\x01\x12:\n\x08\x65nd_time\x18\x07 \x01(\x0b\x32\x1a.google.protobuf.TimestampH\x03R\x07\x65ndTime\x88\x01\x01\x12R\n\x12\x65\x66\x66\x65\x63tive_end_time\x18\x08 \x01(\x0b\x32\x1a.google.protobuf.TimestampB\x03\xe0\x41\x03H\x04R\x10\x65\x66\x66\x65\x63tiveEndTime\x88\x01\x01\x12;\n\x06\x63lient\x18\t \x01(\tB\x1e\xe0\x41\x03\xfa\x41\x18\n\x16jumpstarter.dev/ClientH\x05R\x06\x63lient\x88\x01\x01\x12\x41\n\x08\x65xporter\x18\n \x01(\tB \xe0\x41\x03\xfa\x41\x1a\n\x18jumpstarter.dev/ExporterH\x06R\x08\x65xporter\x88\x01\x01\x12>\n\nconditions\x18\x0b \x03(\x0b\x32\x19.jumpstarter.v1.ConditionB\x03\xe0\x41\x03R\nconditions:P\xea\x41M\n\x15jumpstarter.dev/Lease\x12%namespaces/{namespace}/leases/{lease}*\x06leases2\x05leaseB\x0b\n\t_durationB\r\n\x0b_begin_timeB\x17\n\x15_effective_begin_timeB\x0b\n\t_end_timeB\x15\n\x13_effective_end_timeB\t\n\x07_clientB\x0b\n\t_exporter\"J\n\x12GetExporterRequest\x12\x34\n\x04name\x18\x01 \x01(\tB \xe0\x41\x02\xfa\x41\x1a\n\x18jumpstarter.dev/ExporterR\x04name\"\xb3\x01\n\x14ListExportersRequest\x12\x38\n\x06parent\x18\x01 \x01(\tB \xe0\x41\x02\xfa\x41\x1a\x12\x18jumpstarter.dev/ExporterR\x06parent\x12 \n\tpage_size\x18\x02 \x01(\x05\x42\x03\xe0\x41\x01R\x08pageSize\x12\"\n\npage_token\x18\x03 \x01(\tB\x03\xe0\x41\x01R\tpageToken\x12\x1b\n\x06\x66ilter\x18\x04 \x01(\tB\x03\xe0\x41\x01R\x06\x66ilter\"~\n\x15ListExportersResponse\x12=\n\texporters\x18\x01 \x03(\x0b\x32\x1f.jumpstarter.client.v1.ExporterR\texporters\x12&\n\x0fnext_page_token\x18\x02 \x01(\tR\rnextPageToken\"D\n\x0fGetLeaseRequest\x12\x31\n\x04name\x18\x01 \x01(\tB\x1d\xe0\x41\x02\xfa\x41\x17\n\x15jumpstarter.dev/LeaseR\x04name\"\xe8\x01\n\x11ListLeasesRequest\x12\x35\n\x06parent\x18\x01 \x01(\tB\x1d\xe0\x41\x02\xfa\x41\x17\x12\x15jumpstarter.dev/LeaseR\x06parent\x12 \n\tpage_size\x18\x02 \x01(\x05\x42\x03\xe0\x41\x01R\x08pageSize\x12\"\n\npage_token\x18\x03 \x01(\tB\x03\xe0\x41\x01R\tpageToken\x12\x1b\n\x06\x66ilter\x18\x04 \x01(\tB\x03\xe0\x41\x01R\x06\x66ilter\x12)\n\x0bonly_active\x18\x05 \x01(\x08\x42\x03\xe0\x41\x01H\x00R\nonlyActive\x88\x01\x01\x42\x0e\n\x0c_only_active\"r\n\x12ListLeasesResponse\x12\x34\n\x06leases\x18\x01 \x03(\x0b\x32\x1c.jumpstarter.client.v1.LeaseR\x06leases\x12&\n\x0fnext_page_token\x18\x02 \x01(\tR\rnextPageToken\"\xa4\x01\n\x12\x43reateLeaseRequest\x12\x35\n\x06parent\x18\x01 \x01(\tB\x1d\xe0\x41\x02\xfa\x41\x17\x12\x15jumpstarter.dev/LeaseR\x06parent\x12\x1e\n\x08lease_id\x18\x02 \x01(\tB\x03\xe0\x41\x01R\x07leaseId\x12\x37\n\x05lease\x18\x03 \x01(\x0b\x32\x1c.jumpstarter.client.v1.LeaseB\x03\xe0\x41\x02R\x05lease\"\x8f\x01\n\x12UpdateLeaseRequest\x12\x37\n\x05lease\x18\x01 \x01(\x0b\x32\x1c.jumpstarter.client.v1.LeaseB\x03\xe0\x41\x02R\x05lease\x12@\n\x0bupdate_mask\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.FieldMaskB\x03\xe0\x41\x01R\nupdateMask\"G\n\x12\x44\x65leteLeaseRequest\x12\x31\n\x04name\x18\x01 \x01(\tB\x1d\xe0\x41\x02\xfa\x41\x17\n\x15jumpstarter.dev/LeaseR\x04name2\xa7\x08\n\rClientService\x12\x8d\x01\n\x0bGetExporter\x12).jumpstarter.client.v1.GetExporterRequest\x1a\x1f.jumpstarter.client.v1.Exporter\"2\xda\x41\x04name\x82\xd3\xe4\x93\x02%\x12#/v1/{name=namespaces/*/exporters/*}\x12\xa0\x01\n\rListExporters\x12+.jumpstarter.client.v1.ListExportersRequest\x1a,.jumpstarter.client.v1.ListExportersResponse\"4\xda\x41\x06parent\x82\xd3\xe4\x93\x02%\x12#/v1/{parent=namespaces/*}/exporters\x12\x81\x01\n\x08GetLease\x12&.jumpstarter.client.v1.GetLeaseRequest\x1a\x1c.jumpstarter.client.v1.Lease\"/\xda\x41\x04name\x82\xd3\xe4\x93\x02\"\x12 /v1/{name=namespaces/*/leases/*}\x12\x94\x01\n\nListLeases\x12(.jumpstarter.client.v1.ListLeasesRequest\x1a).jumpstarter.client.v1.ListLeasesResponse\"1\xda\x41\x06parent\x82\xd3\xe4\x93\x02\"\x12 /v1/{parent=namespaces/*}/leases\x12\x9f\x01\n\x0b\x43reateLease\x12).jumpstarter.client.v1.CreateLeaseRequest\x1a\x1c.jumpstarter.client.v1.Lease\"G\xda\x41\x15parent,lease,lease_id\x82\xd3\xe4\x93\x02)\" /v1/{parent=namespaces/*}/leases:\x05lease\x12\xa1\x01\n\x0bUpdateLease\x12).jumpstarter.client.v1.UpdateLeaseRequest\x1a\x1c.jumpstarter.client.v1.Lease\"I\xda\x41\x11lease,update_mask\x82\xd3\xe4\x93\x02/2&/v1/{lease.name=namespaces/*/leases/*}:\x05lease\x12\x81\x01\n\x0b\x44\x65leteLease\x12).jumpstarter.client.v1.DeleteLeaseRequest\x1a\x16.google.protobuf.Empty\"/\xda\x41\x04name\x82\xd3\xe4\x93\x02\"* /v1/{name=namespaces/*/leases/*}B\x9e\x01\n\x19\x63om.jumpstarter.client.v1B\x0b\x43lientProtoP\x01\xa2\x02\x03JCX\xaa\x02\x15Jumpstarter.Client.V1\xca\x02\x15Jumpstarter\\Client\\V1\xe2\x02!Jumpstarter\\Client\\V1\\GPBMetadata\xea\x02\x17Jumpstarter::Client::V1b\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'jumpstarter.client.v1.client_pb2', _globals) if not _descriptor._USE_C_DESCRIPTORS: - DESCRIPTOR._loaded_options = None + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\031com.jumpstarter.client.v1B\013ClientProtoP\001\242\002\003JCX\252\002\025Jumpstarter.Client.V1\312\002\025Jumpstarter\\Client\\V1\342\002!Jumpstarter\\Client\\V1\\GPBMetadata\352\002\027Jumpstarter::Client::V1' _globals['_EXPORTER_LABELSENTRY']._loaded_options = None _globals['_EXPORTER_LABELSENTRY']._serialized_options = b'8\001' _globals['_EXPORTER'].fields_by_name['name']._loaded_options = None @@ -55,8 +56,6 @@ _globals['_LEASE'].fields_by_name['name']._serialized_options = b'\340A\010' _globals['_LEASE'].fields_by_name['selector']._loaded_options = None _globals['_LEASE'].fields_by_name['selector']._serialized_options = b'\340A\002\340A\005' - _globals['_LEASE'].fields_by_name['duration']._loaded_options = None - _globals['_LEASE'].fields_by_name['duration']._serialized_options = b'\340A\002' _globals['_LEASE'].fields_by_name['effective_duration']._loaded_options = None _globals['_LEASE'].fields_by_name['effective_duration']._serialized_options = b'\340A\003' _globals['_LEASE'].fields_by_name['effective_begin_time']._loaded_options = None @@ -91,6 +90,8 @@ _globals['_LISTLEASESREQUEST'].fields_by_name['page_token']._serialized_options = b'\340A\001' _globals['_LISTLEASESREQUEST'].fields_by_name['filter']._loaded_options = None _globals['_LISTLEASESREQUEST'].fields_by_name['filter']._serialized_options = b'\340A\001' + _globals['_LISTLEASESREQUEST'].fields_by_name['only_active']._loaded_options = None + _globals['_LISTLEASESREQUEST'].fields_by_name['only_active']._serialized_options = b'\340A\001' _globals['_CREATELEASEREQUEST'].fields_by_name['parent']._loaded_options = None _globals['_CREATELEASEREQUEST'].fields_by_name['parent']._serialized_options = b'\340A\002\372A\027\022\025jumpstarter.dev/Lease' _globals['_CREATELEASEREQUEST'].fields_by_name['lease_id']._loaded_options = None @@ -122,25 +123,25 @@ _globals['_EXPORTER_LABELSENTRY']._serialized_start=565 _globals['_EXPORTER_LABELSENTRY']._serialized_end=622 _globals['_LEASE']._serialized_start=722 - _globals['_LEASE']._serialized_end=1599 - _globals['_GETEXPORTERREQUEST']._serialized_start=1601 - _globals['_GETEXPORTERREQUEST']._serialized_end=1675 - _globals['_LISTEXPORTERSREQUEST']._serialized_start=1678 - _globals['_LISTEXPORTERSREQUEST']._serialized_end=1857 - _globals['_LISTEXPORTERSRESPONSE']._serialized_start=1859 - _globals['_LISTEXPORTERSRESPONSE']._serialized_end=1985 - _globals['_GETLEASEREQUEST']._serialized_start=1987 - _globals['_GETLEASEREQUEST']._serialized_end=2055 - _globals['_LISTLEASESREQUEST']._serialized_start=2058 - _globals['_LISTLEASESREQUEST']._serialized_end=2231 - _globals['_LISTLEASESRESPONSE']._serialized_start=2233 - _globals['_LISTLEASESRESPONSE']._serialized_end=2347 - _globals['_CREATELEASEREQUEST']._serialized_start=2350 - _globals['_CREATELEASEREQUEST']._serialized_end=2514 - _globals['_UPDATELEASEREQUEST']._serialized_start=2517 - _globals['_UPDATELEASEREQUEST']._serialized_end=2660 - _globals['_DELETELEASEREQUEST']._serialized_start=2662 - _globals['_DELETELEASEREQUEST']._serialized_end=2733 - _globals['_CLIENTSERVICE']._serialized_start=2736 - _globals['_CLIENTSERVICE']._serialized_end=3799 + _globals['_LEASE']._serialized_end=1612 + _globals['_GETEXPORTERREQUEST']._serialized_start=1614 + _globals['_GETEXPORTERREQUEST']._serialized_end=1688 + _globals['_LISTEXPORTERSREQUEST']._serialized_start=1691 + _globals['_LISTEXPORTERSREQUEST']._serialized_end=1870 + _globals['_LISTEXPORTERSRESPONSE']._serialized_start=1872 + _globals['_LISTEXPORTERSRESPONSE']._serialized_end=1998 + _globals['_GETLEASEREQUEST']._serialized_start=2000 + _globals['_GETLEASEREQUEST']._serialized_end=2068 + _globals['_LISTLEASESREQUEST']._serialized_start=2071 + _globals['_LISTLEASESREQUEST']._serialized_end=2303 + _globals['_LISTLEASESRESPONSE']._serialized_start=2305 + _globals['_LISTLEASESRESPONSE']._serialized_end=2419 + _globals['_CREATELEASEREQUEST']._serialized_start=2422 + _globals['_CREATELEASEREQUEST']._serialized_end=2586 + _globals['_UPDATELEASEREQUEST']._serialized_start=2589 + _globals['_UPDATELEASEREQUEST']._serialized_end=2732 + _globals['_DELETELEASEREQUEST']._serialized_start=2734 + _globals['_DELETELEASEREQUEST']._serialized_end=2805 + _globals['_CLIENTSERVICE']._serialized_start=2808 + _globals['_CLIENTSERVICE']._serialized_end=3871 # @@protoc_insertion_point(module_scope) diff --git a/packages/jumpstarter-protocol/jumpstarter_protocol/jumpstarter/v1/common_pb2.py b/packages/jumpstarter-protocol/jumpstarter_protocol/jumpstarter/v1/common_pb2.py index 6349b917c..4da3a3146 100644 --- a/packages/jumpstarter-protocol/jumpstarter_protocol/jumpstarter/v1/common_pb2.py +++ b/packages/jumpstarter-protocol/jumpstarter_protocol/jumpstarter/v1/common_pb2.py @@ -24,13 +24,14 @@ -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1bjumpstarter/v1/common.proto\x12\x0ejumpstarter.v1*\xb6\x02\n\x0e\x45xporterStatus\x12\x1f\n\x1b\x45XPORTER_STATUS_UNSPECIFIED\x10\x00\x12\x1b\n\x17\x45XPORTER_STATUS_OFFLINE\x10\x01\x12\x1d\n\x19\x45XPORTER_STATUS_AVAILABLE\x10\x02\x12%\n!EXPORTER_STATUS_BEFORE_LEASE_HOOK\x10\x03\x12\x1f\n\x1b\x45XPORTER_STATUS_LEASE_READY\x10\x04\x12$\n EXPORTER_STATUS_AFTER_LEASE_HOOK\x10\x05\x12,\n(EXPORTER_STATUS_BEFORE_LEASE_HOOK_FAILED\x10\x06\x12+\n\'EXPORTER_STATUS_AFTER_LEASE_HOOK_FAILED\x10\x07*\x98\x01\n\tLogSource\x12\x1a\n\x16LOG_SOURCE_UNSPECIFIED\x10\x00\x12\x15\n\x11LOG_SOURCE_DRIVER\x10\x01\x12 \n\x1cLOG_SOURCE_BEFORE_LEASE_HOOK\x10\x02\x12\x1f\n\x1bLOG_SOURCE_AFTER_LEASE_HOOK\x10\x03\x12\x15\n\x11LOG_SOURCE_SYSTEM\x10\x04\x62\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1bjumpstarter/v1/common.proto\x12\x0ejumpstarter.v1*\xb6\x02\n\x0e\x45xporterStatus\x12\x1f\n\x1b\x45XPORTER_STATUS_UNSPECIFIED\x10\x00\x12\x1b\n\x17\x45XPORTER_STATUS_OFFLINE\x10\x01\x12\x1d\n\x19\x45XPORTER_STATUS_AVAILABLE\x10\x02\x12%\n!EXPORTER_STATUS_BEFORE_LEASE_HOOK\x10\x03\x12\x1f\n\x1b\x45XPORTER_STATUS_LEASE_READY\x10\x04\x12$\n EXPORTER_STATUS_AFTER_LEASE_HOOK\x10\x05\x12,\n(EXPORTER_STATUS_BEFORE_LEASE_HOOK_FAILED\x10\x06\x12+\n\'EXPORTER_STATUS_AFTER_LEASE_HOOK_FAILED\x10\x07*\x98\x01\n\tLogSource\x12\x1a\n\x16LOG_SOURCE_UNSPECIFIED\x10\x00\x12\x15\n\x11LOG_SOURCE_DRIVER\x10\x01\x12 \n\x1cLOG_SOURCE_BEFORE_LEASE_HOOK\x10\x02\x12\x1f\n\x1bLOG_SOURCE_AFTER_LEASE_HOOK\x10\x03\x12\x15\n\x11LOG_SOURCE_SYSTEM\x10\x04\x42z\n\x12\x63om.jumpstarter.v1B\x0b\x43ommonProtoP\x01\xa2\x02\x03JXX\xaa\x02\x0eJumpstarter.V1\xca\x02\x0eJumpstarter\\V1\xe2\x02\x1aJumpstarter\\V1\\GPBMetadata\xea\x02\x0fJumpstarter::V1b\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'jumpstarter.v1.common_pb2', _globals) if not _descriptor._USE_C_DESCRIPTORS: - DESCRIPTOR._loaded_options = None + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\022com.jumpstarter.v1B\013CommonProtoP\001\242\002\003JXX\252\002\016Jumpstarter.V1\312\002\016Jumpstarter\\V1\342\002\032Jumpstarter\\V1\\GPBMetadata\352\002\017Jumpstarter::V1' _globals['_EXPORTERSTATUS']._serialized_start=48 _globals['_EXPORTERSTATUS']._serialized_end=358 _globals['_LOGSOURCE']._serialized_start=361 diff --git a/packages/jumpstarter-protocol/jumpstarter_protocol/jumpstarter/v1/jumpstarter_pb2.py b/packages/jumpstarter-protocol/jumpstarter_protocol/jumpstarter/v1/jumpstarter_pb2.py index 10892f748..b735bcad6 100644 --- a/packages/jumpstarter-protocol/jumpstarter_protocol/jumpstarter/v1/jumpstarter_pb2.py +++ b/packages/jumpstarter-protocol/jumpstarter_protocol/jumpstarter/v1/jumpstarter_pb2.py @@ -30,13 +30,14 @@ from . import common_pb2 as jumpstarter_dot_v1_dot_common__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n jumpstarter/v1/jumpstarter.proto\x12\x0ejumpstarter.v1\x1a\x1egoogle/protobuf/duration.proto\x1a\x1bgoogle/protobuf/empty.proto\x1a\x1cgoogle/protobuf/struct.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\x1fjumpstarter/v1/kubernetes.proto\x1a\x1bjumpstarter/v1/common.proto\"\xd1\x01\n\x0fRegisterRequest\x12\x43\n\x06labels\x18\x01 \x03(\x0b\x32+.jumpstarter.v1.RegisterRequest.LabelsEntryR\x06labels\x12>\n\x07reports\x18\x02 \x03(\x0b\x32$.jumpstarter.v1.DriverInstanceReportR\x07reports\x1a\x39\n\x0bLabelsEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n\x05value\x18\x02 \x01(\tR\x05value:\x02\x38\x01\"\xd2\x03\n\x14\x44riverInstanceReport\x12\x12\n\x04uuid\x18\x01 \x01(\tR\x04uuid\x12$\n\x0bparent_uuid\x18\x02 \x01(\tH\x00R\nparentUuid\x88\x01\x01\x12H\n\x06labels\x18\x03 \x03(\x0b\x32\x30.jumpstarter.v1.DriverInstanceReport.LabelsEntryR\x06labels\x12%\n\x0b\x64\x65scription\x18\x04 \x01(\tH\x01R\x0b\x64\x65scription\x88\x01\x01\x12m\n\x13methods_description\x18\x05 \x03(\x0b\x32<.jumpstarter.v1.DriverInstanceReport.MethodsDescriptionEntryR\x12methodsDescription\x1a\x39\n\x0bLabelsEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n\x05value\x18\x02 \x01(\tR\x05value:\x02\x38\x01\x1a\x45\n\x17MethodsDescriptionEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n\x05value\x18\x02 \x01(\tR\x05value:\x02\x38\x01\x42\x0e\n\x0c_parent_uuidB\x0e\n\x0c_description\"&\n\x10RegisterResponse\x12\x12\n\x04uuid\x18\x01 \x01(\tR\x04uuid\"+\n\x11UnregisterRequest\x12\x16\n\x06reason\x18\x02 \x01(\tR\x06reason\"\x14\n\x12UnregisterResponse\".\n\rListenRequest\x12\x1d\n\nlease_name\x18\x01 \x01(\tR\tleaseName\"\\\n\x0eListenResponse\x12\'\n\x0frouter_endpoint\x18\x01 \x01(\tR\x0erouterEndpoint\x12!\n\x0crouter_token\x18\x02 \x01(\tR\x0brouterToken\"\x0f\n\rStatusRequest\"\x91\x01\n\x0eStatusResponse\x12\x16\n\x06leased\x18\x01 \x01(\x08R\x06leased\x12\"\n\nlease_name\x18\x02 \x01(\tH\x00R\tleaseName\x88\x01\x01\x12$\n\x0b\x63lient_name\x18\x03 \x01(\tH\x01R\nclientName\x88\x01\x01\x42\r\n\x0b_lease_nameB\x0e\n\x0c_client_name\",\n\x0b\x44ialRequest\x12\x1d\n\nlease_name\x18\x01 \x01(\tR\tleaseName\"Z\n\x0c\x44ialResponse\x12\'\n\x0frouter_endpoint\x18\x01 \x01(\tR\x0erouterEndpoint\x12!\n\x0crouter_token\x18\x02 \x01(\tR\x0brouterToken\"\xa1\x01\n\x12\x41uditStreamRequest\x12#\n\rexporter_uuid\x18\x01 \x01(\tR\x0c\x65xporterUuid\x12\x30\n\x14\x64river_instance_uuid\x18\x02 \x01(\tR\x12\x64riverInstanceUuid\x12\x1a\n\x08severity\x18\x03 \x01(\tR\x08severity\x12\x18\n\x07message\x18\x04 \x01(\tR\x07message\"x\n\x13ReportStatusRequest\x12\x36\n\x06status\x18\x01 \x01(\x0e\x32\x1e.jumpstarter.v1.ExporterStatusR\x06status\x12\x1d\n\x07message\x18\x02 \x01(\tH\x00R\x07message\x88\x01\x01\x42\n\n\x08_message\"\x16\n\x14ReportStatusResponse\"\xb8\x02\n\x11GetReportResponse\x12\x12\n\x04uuid\x18\x01 \x01(\tR\x04uuid\x12\x45\n\x06labels\x18\x02 \x03(\x0b\x32-.jumpstarter.v1.GetReportResponse.LabelsEntryR\x06labels\x12>\n\x07reports\x18\x03 \x03(\x0b\x32$.jumpstarter.v1.DriverInstanceReportR\x07reports\x12M\n\x15\x61lternative_endpoints\x18\x04 \x03(\x0b\x32\x18.jumpstarter.v1.EndpointR\x14\x61lternativeEndpoints\x1a\x39\n\x0bLabelsEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n\x05value\x18\x02 \x01(\tR\x05value:\x02\x38\x01\"\xa5\x01\n\x08\x45ndpoint\x12\x1a\n\x08\x65ndpoint\x18\x01 \x01(\tR\x08\x65ndpoint\x12 \n\x0b\x63\x65rtificate\x18\x02 \x01(\tR\x0b\x63\x65rtificate\x12-\n\x12\x63lient_certificate\x18\x03 \x01(\tR\x11\x63lientCertificate\x12,\n\x12\x63lient_private_key\x18\x04 \x01(\tR\x10\x63lientPrivateKey\"k\n\x11\x44riverCallRequest\x12\x12\n\x04uuid\x18\x01 \x01(\tR\x04uuid\x12\x16\n\x06method\x18\x02 \x01(\tR\x06method\x12*\n\x04\x61rgs\x18\x03 \x03(\x0b\x32\x16.google.protobuf.ValueR\x04\x61rgs\"X\n\x12\x44riverCallResponse\x12\x12\n\x04uuid\x18\x01 \x01(\tR\x04uuid\x12.\n\x06result\x18\x02 \x01(\x0b\x32\x16.google.protobuf.ValueR\x06result\"t\n\x1aStreamingDriverCallRequest\x12\x12\n\x04uuid\x18\x01 \x01(\tR\x04uuid\x12\x16\n\x06method\x18\x02 \x01(\tR\x06method\x12*\n\x04\x61rgs\x18\x03 \x03(\x0b\x32\x16.google.protobuf.ValueR\x04\x61rgs\"a\n\x1bStreamingDriverCallResponse\x12\x12\n\x04uuid\x18\x01 \x01(\tR\x04uuid\x12.\n\x06result\x18\x02 \x01(\x0b\x32\x16.google.protobuf.ValueR\x06result\"\xa0\x01\n\x11LogStreamResponse\x12\x12\n\x04uuid\x18\x01 \x01(\tR\x04uuid\x12\x1a\n\x08severity\x18\x02 \x01(\tR\x08severity\x12\x18\n\x07message\x18\x03 \x01(\tR\x07message\x12\x36\n\x06source\x18\x04 \x01(\x0e\x32\x19.jumpstarter.v1.LogSourceH\x00R\x06source\x88\x01\x01\x42\t\n\x07_source\"\x0e\n\x0cResetRequest\"\x0f\n\rResetResponse\"%\n\x0fGetLeaseRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\"\x93\x03\n\x10GetLeaseResponse\x12\x35\n\x08\x64uration\x18\x01 \x01(\x0b\x32\x19.google.protobuf.DurationR\x08\x64uration\x12\x39\n\x08selector\x18\x02 \x01(\x0b\x32\x1d.jumpstarter.v1.LabelSelectorR\x08selector\x12>\n\nbegin_time\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.TimestampH\x00R\tbeginTime\x88\x01\x01\x12:\n\x08\x65nd_time\x18\x04 \x01(\x0b\x32\x1a.google.protobuf.TimestampH\x01R\x07\x65ndTime\x88\x01\x01\x12(\n\rexporter_uuid\x18\x05 \x01(\tH\x02R\x0c\x65xporterUuid\x88\x01\x01\x12\x39\n\nconditions\x18\x06 \x03(\x0b\x32\x19.jumpstarter.v1.ConditionR\nconditionsB\r\n\x0b_begin_timeB\x0b\n\t_end_timeB\x10\n\x0e_exporter_uuid\"\x87\x01\n\x13RequestLeaseRequest\x12\x35\n\x08\x64uration\x18\x01 \x01(\x0b\x32\x19.google.protobuf.DurationR\x08\x64uration\x12\x39\n\x08selector\x18\x02 \x01(\x0b\x32\x1d.jumpstarter.v1.LabelSelectorR\x08selector\"*\n\x14RequestLeaseResponse\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\")\n\x13ReleaseLeaseRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\"\x16\n\x14ReleaseLeaseResponse\"\x13\n\x11ListLeasesRequest\"*\n\x12ListLeasesResponse\x12\x14\n\x05names\x18\x01 \x03(\tR\x05names\"\x12\n\x10GetStatusRequest\"v\n\x11GetStatusResponse\x12\x36\n\x06status\x18\x01 \x01(\x0e\x32\x1e.jumpstarter.v1.ExporterStatusR\x06status\x12\x1d\n\x07message\x18\x02 \x01(\tH\x00R\x07message\x88\x01\x01\x42\n\n\x08_message2\x92\x07\n\x11\x43ontrollerService\x12M\n\x08Register\x12\x1f.jumpstarter.v1.RegisterRequest\x1a .jumpstarter.v1.RegisterResponse\x12S\n\nUnregister\x12!.jumpstarter.v1.UnregisterRequest\x1a\".jumpstarter.v1.UnregisterResponse\x12Y\n\x0cReportStatus\x12#.jumpstarter.v1.ReportStatusRequest\x1a$.jumpstarter.v1.ReportStatusResponse\x12I\n\x06Listen\x12\x1d.jumpstarter.v1.ListenRequest\x1a\x1e.jumpstarter.v1.ListenResponse0\x01\x12I\n\x06Status\x12\x1d.jumpstarter.v1.StatusRequest\x1a\x1e.jumpstarter.v1.StatusResponse0\x01\x12\x41\n\x04\x44ial\x12\x1b.jumpstarter.v1.DialRequest\x1a\x1c.jumpstarter.v1.DialResponse\x12K\n\x0b\x41uditStream\x12\".jumpstarter.v1.AuditStreamRequest\x1a\x16.google.protobuf.Empty(\x01\x12M\n\x08GetLease\x12\x1f.jumpstarter.v1.GetLeaseRequest\x1a .jumpstarter.v1.GetLeaseResponse\x12Y\n\x0cRequestLease\x12#.jumpstarter.v1.RequestLeaseRequest\x1a$.jumpstarter.v1.RequestLeaseResponse\x12Y\n\x0cReleaseLease\x12#.jumpstarter.v1.ReleaseLeaseRequest\x1a$.jumpstarter.v1.ReleaseLeaseResponse\x12S\n\nListLeases\x12!.jumpstarter.v1.ListLeasesRequest\x1a\".jumpstarter.v1.ListLeasesResponse2\x82\x04\n\x0f\x45xporterService\x12\x46\n\tGetReport\x12\x16.google.protobuf.Empty\x1a!.jumpstarter.v1.GetReportResponse\x12S\n\nDriverCall\x12!.jumpstarter.v1.DriverCallRequest\x1a\".jumpstarter.v1.DriverCallResponse\x12p\n\x13StreamingDriverCall\x12*.jumpstarter.v1.StreamingDriverCallRequest\x1a+.jumpstarter.v1.StreamingDriverCallResponse0\x01\x12H\n\tLogStream\x12\x16.google.protobuf.Empty\x1a!.jumpstarter.v1.LogStreamResponse0\x01\x12\x44\n\x05Reset\x12\x1c.jumpstarter.v1.ResetRequest\x1a\x1d.jumpstarter.v1.ResetResponse\x12P\n\tGetStatus\x12 .jumpstarter.v1.GetStatusRequest\x1a!.jumpstarter.v1.GetStatusResponseb\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n jumpstarter/v1/jumpstarter.proto\x12\x0ejumpstarter.v1\x1a\x1egoogle/protobuf/duration.proto\x1a\x1bgoogle/protobuf/empty.proto\x1a\x1cgoogle/protobuf/struct.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\x1fjumpstarter/v1/kubernetes.proto\x1a\x1bjumpstarter/v1/common.proto\"\xd1\x01\n\x0fRegisterRequest\x12\x43\n\x06labels\x18\x01 \x03(\x0b\x32+.jumpstarter.v1.RegisterRequest.LabelsEntryR\x06labels\x12>\n\x07reports\x18\x02 \x03(\x0b\x32$.jumpstarter.v1.DriverInstanceReportR\x07reports\x1a\x39\n\x0bLabelsEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n\x05value\x18\x02 \x01(\tR\x05value:\x02\x38\x01\"\xd2\x03\n\x14\x44riverInstanceReport\x12\x12\n\x04uuid\x18\x01 \x01(\tR\x04uuid\x12$\n\x0bparent_uuid\x18\x02 \x01(\tH\x00R\nparentUuid\x88\x01\x01\x12H\n\x06labels\x18\x03 \x03(\x0b\x32\x30.jumpstarter.v1.DriverInstanceReport.LabelsEntryR\x06labels\x12%\n\x0b\x64\x65scription\x18\x04 \x01(\tH\x01R\x0b\x64\x65scription\x88\x01\x01\x12m\n\x13methods_description\x18\x05 \x03(\x0b\x32<.jumpstarter.v1.DriverInstanceReport.MethodsDescriptionEntryR\x12methodsDescription\x1a\x39\n\x0bLabelsEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n\x05value\x18\x02 \x01(\tR\x05value:\x02\x38\x01\x1a\x45\n\x17MethodsDescriptionEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n\x05value\x18\x02 \x01(\tR\x05value:\x02\x38\x01\x42\x0e\n\x0c_parent_uuidB\x0e\n\x0c_description\"&\n\x10RegisterResponse\x12\x12\n\x04uuid\x18\x01 \x01(\tR\x04uuid\"+\n\x11UnregisterRequest\x12\x16\n\x06reason\x18\x02 \x01(\tR\x06reason\"\x14\n\x12UnregisterResponse\".\n\rListenRequest\x12\x1d\n\nlease_name\x18\x01 \x01(\tR\tleaseName\"\\\n\x0eListenResponse\x12\'\n\x0frouter_endpoint\x18\x01 \x01(\tR\x0erouterEndpoint\x12!\n\x0crouter_token\x18\x02 \x01(\tR\x0brouterToken\"\x0f\n\rStatusRequest\"\x91\x01\n\x0eStatusResponse\x12\x16\n\x06leased\x18\x01 \x01(\x08R\x06leased\x12\"\n\nlease_name\x18\x02 \x01(\tH\x00R\tleaseName\x88\x01\x01\x12$\n\x0b\x63lient_name\x18\x03 \x01(\tH\x01R\nclientName\x88\x01\x01\x42\r\n\x0b_lease_nameB\x0e\n\x0c_client_name\",\n\x0b\x44ialRequest\x12\x1d\n\nlease_name\x18\x01 \x01(\tR\tleaseName\"Z\n\x0c\x44ialResponse\x12\'\n\x0frouter_endpoint\x18\x01 \x01(\tR\x0erouterEndpoint\x12!\n\x0crouter_token\x18\x02 \x01(\tR\x0brouterToken\"\xa1\x01\n\x12\x41uditStreamRequest\x12#\n\rexporter_uuid\x18\x01 \x01(\tR\x0c\x65xporterUuid\x12\x30\n\x14\x64river_instance_uuid\x18\x02 \x01(\tR\x12\x64riverInstanceUuid\x12\x1a\n\x08severity\x18\x03 \x01(\tR\x08severity\x12\x18\n\x07message\x18\x04 \x01(\tR\x07message\"x\n\x13ReportStatusRequest\x12\x36\n\x06status\x18\x01 \x01(\x0e\x32\x1e.jumpstarter.v1.ExporterStatusR\x06status\x12\x1d\n\x07message\x18\x02 \x01(\tH\x00R\x07message\x88\x01\x01\x42\n\n\x08_message\"\x16\n\x14ReportStatusResponse\"\xb8\x02\n\x11GetReportResponse\x12\x12\n\x04uuid\x18\x01 \x01(\tR\x04uuid\x12\x45\n\x06labels\x18\x02 \x03(\x0b\x32-.jumpstarter.v1.GetReportResponse.LabelsEntryR\x06labels\x12>\n\x07reports\x18\x03 \x03(\x0b\x32$.jumpstarter.v1.DriverInstanceReportR\x07reports\x12M\n\x15\x61lternative_endpoints\x18\x04 \x03(\x0b\x32\x18.jumpstarter.v1.EndpointR\x14\x61lternativeEndpoints\x1a\x39\n\x0bLabelsEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n\x05value\x18\x02 \x01(\tR\x05value:\x02\x38\x01\"\xa5\x01\n\x08\x45ndpoint\x12\x1a\n\x08\x65ndpoint\x18\x01 \x01(\tR\x08\x65ndpoint\x12 \n\x0b\x63\x65rtificate\x18\x02 \x01(\tR\x0b\x63\x65rtificate\x12-\n\x12\x63lient_certificate\x18\x03 \x01(\tR\x11\x63lientCertificate\x12,\n\x12\x63lient_private_key\x18\x04 \x01(\tR\x10\x63lientPrivateKey\"k\n\x11\x44riverCallRequest\x12\x12\n\x04uuid\x18\x01 \x01(\tR\x04uuid\x12\x16\n\x06method\x18\x02 \x01(\tR\x06method\x12*\n\x04\x61rgs\x18\x03 \x03(\x0b\x32\x16.google.protobuf.ValueR\x04\x61rgs\"X\n\x12\x44riverCallResponse\x12\x12\n\x04uuid\x18\x01 \x01(\tR\x04uuid\x12.\n\x06result\x18\x02 \x01(\x0b\x32\x16.google.protobuf.ValueR\x06result\"t\n\x1aStreamingDriverCallRequest\x12\x12\n\x04uuid\x18\x01 \x01(\tR\x04uuid\x12\x16\n\x06method\x18\x02 \x01(\tR\x06method\x12*\n\x04\x61rgs\x18\x03 \x03(\x0b\x32\x16.google.protobuf.ValueR\x04\x61rgs\"a\n\x1bStreamingDriverCallResponse\x12\x12\n\x04uuid\x18\x01 \x01(\tR\x04uuid\x12.\n\x06result\x18\x02 \x01(\x0b\x32\x16.google.protobuf.ValueR\x06result\"\xa0\x01\n\x11LogStreamResponse\x12\x12\n\x04uuid\x18\x01 \x01(\tR\x04uuid\x12\x1a\n\x08severity\x18\x02 \x01(\tR\x08severity\x12\x18\n\x07message\x18\x03 \x01(\tR\x07message\x12\x36\n\x06source\x18\x04 \x01(\x0e\x32\x19.jumpstarter.v1.LogSourceH\x00R\x06source\x88\x01\x01\x42\t\n\x07_source\"\x0e\n\x0cResetRequest\"\x0f\n\rResetResponse\"%\n\x0fGetLeaseRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\"\x93\x03\n\x10GetLeaseResponse\x12\x35\n\x08\x64uration\x18\x01 \x01(\x0b\x32\x19.google.protobuf.DurationR\x08\x64uration\x12\x39\n\x08selector\x18\x02 \x01(\x0b\x32\x1d.jumpstarter.v1.LabelSelectorR\x08selector\x12>\n\nbegin_time\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.TimestampH\x00R\tbeginTime\x88\x01\x01\x12:\n\x08\x65nd_time\x18\x04 \x01(\x0b\x32\x1a.google.protobuf.TimestampH\x01R\x07\x65ndTime\x88\x01\x01\x12(\n\rexporter_uuid\x18\x05 \x01(\tH\x02R\x0c\x65xporterUuid\x88\x01\x01\x12\x39\n\nconditions\x18\x06 \x03(\x0b\x32\x19.jumpstarter.v1.ConditionR\nconditionsB\r\n\x0b_begin_timeB\x0b\n\t_end_timeB\x10\n\x0e_exporter_uuid\"\x87\x01\n\x13RequestLeaseRequest\x12\x35\n\x08\x64uration\x18\x01 \x01(\x0b\x32\x19.google.protobuf.DurationR\x08\x64uration\x12\x39\n\x08selector\x18\x02 \x01(\x0b\x32\x1d.jumpstarter.v1.LabelSelectorR\x08selector\"*\n\x14RequestLeaseResponse\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\")\n\x13ReleaseLeaseRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\"\x16\n\x14ReleaseLeaseResponse\"\x13\n\x11ListLeasesRequest\"*\n\x12ListLeasesResponse\x12\x14\n\x05names\x18\x01 \x03(\tR\x05names\"\x12\n\x10GetStatusRequest\"v\n\x11GetStatusResponse\x12\x36\n\x06status\x18\x01 \x01(\x0e\x32\x1e.jumpstarter.v1.ExporterStatusR\x06status\x12\x1d\n\x07message\x18\x02 \x01(\tH\x00R\x07message\x88\x01\x01\x42\n\n\x08_message2\x92\x07\n\x11\x43ontrollerService\x12M\n\x08Register\x12\x1f.jumpstarter.v1.RegisterRequest\x1a .jumpstarter.v1.RegisterResponse\x12S\n\nUnregister\x12!.jumpstarter.v1.UnregisterRequest\x1a\".jumpstarter.v1.UnregisterResponse\x12Y\n\x0cReportStatus\x12#.jumpstarter.v1.ReportStatusRequest\x1a$.jumpstarter.v1.ReportStatusResponse\x12I\n\x06Listen\x12\x1d.jumpstarter.v1.ListenRequest\x1a\x1e.jumpstarter.v1.ListenResponse0\x01\x12I\n\x06Status\x12\x1d.jumpstarter.v1.StatusRequest\x1a\x1e.jumpstarter.v1.StatusResponse0\x01\x12\x41\n\x04\x44ial\x12\x1b.jumpstarter.v1.DialRequest\x1a\x1c.jumpstarter.v1.DialResponse\x12K\n\x0b\x41uditStream\x12\".jumpstarter.v1.AuditStreamRequest\x1a\x16.google.protobuf.Empty(\x01\x12M\n\x08GetLease\x12\x1f.jumpstarter.v1.GetLeaseRequest\x1a .jumpstarter.v1.GetLeaseResponse\x12Y\n\x0cRequestLease\x12#.jumpstarter.v1.RequestLeaseRequest\x1a$.jumpstarter.v1.RequestLeaseResponse\x12Y\n\x0cReleaseLease\x12#.jumpstarter.v1.ReleaseLeaseRequest\x1a$.jumpstarter.v1.ReleaseLeaseResponse\x12S\n\nListLeases\x12!.jumpstarter.v1.ListLeasesRequest\x1a\".jumpstarter.v1.ListLeasesResponse2\x82\x04\n\x0f\x45xporterService\x12\x46\n\tGetReport\x12\x16.google.protobuf.Empty\x1a!.jumpstarter.v1.GetReportResponse\x12S\n\nDriverCall\x12!.jumpstarter.v1.DriverCallRequest\x1a\".jumpstarter.v1.DriverCallResponse\x12p\n\x13StreamingDriverCall\x12*.jumpstarter.v1.StreamingDriverCallRequest\x1a+.jumpstarter.v1.StreamingDriverCallResponse0\x01\x12H\n\tLogStream\x12\x16.google.protobuf.Empty\x1a!.jumpstarter.v1.LogStreamResponse0\x01\x12\x44\n\x05Reset\x12\x1c.jumpstarter.v1.ResetRequest\x1a\x1d.jumpstarter.v1.ResetResponse\x12P\n\tGetStatus\x12 .jumpstarter.v1.GetStatusRequest\x1a!.jumpstarter.v1.GetStatusResponseB\x7f\n\x12\x63om.jumpstarter.v1B\x10JumpstarterProtoP\x01\xa2\x02\x03JXX\xaa\x02\x0eJumpstarter.V1\xca\x02\x0eJumpstarter\\V1\xe2\x02\x1aJumpstarter\\V1\\GPBMetadata\xea\x02\x0fJumpstarter::V1b\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'jumpstarter.v1.jumpstarter_pb2', _globals) if not _descriptor._USE_C_DESCRIPTORS: - DESCRIPTOR._loaded_options = None + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\022com.jumpstarter.v1B\020JumpstarterProtoP\001\242\002\003JXX\252\002\016Jumpstarter.V1\312\002\016Jumpstarter\\V1\342\002\032Jumpstarter\\V1\\GPBMetadata\352\002\017Jumpstarter::V1' _globals['_REGISTERREQUEST_LABELSENTRY']._loaded_options = None _globals['_REGISTERREQUEST_LABELSENTRY']._serialized_options = b'8\001' _globals['_DRIVERINSTANCEREPORT_LABELSENTRY']._loaded_options = None diff --git a/packages/jumpstarter-protocol/jumpstarter_protocol/jumpstarter/v1/kubernetes_pb2.py b/packages/jumpstarter-protocol/jumpstarter_protocol/jumpstarter/v1/kubernetes_pb2.py index fc7fcc649..d7e6db9b4 100644 --- a/packages/jumpstarter-protocol/jumpstarter_protocol/jumpstarter/v1/kubernetes_pb2.py +++ b/packages/jumpstarter-protocol/jumpstarter_protocol/jumpstarter/v1/kubernetes_pb2.py @@ -24,13 +24,14 @@ -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1fjumpstarter/v1/kubernetes.proto\x12\x0ejumpstarter.v1\"`\n\x18LabelSelectorRequirement\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12\x1a\n\x08operator\x18\x02 \x01(\tR\x08operator\x12\x16\n\x06values\x18\x03 \x03(\tR\x06values\"\xf9\x01\n\rLabelSelector\x12U\n\x11match_expressions\x18\x01 \x03(\x0b\x32(.jumpstarter.v1.LabelSelectorRequirementR\x10matchExpressions\x12Q\n\x0cmatch_labels\x18\x02 \x03(\x0b\x32..jumpstarter.v1.LabelSelector.MatchLabelsEntryR\x0bmatchLabels\x1a>\n\x10MatchLabelsEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n\x05value\x18\x02 \x01(\tR\x05value:\x02\x38\x01\"V\n\x04Time\x12\x1d\n\x07seconds\x18\x01 \x01(\x03H\x00R\x07seconds\x88\x01\x01\x12\x19\n\x05nanos\x18\x02 \x01(\x05H\x01R\x05nanos\x88\x01\x01\x42\n\n\x08_secondsB\x08\n\x06_nanos\"\xd6\x02\n\tCondition\x12\x17\n\x04type\x18\x01 \x01(\tH\x00R\x04type\x88\x01\x01\x12\x1b\n\x06status\x18\x02 \x01(\tH\x01R\x06status\x88\x01\x01\x12\x33\n\x12observedGeneration\x18\x03 \x01(\x03H\x02R\x12observedGeneration\x88\x01\x01\x12I\n\x12lastTransitionTime\x18\x04 \x01(\x0b\x32\x14.jumpstarter.v1.TimeH\x03R\x12lastTransitionTime\x88\x01\x01\x12\x1b\n\x06reason\x18\x05 \x01(\tH\x04R\x06reason\x88\x01\x01\x12\x1d\n\x07message\x18\x06 \x01(\tH\x05R\x07message\x88\x01\x01\x42\x07\n\x05_typeB\t\n\x07_statusB\x15\n\x13_observedGenerationB\x15\n\x13_lastTransitionTimeB\t\n\x07_reasonB\n\n\x08_messageb\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1fjumpstarter/v1/kubernetes.proto\x12\x0ejumpstarter.v1\"`\n\x18LabelSelectorRequirement\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12\x1a\n\x08operator\x18\x02 \x01(\tR\x08operator\x12\x16\n\x06values\x18\x03 \x03(\tR\x06values\"\xf9\x01\n\rLabelSelector\x12U\n\x11match_expressions\x18\x01 \x03(\x0b\x32(.jumpstarter.v1.LabelSelectorRequirementR\x10matchExpressions\x12Q\n\x0cmatch_labels\x18\x02 \x03(\x0b\x32..jumpstarter.v1.LabelSelector.MatchLabelsEntryR\x0bmatchLabels\x1a>\n\x10MatchLabelsEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n\x05value\x18\x02 \x01(\tR\x05value:\x02\x38\x01\"V\n\x04Time\x12\x1d\n\x07seconds\x18\x01 \x01(\x03H\x00R\x07seconds\x88\x01\x01\x12\x19\n\x05nanos\x18\x02 \x01(\x05H\x01R\x05nanos\x88\x01\x01\x42\n\n\x08_secondsB\x08\n\x06_nanos\"\xd6\x02\n\tCondition\x12\x17\n\x04type\x18\x01 \x01(\tH\x00R\x04type\x88\x01\x01\x12\x1b\n\x06status\x18\x02 \x01(\tH\x01R\x06status\x88\x01\x01\x12\x33\n\x12observedGeneration\x18\x03 \x01(\x03H\x02R\x12observedGeneration\x88\x01\x01\x12I\n\x12lastTransitionTime\x18\x04 \x01(\x0b\x32\x14.jumpstarter.v1.TimeH\x03R\x12lastTransitionTime\x88\x01\x01\x12\x1b\n\x06reason\x18\x05 \x01(\tH\x04R\x06reason\x88\x01\x01\x12\x1d\n\x07message\x18\x06 \x01(\tH\x05R\x07message\x88\x01\x01\x42\x07\n\x05_typeB\t\n\x07_statusB\x15\n\x13_observedGenerationB\x15\n\x13_lastTransitionTimeB\t\n\x07_reasonB\n\n\x08_messageB~\n\x12\x63om.jumpstarter.v1B\x0fKubernetesProtoP\x01\xa2\x02\x03JXX\xaa\x02\x0eJumpstarter.V1\xca\x02\x0eJumpstarter\\V1\xe2\x02\x1aJumpstarter\\V1\\GPBMetadata\xea\x02\x0fJumpstarter::V1b\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'jumpstarter.v1.kubernetes_pb2', _globals) if not _descriptor._USE_C_DESCRIPTORS: - DESCRIPTOR._loaded_options = None + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\022com.jumpstarter.v1B\017KubernetesProtoP\001\242\002\003JXX\252\002\016Jumpstarter.V1\312\002\016Jumpstarter\\V1\342\002\032Jumpstarter\\V1\\GPBMetadata\352\002\017Jumpstarter::V1' _globals['_LABELSELECTOR_MATCHLABELSENTRY']._loaded_options = None _globals['_LABELSELECTOR_MATCHLABELSENTRY']._serialized_options = b'8\001' _globals['_LABELSELECTORREQUIREMENT']._serialized_start=51 diff --git a/packages/jumpstarter-protocol/jumpstarter_protocol/jumpstarter/v1/router_pb2.py b/packages/jumpstarter-protocol/jumpstarter_protocol/jumpstarter/v1/router_pb2.py index 030a29cbb..0d86b315a 100644 --- a/packages/jumpstarter-protocol/jumpstarter_protocol/jumpstarter/v1/router_pb2.py +++ b/packages/jumpstarter-protocol/jumpstarter_protocol/jumpstarter/v1/router_pb2.py @@ -24,13 +24,14 @@ -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1bjumpstarter/v1/router.proto\x12\x0ejumpstarter.v1\"c\n\rStreamRequest\x12\x18\n\x07payload\x18\x01 \x01(\x0cR\x07payload\x12\x38\n\nframe_type\x18\x02 \x01(\x0e\x32\x19.jumpstarter.v1.FrameTypeR\tframeType\"d\n\x0eStreamResponse\x12\x18\n\x07payload\x18\x01 \x01(\x0cR\x07payload\x12\x38\n\nframe_type\x18\x02 \x01(\x0e\x32\x19.jumpstarter.v1.FrameTypeR\tframeType*g\n\tFrameType\x12\x13\n\x0f\x46RAME_TYPE_DATA\x10\x00\x12\x19\n\x15\x46RAME_TYPE_RST_STREAM\x10\x03\x12\x13\n\x0f\x46RAME_TYPE_PING\x10\x06\x12\x15\n\x11\x46RAME_TYPE_GOAWAY\x10\x07\x32\\\n\rRouterService\x12K\n\x06Stream\x12\x1d.jumpstarter.v1.StreamRequest\x1a\x1e.jumpstarter.v1.StreamResponse(\x01\x30\x01\x62\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1bjumpstarter/v1/router.proto\x12\x0ejumpstarter.v1\"c\n\rStreamRequest\x12\x18\n\x07payload\x18\x01 \x01(\x0cR\x07payload\x12\x38\n\nframe_type\x18\x02 \x01(\x0e\x32\x19.jumpstarter.v1.FrameTypeR\tframeType\"d\n\x0eStreamResponse\x12\x18\n\x07payload\x18\x01 \x01(\x0cR\x07payload\x12\x38\n\nframe_type\x18\x02 \x01(\x0e\x32\x19.jumpstarter.v1.FrameTypeR\tframeType*g\n\tFrameType\x12\x13\n\x0f\x46RAME_TYPE_DATA\x10\x00\x12\x19\n\x15\x46RAME_TYPE_RST_STREAM\x10\x03\x12\x13\n\x0f\x46RAME_TYPE_PING\x10\x06\x12\x15\n\x11\x46RAME_TYPE_GOAWAY\x10\x07\x32\\\n\rRouterService\x12K\n\x06Stream\x12\x1d.jumpstarter.v1.StreamRequest\x1a\x1e.jumpstarter.v1.StreamResponse(\x01\x30\x01\x42z\n\x12\x63om.jumpstarter.v1B\x0bRouterProtoP\x01\xa2\x02\x03JXX\xaa\x02\x0eJumpstarter.V1\xca\x02\x0eJumpstarter\\V1\xe2\x02\x1aJumpstarter\\V1\\GPBMetadata\xea\x02\x0fJumpstarter::V1b\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'jumpstarter.v1.router_pb2', _globals) if not _descriptor._USE_C_DESCRIPTORS: - DESCRIPTOR._loaded_options = None + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\022com.jumpstarter.v1B\013RouterProtoP\001\242\002\003JXX\252\002\016Jumpstarter.V1\312\002\016Jumpstarter\\V1\342\002\032Jumpstarter\\V1\\GPBMetadata\352\002\017Jumpstarter::V1' _globals['_FRAMETYPE']._serialized_start=250 _globals['_FRAMETYPE']._serialized_end=353 _globals['_STREAMREQUEST']._serialized_start=47