utils/vmimage: Add fallback mirrors for Fedora images#6266
utils/vmimage: Add fallback mirrors for Fedora images#6266harvey0100 wants to merge 2 commits intoavocado-framework:masterfrom
Conversation
Summary of ChangesHello @harvey0100, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request significantly enhances the reliability of Fedora image provisioning by introducing a comprehensive fallback system for download URLs. It addresses recurring CI failures caused by volatile Fedora mirror availability by allowing the image provider to cycle through multiple mirrors and handle a broader range of network errors. This change ensures more stable and consistent image acquisition, reducing maintenance overhead and improving the overall robustness of the system. Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request introduces a fallback mechanism for Fedora image download URLs to prevent CI failures due to mirror unavailability or URL changes, by adding URLError handling and multiple mirror attempts. However, the implementation exposes two security vulnerabilities: a potential Argument Injection in the qemu-img command construction due to unquoted user-supplied parameters, and a Regular Expression Denial of Service (ReDoS) vulnerability where the version parameter is used directly as a regex pattern. It is recommended to sanitize user inputs and ensure proper quoting of command-line arguments to address these issues.
| if self.url_old_images and int(self.version) <= 40: | ||
| self.url_versions = self.url_old_images | ||
|
|
||
| self.url_images = self.url_versions + "{version}/" + cloud + "/{arch}/images/" |
There was a problem hiding this comment.
The url_images template is constructed using user-supplied version and arch parameters. This template is eventually formatted and used to derive a file path (self.base_image) which is passed unquoted to a shell command in _take_snapshot (line 701). Because process.run() uses shlex.split() on the entire command string, any spaces in version or arch will result in additional arguments being injected into the qemu-img command. For example, a version string like 30 -o backing_file=/etc/shadow could allow an attacker to manipulate qemu-img options or potentially read sensitive files.
avocado/utils/vmimage.py
Outdated
| self.url_versions = mirror | ||
|
|
||
| # Determine cloud directory based on version | ||
| if int(self.version) >= 28: |
There was a problem hiding this comment.
The self.version property (which uses the user-supplied version parameter) is called in the new _get_image_url_from_mirror method. This property triggers a version discovery process that uses the version string as a regular expression pattern in VMImageHtmlParser. A malicious user can provide a crafted regex (e.g., (a+)+$) that causes catastrophic backtracking when matched against mirror HTML content, leading to a Denial of Service (ReDoS).
| raise ImageProviderError( | ||
| f"All mirrors failed for Fedora {self._version}. " | ||
| f"Last error: {last_error}" | ||
| ) |
There was a problem hiding this comment.
The error message could be improved by including the specific Fedora version that failed, instead of a generic self._version. This would help in debugging.
| raise ImageProviderError( | |
| f"All mirrors failed for Fedora {self._version}. " | |
| f"Last error: {last_error}" | |
| ) | |
| raise ImageProviderError( | |
| f"All mirrors failed for Fedora {self.version}. " # Use self.version to display the specific Fedora version | |
| f"Last error: {last_error}" | |
| ) |
avocado/utils/vmimage.py
Outdated
| self.url_mirrors = self.MIRRORS | ||
| self.url_versions = self.MIRRORS[0] # Default to first mirror |
There was a problem hiding this comment.
| data = u.read() | ||
| parser.feed(astring.to_text(data, self.HTML_ENCODING)) | ||
| except HTTPError as exc: | ||
| except (HTTPError, URLError) as exc: |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Fix all issues with AI agents
In `@avocado/utils/vmimage.py`:
- Around line 286-297: The code currently parses self._version to int (ver) and
selects image_pattern immediately, which misclassifies the default regex
"[0-9]+" as ver==0 and forces the legacy pattern; change this to defer selecting
or setting self.image_pattern until the concrete/resolved version is known (not
when self._version is a regex). Update the logic around the int conversion of
self._version and the pattern assignments (referencing ver, self._version, and
self.image_pattern) so that if self._version is non-numeric or a regex (e.g.,
contains non-digits or brackets) you skip pattern selection now and instead
choose the correct Fedora 40 vs 41+ vs legacy pattern after the actual resolved
version is obtained.
- Around line 233-247: The final ImageProviderError raised in get_image_url
currently only includes the last_error; change it to include the full list of
mirrors tried and (optionally) per-mirror errors: iterate self.url_mirrors
collecting each mirror and its exception message when
_get_image_url_from_mirror(mirror) raises, then raise ImageProviderError
containing a summary like "All mirrors failed for Fedora {self._version}: tried
mirrors [..]" and include the per-mirror error details (use last_error only if
needed). Update the raise in get_image_url to reference self.url_mirrors and the
collected error info so callers see all attempted mirrors and their errors.
- Around line 273-277: Annotate the class-level mutable sequences with
typing.ClassVar to satisfy Ruff RUF012: import ClassVar and the appropriate
collection type from typing (e.g., List[str]) and change the declarations such
as MIRRORS to use a ClassVar annotation (e.g., MIRRORS: ClassVar[List[str]] =
[...]); apply the same ClassVar/List[str] annotation to the other mutable class
attributes referenced around lines 310-313 so all mutable class attributes are
explicitly ClassVar-annotated.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #6266 +/- ##
==========================================
+ Coverage 75.02% 75.13% +0.11%
==========================================
Files 206 206
Lines 22510 22617 +107
==========================================
+ Hits 16888 16994 +106
- Misses 5622 5623 +1 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
6c5f243 to
e9f4e1a
Compare
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
avocado/utils/vmimage.py (1)
252-262: PotentialValueErrorifversionproperty returns a non-numeric string.When
url_mirrorsis empty/None, the code falls through toint(self.version). If version discovery fails or returns a non-numeric value, this will raiseValueError. Consider adding defensive handling similar to_get_image_url_from_mirror.🛠️ Suggested fix
# No mirrors configured, use default behavior - if int(self.version) >= 28: + try: + ver = int(self.version) + except ValueError: + ver = 99 # Assume recent version + if ver >= 28: cloud = "Cloud" else: cloud = "CloudImages" - if self.url_old_images and int(self.version) <= 40: + if self.url_old_images and ver <= 40: self.url_versions = self.url_old_images
7fcabea to
c68bad4
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@avocado/utils/vmimage.py`:
- Around line 203-235: The code currently triggers version discovery via the
version property before trying mirrors, which can raise ImageProviderError and
short‑circuit fallback; modify the mirror-resolution flow so version parsing and
cloud/path selection happen inside each mirror attempt (e.g., move logic from
FedoraImageProvider.get_image_url()/FedoraSecondaryImageProvider.get_image_url()
into FedoraImageProviderBase._get_image_url_from_mirror) and ensure those
methods use the init value self._version (or a local parsed int) rather than
self.version, so a failing mirror only affects that iteration and remaining
mirrors are still tried; update similar blocks referenced around lines 321-329
and 370-377 to follow the same per‑mirror deferred discovery pattern.
c68bad4 to
698191c
Compare
clebergnu
left a comment
There was a problem hiding this comment.
Hi @harvey0100, thanks for this! Here are some comments for this round of review.
9649c28 to
2c851ca
Compare
Add URLError to exception handling in _feed_html_parser() to catch DNS resolution failures and other network errors that don't result in an HTTP response. Also add debug logging for network errors to aid troubleshooting. Reference: avocado-framework#6266 Assisted-By: Cursor-Claude-4-Sonnet Signed-off-by: Harvey Lynden <hlynden@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com>
In list_downloaded_images(), prevent unnecessary network calls by setting provider._best_version from metadata after creating the provider, and using metadata values directly instead of provider properties which may trigger network lookups. This fixes cascading failures where listing cached non-Fedora images would trigger network calls to Fedora servers. Reference: avocado-framework#6266 Assisted-By: Cursor-Claude-4-Sonnet Signed-off-by: Harvey Lynden <hlynden@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com>
The Fedora download URLs change periodically as versions are released, archived, or mirrors become unavailable. This has caused recurring CI failures with 'Provider not available' errors. Changes: - FedoraImageProviderBase now tries multiple mirrors in sequence - If one mirror fails (404/network error), it automatically tries next - Archives are the LAST mirror (fallback for old versions) - Use class attribute MIRRORS directly instead of instance variable - Use self._version instead of self.version to avoid network calls - Fedora 40+ uses different naming: Fedora-Cloud-Base-Generic-* - Pattern is now updated AFTER version discovery, not just at init - Better error messages showing actual version and mirrors tried Note: download.fedoraproject.org already redirects to mirrors automatically, so we only include dl.fedoraproject.org as fallback. Reference: avocado-framework#6266 Assisted-By: Cursor-Claude-4-Sonnet Signed-off-by: Harvey Lynden <hlynden@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com>
2c851ca to
c4bd3ba
Compare
Add URLError to exception handling in _feed_html_parser() to catch DNS resolution failures and other network errors that don't result in an HTTP response. Also add debug logging for network errors to aid troubleshooting. Reference: avocado-framework#6266 Assisted-By: Cursor-Claude-4-Sonnet Signed-off-by: Harvey Lynden <hlynden@redhat.com>
In list_downloaded_images(), prevent unnecessary network calls by setting provider._best_version from metadata after creating the provider, and using metadata values directly instead of provider properties which may trigger network lookups. This fixes cascading failures where listing cached non-Fedora images would trigger network calls to Fedora servers. Reference: avocado-framework#6266 Assisted-By: Cursor-Claude-4-Sonnet Signed-off-by: Harvey Lynden <hlynden@redhat.com>
The Fedora download URLs change periodically as versions are released, archived, or mirrors become unavailable. This has caused recurring CI failures with 'Provider not available' errors. Changes: - FedoraImageProviderBase now tries multiple mirrors in sequence - If one mirror fails (404/network error), it automatically tries next - Archives are the LAST mirror (fallback for old versions) - Use class attribute MIRRORS directly instead of instance variable - Use self._version instead of self.version to avoid network calls - Fedora 40+ uses different naming: Fedora-Cloud-Base-Generic-* - Pattern is now updated AFTER version discovery, not just at init - Better error messages showing actual version and mirrors tried Note: download.fedoraproject.org already redirects to mirrors automatically, so we only include dl.fedoraproject.org as fallback. Reference: avocado-framework#6266 Assisted-By: Cursor-Claude-4-Sonnet Signed-off-by: Harvey Lynden <hlynden@redhat.com>
c4bd3ba to
c21fb6f
Compare
Add fallback mirror support to FedoraImageProviderBase for handling
unavailable mirrors. Use dl.fedoraproject.org as primary mirror with
archives.fedoraproject.org as fallback for older versions.
Add dynamic image pattern selection based on Fedora version:
- < 40: Fedora-Cloud-Base-{version}-{build}.{arch}.qcow2
- = 40: Fedora-Cloud-Base-Generic.{arch}-{version}-{build}.qcow2
- >= 41: Fedora-Cloud-Base-Generic-{version}-{build}.{arch}.qcow2
Reference: PR avocado-framework#6266
Assisted-By: Cursor-Claude-4-Sonnet
Signed-off-by: Harvey Lynden <hlynden@redhat.com>
c21fb6f to
9f07561
Compare
Add fallback mirror support to FedoraImageProviderBase for handling
unavailable mirrors. Use dl.fedoraproject.org as primary mirror with
archives.fedoraproject.org as fallback for older versions.
Add dynamic image pattern selection based on Fedora version:
- < 40: Fedora-Cloud-Base-{version}-{build}.{arch}.qcow2
- = 40: Fedora-Cloud-Base-Generic.{arch}-{version}-{build}.qcow2
- >= 41: Fedora-Cloud-Base-Generic-{version}-{build}.{arch}.qcow2
Reference: PR avocado-framework#6266
Assisted-By: Cursor-Claude-4-Sonnet
Signed-off-by: Harvey Lynden <hlynden@redhat.com>
9f07561 to
e7c7e01
Compare
The Fedora download URLs and image naming conventions are subject to frequent changes as versions are released or archived. Recently, Fedora 40 introduced a new naming scheme that caused recurring CI failures with Provider not available errors.
This PR implements a more robust fetching logic to handle network instability and evolving directory structures.
Changes
URLError Support: Now catches DNS resolution failures and general network errors in addition to HTTPError.
Improved Observability: Added debug logging for network-level exceptions to simplify remote troubleshooting.
Automatic Fallback: FedoraImageProvider now attempts to reach multiple mirrors in sequence if the primary fails (e.g., a 404 or timeout).
Primary: dl.fedoraproject.org
Secondary (Archive): archives.fedoraproject.org
Dynamic Pattern Selection: Implemented version-aware naming patterns to support Fedora's evolving conventions: