Skip to content
This repository was archived by the owner on Jan 23, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/source/cli/run-tests.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ the base class will attempt to:
1. Use a local connection based on the `JUMPSTARTER_HOST` environment variable
2. Use an existing lease based on the `JMP_LEASE` environment variable, and existing credentials.
See the cli reference for [jmp lease request](../cli-reference/jmp.md#jmp-lease-request).
3. Request a lease based on the `filter_labels` provided in the test class.
3. Request a lease based on the `selector` provided in the test class.

```{eval-rst}
.. autoclass:: jumpstarter_testing.pytest.JumpstarterTest
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@


class TestResource(JumpstarterTest):
filter_labels = {"board": "rpi4"}
selector = "board=rpi4"

@pytest.fixture()
def console(self, client):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@
import asyncclick as click
from jumpstarter_cli_common.exceptions import handle_exceptions

from .common import opt_config, opt_selector_simple, selector_to_labels
from jumpstarter.common import MetadataFilter
from .common import opt_config, opt_selector_simple
from jumpstarter.common.utils import launch_shell


Expand All @@ -18,9 +17,7 @@ def client_shell(config, selector: str, lease_name):

exit_code = 0

with config.lease(
metadata_filter=MetadataFilter(labels=selector_to_labels(selector)), lease_name=lease_name
) as lease:
with config.lease(selector=selector, lease_name=lease_name) as lease:
with lease.serve_unix() as path:
with lease.monitor():
exit_code = launch_shell(path, "remote", config.drivers.allow, config.drivers.unsafe)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,6 @@
)


def selector_to_labels(selector: str):
# TODO: support complex selectors (e.g. !=)
return dict([term.split("=") for term in selector.split(",")])


class ClientParamType(click.ParamType):
name = "client"

Expand Down
3 changes: 1 addition & 2 deletions packages/jumpstarter-driver-dutlink/examples/dutlink.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@
from jumpstarter.common.utils import env

# initialize client from exporter config
# from jumpstarter.common import MetadataFilter
# from jumpstarter.config.client import ClientConfigV1Alpha1
# with ClientConfigV1Alpha1.load("default").lease(metadata_filter=MetadataFilter()) as lease:
# with ClientConfigV1Alpha1.load("default").lease(selector="example.com/board=dutlink") as lease:
# with lease.connect() as client:

# initialize client from environment
Expand Down
2 changes: 1 addition & 1 deletion packages/jumpstarter-driver-tftp/examples/tftp_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@


class TestResource(JumpstarterTest):
filter_labels = {"board": "rpi4"}
selector = "board=rpi4"

@pytest.fixture()
def setup_tftp(self, client):
Expand Down
11 changes: 5 additions & 6 deletions packages/jumpstarter-testing/jumpstarter_testing/pytest.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

import pytest

from jumpstarter.common import MetadataFilter
from jumpstarter.common.utils import env
from jumpstarter.config.client import ClientConfigV1Alpha1

Expand All @@ -19,7 +18,7 @@ class JumpstarterTest:

Looks for the `JUMPSTARTER_HOST` environment variable to connect to an
established Jumpstarter shell, otherwise it will try to acquire a lease
for a single exporter using the filter_labels annotation.
for a single exporter using the selector annotation.
i.e.:

.. code-block:: python
Expand All @@ -33,7 +32,7 @@ class JumpstarterTest:
log = logging.getLogger(__name__)

class TestResource(JumpstarterTest):
filter_labels = {"board":"rpi4"}
selector = "board=rpi4"

@pytest.fixture()
def console(self, client):
Expand All @@ -49,17 +48,17 @@ def test_setup_device(self, client, console):

"""

filter_labels: ClassVar[dict[str, str]]
selector: ClassVar[str]

@pytest.fixture(scope="class")
def client(self):
try:
with env() as client:
yield client
except RuntimeError:
labels = getattr(self, "filter_labels", {})
selector = getattr(self, "selector", None)
config = ClientConfigV1Alpha1.load("default")
with config.lease(metadata_filter=MetadataFilter(labels=labels)) as lease:
with config.lease(selector=selector) as lease:
with lease.connect() as client:
yield client
# BUG workaround: make sure that grpc servers get the client/lease release properly
Expand Down
11 changes: 5 additions & 6 deletions packages/jumpstarter/jumpstarter/client/lease.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from .exceptions import LeaseError
from jumpstarter.client import client_from_path
from jumpstarter.client.grpc import ClientService
from jumpstarter.common import MetadataFilter, TemporaryUnixListener
from jumpstarter.common import TemporaryUnixListener
from jumpstarter.common.condition import condition_false, condition_message, condition_present_and_equal, condition_true
from jumpstarter.common.grpc import translate_grpc_exceptions
from jumpstarter.common.streams import connect_router_stream
Expand All @@ -31,7 +31,7 @@
class Lease(AbstractContextManager, AbstractAsyncContextManager):
channel: Channel
timeout: int = 1800
metadata_filter: MetadataFilter = field(default_factory=MetadataFilter)
selector: str
portal: BlockingPortal
namespace: str
name: str | None = field(default=None)
Expand All @@ -52,17 +52,16 @@ def __post_init__(self):

async def _create(self):
duration = timedelta(seconds=self.timeout)
selector = ",".join(("{}={}".format(label[0], label[1]) for label in self.metadata_filter.labels.items()))

logger.debug("Creating lease request for selector %s for duration %s", selector, duration)
logger.debug("Creating lease request for selector %s for duration %s", self.selector, duration)
with translate_grpc_exceptions():
self.name = (
await self.svc.CreateLease(
selector=selector,
selector=self.selector,
duration=timedelta(seconds=self.timeout),
)
).name
logger.info("Created lease request for selector %s for duration %s", selector, duration)
logger.info("Created lease request for selector %s for duration %s", self.selector, duration)

async def get(self):
with translate_grpc_exceptions():
Expand Down
4 changes: 2 additions & 2 deletions packages/jumpstarter/jumpstarter/common/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from .metadata import Metadata, MetadataFilter
from .metadata import Metadata
from .tempfile import TemporarySocket, TemporaryTcpListener, TemporaryUnixListener

__all__ = ["Metadata", "MetadataFilter", "TemporarySocket", "TemporaryUnixListener", "TemporaryTcpListener"]
__all__ = ["Metadata", "TemporarySocket", "TemporaryUnixListener", "TemporaryTcpListener"]
5 changes: 0 additions & 5 deletions packages/jumpstarter/jumpstarter/common/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,3 @@ class Metadata:
@property
def name(self):
return self.labels.get("jumpstarter.dev/name", "unknown")


@dataclass(kw_only=True, slots=True)
class MetadataFilter:
labels: dict[str, str] = field(default_factory=dict)
17 changes: 8 additions & 9 deletions packages/jumpstarter/jumpstarter/config/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
from .grpc import call_credentials
from .tls import TLSConfigV1Alpha1
from jumpstarter.client.grpc import ClientService
from jumpstarter.common import MetadataFilter
from jumpstarter.common.exceptions import FileNotFoundError
from jumpstarter.common.grpc import aio_secure_channel, ssl_channel_credentials, translate_grpc_exceptions

Expand Down Expand Up @@ -62,9 +61,9 @@ async def channel(self):
return aio_secure_channel(self.endpoint, credentials, self.grpcOptions)

@contextmanager
def lease(self, metadata_filter: MetadataFilter, lease_name: str | None = None):
def lease(self, selector: str | None = None, lease_name: str | None = None):
with start_blocking_portal() as portal:
with portal.wrap_async_context_manager(self.lease_async(metadata_filter, lease_name, portal)) as lease:
with portal.wrap_async_context_manager(self.lease_async(selector, lease_name, portal)) as lease:
yield lease

def get_exporter(self, name: str):
Expand All @@ -80,9 +79,9 @@ def list_exporters(
with start_blocking_portal() as portal:
return portal.call(self.list_exporters_async, page_size, page_token, filter)

def request_lease(self, metadata_filter: MetadataFilter):
def request_lease(self, selector: str):
with start_blocking_portal() as portal:
return portal.call(self.request_lease_async, metadata_filter, portal)
return portal.call(self.request_lease_async, selector, portal)

def list_leases(self, filter: str):
with start_blocking_portal() as portal:
Expand Down Expand Up @@ -147,7 +146,7 @@ async def delete_lease_async(self, name: str):

async def request_lease_async(
self,
metadata_filter: MetadataFilter,
selector: str,
portal: BlockingPortal,
):
# dynamically import to avoid circular imports
Expand All @@ -157,7 +156,7 @@ async def request_lease_async(
channel=await self.channel(),
namespace=self.metadata.namespace,
name=None,
metadata_filter=metadata_filter,
selector=selector,
portal=portal,
allow=self.drivers.allow,
unsafe=self.drivers.unsafe,
Expand Down Expand Up @@ -185,7 +184,7 @@ async def release_lease_async(self, name):
@asynccontextmanager
async def lease_async(
self,
metadata_filter: MetadataFilter,
selector: str,
lease_name: str | None,
portal: BlockingPortal,
):
Expand All @@ -200,7 +199,7 @@ async def lease_async(
channel=await self.channel(),
namespace=self.metadata.namespace,
name=lease_name,
metadata_filter=metadata_filter,
selector=selector,
portal=portal,
allow=self.drivers.allow,
unsafe=self.drivers.unsafe,
Expand Down