Skip to content

Commit e0cc468

Browse files
authored
arm: validate archive and retry on all errors for FVP and toolchain downloads (#19309)
### Summary The Test ARM Backend workflow has been intermittently failing with `curl: (92) HTTP/2 stream 0 was not closed cleanly: INTERNAL_ERROR` during the FVP corstone download from developer.arm.com's CDN. The toolchain download in the same setup uses the same bare-curl pattern and fails the same way when the CDN flakes. In both cases the previous flow was a single `curl --output ...` followed by a fatal `verify_md5`, so neither a transient HTTP/2 reset nor a short error body that curl treats as a successful 200 was retried. Factor out a `download_with_retry` helper in utils.sh that wraps the download in a five-attempt outer loop using `curl --fail --retry-all-errors` and validates each attempt against the published MD5 before proceeding, with the on-disk file size logged on failure for diagnosis. Switch verify_md5's mismatch path from `exit 2` to `return 2` so the helper can treat a bad checksum as a retryable failure; existing callers (`verify_md5 ... || exit 1`) keep the same fatal-on-mismatch behavior since the function still returns non-zero on a bad checksum. Use the helper from both fvp_utils.sh and toolchain_utils.sh in place of the bare `curl` + `verify_md5` pair. Authored with Claude Code. ### Test plan CI cc @digantdesai @freddan80 @per @zingo @oscarandersson8218 @mansnils @Sebastian-Larsson @robell
1 parent fe2ce06 commit e0cc468

3 files changed

Lines changed: 47 additions & 7 deletions

File tree

backends/arm/scripts/fvp_utils.sh

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,9 @@ function install_fvp() {
6767
log_step "fvp" "Downloading FVP ${fvp}"
6868
url_variable=${fvp}_url
6969
fvp_url=${!url_variable}
70-
curl --output "FVP_${fvp}.tgz" "${fvp_url}"
7170
md5_variable=${fvp}_md5_checksum
7271
fvp_md5_checksum=${!md5_variable}
73-
verify_md5 ${fvp_md5_checksum} FVP_${fvp}.tgz || exit 1
72+
download_with_retry "fvp" "${fvp_url}" "FVP_${fvp}.tgz" "${fvp_md5_checksum}" || exit 1
7473
fi
7574

7675
log_step "fvp" "Installing FVP ${fvp}"

backends/arm/scripts/toolchain_utils.sh

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,7 @@ function setup_toolchain() {
107107

108108
if [[ ! -e "${toolchain_archive}" ]]; then
109109
log_step "toolchain" "Downloading ${toolchain_dir} toolchain"
110-
curl --output "${toolchain_archive}" -L "${toolchain_url}"
111-
verify_md5 ${toolchain_md5_checksum} "${toolchain_archive}" || exit 1
110+
download_with_retry "toolchain" "${toolchain_url}" "${toolchain_archive}" "${toolchain_md5_checksum}" || exit 1
112111
fi
113112

114113
log_step "toolchain" "Installing ${toolchain_dir} toolchain"

backends/arm/scripts/utils.sh

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,10 @@ function verify_md5() {
4747
# Arg 1: Expected checksum for file
4848
# Arg 2: Path to file
4949
# Exits with return code 1 if the number of arguments is incorrect.
50-
# Exits with return code 2 if the calculated mf5 does not match the given.
50+
# Returns 2 if the calculated md5 does not match the given. Returning
51+
# rather than exiting lets callers like download_with_retry treat a bad
52+
# checksum as a retryable failure (e.g. truncated download) instead of
53+
# tearing down the whole script.
5154

5255
[[ $# -ne 2 ]] \
5356
&& { echo "[${FUNCNAME[0]}] Invalid number of args, expecting 2, but got $#"; exit 1; }
@@ -60,11 +63,50 @@ function verify_md5() {
6063
local file_checksum="$(md5sum $file | awk '{print $1}')"
6164
fi
6265
if [[ ${ref_checksum} != ${file_checksum} ]]; then
63-
echo "Mismatched MD5 checksum for file: ${file}. Expecting ${ref_checksum} but got ${file_checksum}. Exiting."
64-
exit 2
66+
echo "Mismatched MD5 checksum for file: ${file}. Expecting ${ref_checksum} but got ${file_checksum}."
67+
return 2
6568
fi
6669
}
6770

71+
function download_with_retry() {
72+
# Download a URL to a path and validate its MD5, retrying on transport
73+
# or checksum errors. developer.arm.com's CDN intermittently aborts the
74+
# download mid-stream with HTTP/2 INTERNAL_ERROR (curl exit 92), and
75+
# rare cases return a short error body that curl treats as success;
76+
# both are caught here. --fail rejects HTTP errors,
77+
# --retry-all-errors handles transport errors, and verify_md5 catches
78+
# truncation / wrong-content via the published archive checksum.
79+
80+
# Arg 1: log context (passed to log_step)
81+
# Arg 2: URL to download
82+
# Arg 3: Output path
83+
# Arg 4: Expected MD5 checksum
84+
85+
[[ $# -ne 4 ]] \
86+
&& { echo "[${FUNCNAME[0]}] Invalid number of args, expecting 4, but got $#"; exit 1; }
87+
local context="${1}"
88+
local url="${2}"
89+
local output="${3}"
90+
local expected_md5="${4}"
91+
92+
local max_attempts=5
93+
for attempt in $(seq 1 ${max_attempts}); do
94+
rm -f "${output}"
95+
if curl --fail --retry 3 --retry-delay 5 --retry-connrefused --retry-all-errors \
96+
-L --output "${output}" "${url}" \
97+
&& verify_md5 "${expected_md5}" "${output}"; then
98+
return 0
99+
fi
100+
ls -l "${output}" 2>&1 || true
101+
if [[ "${attempt}" = "${max_attempts}" ]]; then
102+
log_step "${context}" "ERROR: download of ${url} failed after ${attempt} attempts"
103+
return 1
104+
fi
105+
log_step "${context}" "download attempt ${attempt} failed; retrying in $((attempt * 10))s..."
106+
sleep $((attempt * 10))
107+
done
108+
}
109+
68110
function patch_repo() {
69111
# Patch git repo found in $repo_dir, starting from patch $base_rev and applying patches found in $patch_dir/$name.
70112

0 commit comments

Comments
 (0)