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

Commit 7963e82

Browse files
authored
Merge pull request #783 from bennyz/ridesx-insecure
ridesx: support http and insecure https
2 parents 864e2d8 + c39c1d9 commit 7963e82

1 file changed

Lines changed: 86 additions & 5 deletions

File tree

  • packages/jumpstarter-driver-ridesx/jumpstarter_driver_ridesx

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

Lines changed: 86 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
1+
import ssl
2+
import tempfile
13
from dataclasses import dataclass
24
from pathlib import Path
35
from typing import Dict, Optional
6+
from urllib.parse import urlparse
47

8+
import aiohttp
9+
import click
510
from jumpstarter_driver_composite.client import CompositeClient
611
from jumpstarter_driver_opendal.client import FlasherClient, operator_for_path
712
from jumpstarter_driver_power.client import PowerClient
@@ -22,10 +27,51 @@ def __post_init__(self):
2227
def boot_to_fastboot(self):
2328
return self.call("boot_to_fastboot")
2429

25-
def _upload_file_if_needed(self, file_path: str, operator: Operator | None = None) -> str:
30+
def _is_http_url(self, path: str) -> bool:
31+
"""Check if the path is an HTTP or HTTPS URL."""
32+
return isinstance(path, str) and path.startswith(("http://", "https://"))
33+
34+
def _download_http_to_storage(self, url: str, storage, filename: str, insecure_tls: bool = False) -> None:
35+
async def _download():
36+
parsed = urlparse(url)
37+
if parsed.scheme == "http" or insecure_tls:
38+
ssl_context: ssl.SSLContext | bool = False
39+
else:
40+
ssl_context = True
41+
42+
connector = aiohttp.TCPConnector(ssl=ssl_context)
43+
async with aiohttp.ClientSession(connector=connector) as session:
44+
async with session.get(url) as response:
45+
response.raise_for_status()
46+
with tempfile.NamedTemporaryFile(delete=False, dir="/var/tmp") as f:
47+
async for chunk in response.content.iter_chunked(65536):
48+
f.write(chunk)
49+
return Path(f.name)
50+
51+
tmp_path = self.portal.call(_download)
52+
try:
53+
storage.write_from_path(filename, tmp_path)
54+
finally:
55+
tmp_path.unlink()
56+
57+
def _upload_file_if_needed(
58+
self, file_path: str, operator: Operator | None = None, insecure_tls: bool = False
59+
) -> str:
2660
if not file_path or not file_path.strip():
2761
raise ValueError("File path cannot be empty. Please provide a valid file path.")
2862

63+
if self._is_http_url(file_path) and operator is None:
64+
parsed = urlparse(file_path)
65+
is_insecure_http = parsed.scheme == "http"
66+
67+
# use aiohttp for: http:// URLs, or https:// with insecure_tls
68+
if is_insecure_http or insecure_tls:
69+
filename = Path(parsed.path).name
70+
self.logger.info(f"Downloading {file_path} to storage as {filename}")
71+
self._download_http_to_storage(file_path, self.storage, filename, insecure_tls=insecure_tls)
72+
return filename
73+
74+
# use opendal for local files, https:// (secure), and other schemes
2975
if operator is None:
3076
path_buf, operator, operator_scheme = operator_for_path(file_path)
3177
else:
@@ -46,12 +92,18 @@ def _upload_file_if_needed(self, file_path: str, operator: Operator | None = Non
4692

4793
return filename
4894

49-
def flash_images(self, partitions: Dict[str, str], operators: Optional[Dict[str, Operator]] = None):
95+
def flash_images(
96+
self,
97+
partitions: Dict[str, str],
98+
operators: Optional[Dict[str, Operator]] = None,
99+
insecure_tls: bool = False,
100+
):
50101
"""Flash images to specified partitions
51102
52103
Args:
53104
partitions: Dictionary mapping partition names to file paths
54105
operators: Optional dictionary mapping partition names to operators
106+
insecure_tls: Skip TLS certificate verification for HTTPS URLs
55107
"""
56108
if not partitions:
57109
raise ValueError("At least one partition must be provided")
@@ -62,7 +114,7 @@ def flash_images(self, partitions: Dict[str, str], operators: Optional[Dict[str,
62114
for partition, file_path in partitions.items():
63115
self.logger.info(f"Processing {partition} image: {file_path}")
64116
operator = operators.get(partition)
65-
remote_files[partition] = self._upload_file_if_needed(file_path, operator)
117+
remote_files[partition] = self._upload_file_if_needed(file_path, operator, insecure_tls=insecure_tls)
66118

67119
self.logger.info("Checking for fastboot devices on Exporter...")
68120
detection_result = self.call("detect_fastboot_device", 5, 2.0)
@@ -84,6 +136,7 @@ def flash(
84136
target: str | None = None,
85137
operator: Operator | Dict[str, Operator] | None = None,
86138
compression=None,
139+
insecure_tls: bool = False,
87140
):
88141
if isinstance(path, dict):
89142
partitions = path
@@ -109,7 +162,7 @@ def flash(
109162

110163
self.boot_to_fastboot()
111164

112-
result = self.flash_images(partitions, operators)
165+
result = self.flash_images(partitions, operators, insecure_tls=insecure_tls)
113166

114167
self.logger.info("flash operation completed successfully")
115168

@@ -130,7 +183,35 @@ def base():
130183
pass
131184

132185
for name, cmd in generic_cli.commands.items():
133-
base.add_command(cmd, name=name)
186+
if name != "flash":
187+
base.add_command(cmd, name=name)
188+
189+
@base.command()
190+
@click.argument("file", nargs=-1, required=False)
191+
@click.option(
192+
"--target",
193+
"-t",
194+
"target_specs",
195+
multiple=True,
196+
help="name:file",
197+
)
198+
@click.option("--insecure-tls", is_flag=True, help="Skip TLS certificate verification")
199+
def flash(file, target_specs, insecure_tls):
200+
"""Flash image to DUT"""
201+
if target_specs:
202+
mapping: dict[str, str] = {}
203+
for spec in target_specs:
204+
if ":" not in spec:
205+
raise click.ClickException(f"Invalid target spec '{spec}', expected name:file")
206+
name, img = spec.split(":", 1)
207+
mapping[name] = img
208+
self.flash(mapping, insecure_tls=insecure_tls)
209+
return
210+
211+
if not file:
212+
raise click.ClickException("FILE argument is required unless --target/-t is used")
213+
214+
self.flash(file[0], target=None, insecure_tls=insecure_tls)
134215

135216
@base.command()
136217
def boot_to_fastboot():

0 commit comments

Comments
 (0)