@@ -309,43 +309,84 @@ def _flash_with_progress(
309309 self ._check_url_access (console , prompt , image_url , tls_args , header_args )
310310
311311 # Flash the image, we run curl -> decompress -> dd in the background, so we can monitor dd's progress
312+ # Use pipefail to ensure the pipeline fails if any command in the pipe fails
312313 flash_cmd = (
313- f'( curl -fsSL { tls_args } { header_args } "{ image_url } " | '
314+ f'( set -o pipefail; curl -fsSL { tls_args } { header_args } "{ image_url } " | '
314315 f"{ decompress_cmd } "
315- f"dd of={ target_path } bs=64k iflag=fullblock oflag=direct) &"
316+ f'dd of={ target_path } bs=64k iflag=fullblock oflag=direct ' +
317+ '&& echo "FLASH_COMPLETE" || echo "FLASH_FAILED" ) &'
316318 )
317319 console .sendline (flash_cmd )
318320 console .expect (prompt , timeout = EXPECT_TIMEOUT_DEFAULT * 2 )
319321
320- # monitor the dd process to understand flashing progrses
322+ # Monitor flash progress by accumulating console output and looking for completion markers
321323 console .sendline ("pidof dd" )
322324 console .expect (prompt , timeout = EXPECT_TIMEOUT_DEFAULT )
323325 dd_pid = console .before .decode (errors = "ignore" ).splitlines ()[1 ].strip ()
324326
325327 # Initialize progress tracking variables
326328 last_pos = 0
327329 last_time = time .time ()
330+ flash_start_time = time .time ()
331+ accumulated_output = ""
332+ dd_finished_time = None
328333
329334 while True :
335+ # Check if dd process is still running for progress monitoring
330336 console .sendline (f"cat /proc/{ dd_pid } /fdinfo/1" )
331337 console .expect (prompt , timeout = EXPECT_TIMEOUT_DEFAULT )
332338 if "No such file or directory" in console .before .decode (errors = "ignore" ):
333- break
334- data = console .before .decode (errors = "ignore" )
335- match = re .search (r"pos:\s+(\d+)" , data )
336- if match :
337- current_bytes = int (match .group (1 ))
338- current_time = time .time ()
339- elapsed = current_time - last_time
340-
341- if elapsed >= 5.0 : # Update speed every 5 seconds
342- bytes_diff = current_bytes - last_pos
343- speed_mb = (bytes_diff / (1024 * 1024 )) / elapsed
344- total_mb = current_bytes / (1024 * 1024 )
345- self .logger .info (f"Flash progress: { total_mb :.2f} MB, Speed: { speed_mb :.2f} MB/s" )
346-
347- last_pos = current_bytes
348- last_time = current_time
339+ # dd process finished, check for completion markers in accumulated output
340+ if "FLASH_COMPLETE" in accumulated_output :
341+ self .logger .info ("Flash operation completed successfully" )
342+ break
343+ elif "FLASH_FAILED" in accumulated_output :
344+ raise RuntimeError ("Flash operation failed - curl or pipeline failed" )
345+ else :
346+ # dd finished but no completion marker found yet - wait a bit for echo to execute
347+ if dd_finished_time is None :
348+ dd_finished_time = time .time ()
349+ elif time .time () - dd_finished_time > 5 : # Wait up to 5 seconds for echo
350+ raise RuntimeError ("Flash operation completed without success/failure marker" )
351+ # Continue checking for a few more iterations
352+ time .sleep (1 )
353+ continue
354+ else :
355+ # dd is still running, check for progress and accumulate output
356+ data = console .before .decode (errors = "ignore" )
357+ # Keep only the last 128 bytes to prevent memory growth
358+ if len (accumulated_output ) > 128 :
359+ accumulated_output = accumulated_output [- 128 :]
360+
361+ accumulated_output += data
362+
363+ # Check for completion markers in the accumulated output
364+ if "FLASH_COMPLETE" in accumulated_output :
365+ self .logger .info ("Flash operation completed successfully" )
366+ break
367+ elif "FLASH_FAILED" in accumulated_output :
368+ raise RuntimeError ("Flash operation failed - curl or pipeline failed" )
369+
370+ # Monitor dd progress
371+ match = re .search (r"pos:\s+(\d+)" , data )
372+ if match :
373+ current_bytes = int (match .group (1 ))
374+ current_time = time .time ()
375+ elapsed = current_time - last_time
376+
377+ if elapsed >= 5.0 : # Update speed every 5 seconds
378+ bytes_diff = current_bytes - last_pos
379+ speed_mb = (bytes_diff / (1024 * 1024 )) / elapsed
380+ total_mb = current_bytes / (1024 * 1024 )
381+ self .logger .info (f"Flash progress: { total_mb :.2f} MB, Speed: { speed_mb :.2f} MB/s" )
382+
383+ last_pos = current_bytes
384+ last_time = current_time
385+
386+ # Check for timeout (prevent infinite loops)
387+ if time .time () - flash_start_time > EXPECT_TIMEOUT_SYNC :
388+ raise RuntimeError ("Flash operation timed out" )
389+
349390 time .sleep (1 )
350391
351392 self .logger .info ("Flushing buffers" )
0 commit comments