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

Commit fc6933e

Browse files
authored
Merge branch 'main' into qemu_image
2 parents 0259828 + 8dd161a commit fc6933e

5 files changed

Lines changed: 42 additions & 11 deletions

File tree

  • packages
    • jumpstarter-cli-common/jumpstarter_cli_common
    • jumpstarter-cli/jumpstarter_cli
    • jumpstarter-driver-ridesx
    • jumpstarter/jumpstarter/config

packages/jumpstarter-cli-common/jumpstarter_cli_common/oidc.py

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import json
2+
import os
23
from dataclasses import dataclass
34
from functools import wraps
45
from typing import ClassVar
@@ -12,6 +13,8 @@
1213
from joserfc.jws import extract_compact
1314
from yarl import URL
1415

16+
from jumpstarter.config.env import JMP_OIDC_CALLBACK_PORT
17+
1518

1619
def opt_oidc(f):
1720
@click.option("--issuer", help="OIDC issuer")
@@ -20,6 +23,12 @@ def opt_oidc(f):
2023
@click.option("--username", help="OIDC username")
2124
@click.option("--password", help="OIDC password")
2225
@click.option("--connector-id", "connector_id", help="OIDC token exchange connector id (Dex specific)")
26+
@click.option("--callback-port",
27+
"callback_port",
28+
type=click.IntRange(0, 65535),
29+
default=None,
30+
help="Port for OIDC callback server (0=random port)",
31+
)
2332
@wraps(f)
2433
def wrapper(*args, **kwds):
2534
return f(*args, **kwds)
@@ -71,9 +80,21 @@ async def password_grant(self, username: str, password: str):
7180
)
7281
)
7382

74-
async def authorization_code_grant(self):
83+
async def authorization_code_grant(self, callback_port: int | None = None):
7584
config = await self.configuration()
7685

86+
# Use provided port, fall back to env var, then default to 0 (OS picks)
87+
if callback_port is not None:
88+
port = callback_port
89+
else:
90+
env_value = os.environ.get(JMP_OIDC_CALLBACK_PORT)
91+
if env_value is None:
92+
port = 0
93+
elif env_value.isdigit() and int(env_value) <= 65535:
94+
port = int(env_value)
95+
else:
96+
raise click.ClickException(f"Invalid {JMP_OIDC_CALLBACK_PORT} \"{env_value}\": must be a valid port")
97+
7798
tx, rx = create_memory_object_stream()
7899

79100
async def callback(request):
@@ -86,8 +107,12 @@ async def callback(request):
86107
runner = web.AppRunner(app, access_log=None)
87108
await runner.setup()
88109

89-
site = web.TCPSite(runner, "localhost", 0)
90-
await site.start()
110+
site = web.TCPSite(runner, "localhost", port)
111+
try:
112+
await site.start()
113+
except OSError as e:
114+
await runner.cleanup()
115+
raise click.ClickException(f"Failed to start callback server on port {port}: {e}") from None
91116

92117
redirect_uri = "http://localhost:%d/callback" % site._server.sockets[0].getsockname()[1]
93118

packages/jumpstarter-cli/jumpstarter_cli/login.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ async def login( # noqa: C901
4747
issuer: str,
4848
client_id: str,
4949
connector_id: str,
50+
callback_port: int | None,
5051
unsafe,
5152
insecure_tls_config: bool,
5253
nointeractive: bool,
@@ -123,7 +124,7 @@ async def login( # noqa: C901
123124
elif username is not None and password is not None:
124125
tokens = await oidc.password_grant(username, password)
125126
else:
126-
tokens = await oidc.authorization_code_grant()
127+
tokens = await oidc.authorization_code_grant(callback_port=callback_port)
127128

128129
config.token = tokens["access_token"]
129130

packages/jumpstarter-driver-ridesx/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ Both drivers require:
104104

105105
```{testcode}
106106
# Flash a single partition
107-
ridesx_client.flash("/path/to/boot.img", partition="boot")
107+
ridesx_client.flash("/path/to/boot.img", target="boot")
108108
```
109109

110110
### Flash Multiple Partitions
@@ -125,7 +125,7 @@ The driver automatically handles compressed images (`.gz`, `.gzip`, `.xz`):
125125

126126
```{testcode}
127127
# Flash compressed images - decompression is automatic
128-
ridesx_client.flash("/path/to/boot.img.gz", partition="boot")
128+
ridesx_client.flash("/path/to/boot.img.gz", target="boot")
129129
```
130130

131131
### Power Control

packages/jumpstarter-driver-ridesx/jumpstarter_driver_ridesx/client.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -81,18 +81,22 @@ def flash(
8181
self,
8282
path: str | Dict[str, str],
8383
*,
84-
partition: str | None = None,
84+
target: str | None = None,
8585
operator: Operator | Dict[str, Operator] | None = None,
8686
compression=None,
8787
):
8888
if isinstance(path, dict):
8989
partitions = path
9090
operators = operator if isinstance(operator, dict) else None
9191
else:
92-
if partition is None:
93-
raise ValueError("'partition' must be provided")
94-
partitions = {partition: path}
95-
operators = {partition: operator} if isinstance(operator, Operator) else None
92+
if target is None:
93+
raise ValueError(
94+
"This driver requires a target partition.\n"
95+
"Usage: j storage flash --target <partition>:<file>\n"
96+
"Example: j storage flash -t boot_a:aboot.img -t system_a:rootfs.simg -t system_b:qm_var.simg"
97+
)
98+
partitions = {target: path}
99+
operators = {target: operator} if isinstance(operator, Operator) else None
96100

97101
for partition_name, file_path in partitions.items():
98102
if not file_path or not file_path.strip():

packages/jumpstarter/jumpstarter/config/env.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@
1010
JMP_LEASE = "JMP_LEASE"
1111

1212
JMP_DISABLE_COMPRESSION = "JMP_DISABLE_COMPRESSION"
13+
JMP_OIDC_CALLBACK_PORT = "JMP_OIDC_CALLBACK_PORT"

0 commit comments

Comments
 (0)