Skip to content
Draft
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
9 changes: 8 additions & 1 deletion python/Containerfile
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,14 @@ RUN ARCH=$(uname -m) && \
TEMP_FILE="/tmp/fls-${ARCH}-${SUFFIX}.tmp" && \
curl -fsSL "${URL}" -o "${TEMP_FILE}" && \
mv "${TEMP_FILE}" /usr/local/bin/fls && \
chmod +x /usr/local/bin/fls
chmod +x /usr/local/bin/fls && \
if [ "$ARCH" != "aarch64" ]; then \
curl -fsSL "https://github.com/jumpstarter-dev/fls/releases/download/${FLS_VERSION}/fls-aarch64-linux-musl" \
-o /usr/local/bin/fls-aarch64 && \
chmod +x /usr/local/bin/fls-aarch64; \
else \
ln -s /usr/local/bin/fls /usr/local/bin/fls-aarch64; \
fi

FROM builder AS wheels
ADD . /src
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@

from jumpstarter.client.core import DriverMethodNotImplemented
from jumpstarter.client.decorators import driver_click_group
from jumpstarter.common.exceptions import ArgumentError, JumpstarterException
from jumpstarter.common.exceptions import ArgumentError, ConfigurationError, JumpstarterException
from jumpstarter.common.fls import get_fls_github_url


Expand Down Expand Up @@ -306,6 +306,11 @@ def _categorize_exception(self, exception: Exception) -> FlashRetryableError | F
if isinstance(exception, CancelledError):
return FlashNonRetryableError("Operation cancelled")

# ConfigurationError is deterministic and should not be retried
config_error = self._find_exception_in_chain(exception, ConfigurationError)
if config_error is not None:
return FlashNonRetryableError(str(config_error))

# Unknown exception - log full stack trace and wrap as retryable
self.logger.exception(
f"Unknown exception encountered during flash operation, treating as retryable: "
Expand Down Expand Up @@ -701,9 +706,13 @@ def _flash_with_fls(
self._download_fls_binary(console, prompt, fls_binary_url, f"Failed to download FLS from {fls_binary_url}")
elif fls_version != "":
self.logger.info(f"Downloading FLS version {fls_version} from GitHub releases")
# Download fls binary to the target device (always aarch64 for target devices)
fls_url = get_fls_github_url(fls_version, arch="aarch64")
self._download_fls_binary(console, prompt, fls_url, f"Failed to download FLS from {fls_url}")
else:
self.logger.info("Serving FLS binary from exporter container")
self.call("setup_fls_binary")
fls_url = self.http.get_url() + "/fls"
self._download_fls_binary(console, prompt, fls_url, "Failed to download FLS from exporter")

# Flash the image
creds_file = None
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,16 @@ async def get_cacert(self) -> str | None:
with open(self.cacert) as f:
return f.read()

@export
async def setup_fls_binary(self):
"""Copy local fls binary to HTTP server root for DUT download"""
fls_path = Path("/usr/local/bin/fls-aarch64")
if not fls_path.exists():
fls_path = Path("/usr/local/bin/fls")
if not fls_path.exists():
raise ConfigurationError("fls binary not found at /usr/local/bin/fls-aarch64 or /usr/local/bin/fls")
await self.http.storage.copy_exporter_file(fls_path, "fls")

@export
async def setup_flasher_bundle(self, force_flash_bundle: str | None = None):
"""Setup flasher bundle
Expand Down
Loading