1- import ssl
2- import tempfile
31from dataclasses import dataclass
42from pathlib import Path
53from typing import Dict , Optional
6- from urllib .parse import urlparse
74
8- import aiohttp
9- import click
105from jumpstarter_driver_composite .client import CompositeClient
116from jumpstarter_driver_opendal .client import FlasherClient , operator_for_path
127from jumpstarter_driver_power .client import PowerClient
@@ -27,51 +22,10 @@ def __post_init__(self):
2722 def boot_to_fastboot (self ):
2823 return self .call ("boot_to_fastboot" )
2924
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 :
25+ def _upload_file_if_needed (self , file_path : str , operator : Operator | None = None ) -> str :
6026 if not file_path or not file_path .strip ():
6127 raise ValueError ("File path cannot be empty. Please provide a valid file path." )
6228
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
7529 if operator is None :
7630 path_buf , operator , operator_scheme = operator_for_path (file_path )
7731 else :
@@ -92,18 +46,12 @@ def _upload_file_if_needed(
9246
9347 return filename
9448
95- def flash_images (
96- self ,
97- partitions : Dict [str , str ],
98- operators : Optional [Dict [str , Operator ]] = None ,
99- insecure_tls : bool = False ,
100- ):
49+ def flash_images (self , partitions : Dict [str , str ], operators : Optional [Dict [str , Operator ]] = None ):
10150 """Flash images to specified partitions
10251
10352 Args:
10453 partitions: Dictionary mapping partition names to file paths
10554 operators: Optional dictionary mapping partition names to operators
106- insecure_tls: Skip TLS certificate verification for HTTPS URLs
10755 """
10856 if not partitions :
10957 raise ValueError ("At least one partition must be provided" )
@@ -114,7 +62,7 @@ def flash_images(
11462 for partition , file_path in partitions .items ():
11563 self .logger .info (f"Processing { partition } image: { file_path } " )
11664 operator = operators .get (partition )
117- remote_files [partition ] = self ._upload_file_if_needed (file_path , operator , insecure_tls = insecure_tls )
65+ remote_files [partition ] = self ._upload_file_if_needed (file_path , operator )
11866
11967 self .logger .info ("Checking for fastboot devices on Exporter..." )
12068 detection_result = self .call ("detect_fastboot_device" , 5 , 2.0 )
@@ -136,7 +84,6 @@ def flash(
13684 target : str | None = None ,
13785 operator : Operator | Dict [str , Operator ] | None = None ,
13886 compression = None ,
139- insecure_tls : bool = False ,
14087 ):
14188 if isinstance (path , dict ):
14289 partitions = path
@@ -162,7 +109,7 @@ def flash(
162109
163110 self .boot_to_fastboot ()
164111
165- result = self .flash_images (partitions , operators , insecure_tls = insecure_tls )
112+ result = self .flash_images (partitions , operators )
166113
167114 self .logger .info ("flash operation completed successfully" )
168115
@@ -183,35 +130,7 @@ def base():
183130 pass
184131
185132 for name , cmd in generic_cli .commands .items ():
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 )
133+ base .add_command (cmd , name = name )
215134
216135 @base .command ()
217136 def boot_to_fastboot ():
0 commit comments