fix(software): retry truncated/empty downloads and report real checksum algorithm#795
Merged
Merged
Conversation
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
Contributor
There was a problem hiding this comment.
Pull request overview
This PR improves the resiliency and debuggability of pkg/software downloads by (1) detecting empty/truncated HTTP responses earlier, (2) adding a bounded retry loop that retries both download failures and checksum mismatches, and (3) ensuring checksum-mismatch errors report the actual algorithm used (e.g., sha256 instead of unknown).
Changes:
- Harden
Downloader.Downloadto reject zero-byte bodies and advertisedContent-Lengthmismatches. - Add
Downloader.DownloadAndVerifywith capped exponential backoff and configurable retry settings. - Thread the checksum algorithm through integrity checking so error messages report the real algorithm; update installer call sites and tests accordingly.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
pkg/software/downloader.go |
Adds retry/backoff + download validation for empty/truncated responses. |
pkg/software/integrity_checker.go |
Propagates the actual checksum algorithm into mismatch errors. |
pkg/software/base_installer.go |
Switches archive/binary/config downloads to the new DownloadAndVerify path. |
pkg/software/downloader_test.go |
Adds unit tests covering zero-byte rejection and retry behavior. |
pkg/software/integrity_checker_test.go |
Adds assertions that mismatch errors name the real algorithm (not unknown). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
1c50433 to
e73afa8
Compare
…um algorithm Software downloads failed the whole workflow on a transient CDN/network hiccup: a 200-with-empty-body, a truncated copy, or a mid-stream reset wrote a corrupt file that the checksum verifier correctly rejected, but with no retry the entire run aborted. - Download now rejects zero-byte bodies and, when Content-Length is advertised, fails on a byte-count mismatch instead of persisting a truncated file. - Add Downloader.DownloadAndVerify, which retries download+verify with capped exponential backoff (default 3 attempts), deleting the bad file between attempts; both download errors and checksum mismatches are retryable. The three base_installer call sites now use it. - Thread the real algorithm through the checksum helper so mismatch errors report e.g. 'sha256' instead of the misleading 'unknown'. Closes #786 Signed-off-by: alex-au <alex.w.aus@gmail.com>
e73afa8 to
48ce8f6
Compare
brunodam
approved these changes
Jul 1, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Software downloads (
pkg/software/downloader.go) failed the whole workflow when adownload came back truncated or empty, with no retry.
Downloaddid a singlehttp.Get,checked only
StatusCode == 200, thenio.Copy'd the body to disk — so a 200-with-empty-body,a silently truncated copy, or a mid-stream reset produced a corrupt file that the checksum
verifier correctly rejected but the run could not recover from. Observed in the UAT Purge
Storage job on PR #785 during
setup-crio:Downloads are now self-healing for transient corruption.
Downloadvalidates the responsebefore trusting it (rejects zero-byte bodies, and — when
Content-Lengthis advertised — failson a byte-count mismatch instead of persisting a truncated file). A new
Downloader.DownloadAndVerifywraps download + checksum in a bounded retry loop: both downloaderrors and a subsequent checksum mismatch are treated as retryable, the bad file is deleted
between attempts, and it gives up after
maxAttempts(default 3) with capped exponentialbackoff. The three
base_installer.gocall sites now use it.The misleading
algorithm 'unknown'in checksum-mismatch errors is also fixed — the realalgorithm (
sha256, etc.) is now threaded through the checksum helper.Retry uses a small hand-rolled loop matching the repo's existing
ProbeTCPstyle rather thanpulling in
cenkalti/backoff(which is only a transitive go.sum entry, not a vendored/useddependency).
Files changed
pkg/software/downloader.goDownloadrejects zero-byte / Content-Length-mismatched responses; newDownloadAndVerifyretry loop +WithMaxAttempts/WithRetryDelayoptionspkg/software/integrity_checker.gochecksumhelper so mismatch errors no longer reportunknownpkg/software/base_installer.goDownload+VerifyChecksumpairs (archive/binary/config) withDownloadAndVerifypkg/software/downloader_test.gopkg/software/integrity_checker_test.gounknownReview guide
Code review checklist
downloader.goDownload— zero-byte check fires before the Content-Length check; the Content-Length comparison is guarded onresp.ContentLength >= 0so chunked/unknown-length responses (-1) are not falsely rejected.downloader.goDownloadAndVerify— the bad file is removed on every failed attempt (via base-path-validatedos.Remove), so a corrupt partial never persists; the last error is returned once attempts are exhausted.downloader.gosleepBackoff— delay is capped atmaxRetryDelayand the shift-overflow guard (delay < fd.retryDelay) holds; a zeroretryDelaydisables sleeping (used by tests).NewDownloadErrorwrapping anerrorx.ExternalErrorcause — they staysoftware.download_error(retryable), no new errorx namespace.integrity_checker.go— all threechecksum(...)calls pass the matching algorithm string; bothNewChecksumErrorsites use it.base_installer.go— the pre-existing-file skip/delete logic is untouched; only the download+verify pair was collapsed.Unit tests
Manual UAT
The failure is inherently transient (CDN/network hiccup), so it is exercised by the
Test_Downloader_DownloadAndVerify_RetriesThenSucceedstest, which stands up anhttptestserver that returns an empty body on the first request and the correct payload on the second,
and asserts the download recovers without failing.
..._ExhaustsAttemptsasserts apersistently-corrupt file still fails after 3 tries with the file removed.
Risks / rollback
A persistently-unreachable or invalid URL now takes up to 3 attempts (plus a few seconds of
backoff) before failing, instead of failing immediately — bounded, and only on the error path.
The change is additive to the download path; revert is dropping
DownloadAndVerifyandrestoring the original
Download+VerifyChecksumcall-site blocks.Related Issues