Files
computer-playbook/plugins/filter/curl.py
T
kevinveenbirkenbachandClaude Opus 4.8 07d31d9001 fix(ci): retry-harden every egress path (curl filter, shell downloads, package managers)
Transient CDN/mirror TLS resets are a recurring hard-fail class (docker.com, PyPI). Harden all three egress paths at their SPOT: the curl filter now emits --retry-all-errors (retries=0 opts a call out), so every ansible curl retries; a new lint requires --retry-all-errors on file-downloading curl and --tries on wget in verbatim files/*.sh (which cannot use the filter), fixing the moodle/alias downloads it flagged; and the update role configures native retries for every package manager - apt Acquire::Retries, dnf retries=, pacman XferCommand over retrying curl - covering all five CI distros (ubuntu shares apt, centos shares dnf).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-23 20:01:31 +02:00

53 lines
2.1 KiB
Python

"""Render the timeout-hardened, retry-hardened curl base command (SPOT).
{{ 30 | curl }} -> curl -s --connect-timeout 5 --max-time 30 --retry 3 --retry-all-errors --retry-delay 2
{{ 300 | curl }} -> curl -s --connect-timeout 5 --max-time 300 --retry 3 --retry-all-errors --retry-delay 2
{{ lookup('timeout', 30) | curl }} -> TIMEOUT_FACTOR-scaled variant
Args:
max_time: Seconds for the whole transfer; without it curl hangs forever
on an accepted-but-silent peer and task retries never fire.
connect_timeout: Seconds curl waits for the TCP/TLS connect (default 5).
retries: --retry count; --retry-all-errors makes it cover transport-layer
resets (exit 35/52/56), the class that hard-fails deploys on flaky CI
egress. Pass 0 to opt a call out of retrying.
Callers append their own flags (``-S``, ``-f``, ``-o``, ...) after the filter.
"""
from __future__ import annotations
from typing import Any
from ansible.errors import AnsibleFilterError
def curl(max_time: Any, connect_timeout: Any = 5, retries: Any = 3) -> str:
try:
max_time_val = int(max_time)
connect_timeout_val = int(connect_timeout)
retries_val = int(retries)
except (TypeError, ValueError) as exc:
raise AnsibleFilterError(
f"curl filter: max_time {max_time!r} / connect_timeout "
f"{connect_timeout!r} / retries {retries!r} must be integers"
) from exc
if max_time_val <= 0 or connect_timeout_val <= 0:
raise AnsibleFilterError(
f"curl filter: max_time {max_time_val} and connect_timeout "
f"{connect_timeout_val} must be > 0"
)
if retries_val < 0:
raise AnsibleFilterError(f"curl filter: retries {retries_val} must be >= 0")
base = f"curl -s --connect-timeout {connect_timeout_val} --max-time {max_time_val}"
if retries_val:
base += f" --retry {retries_val} --retry-all-errors --retry-delay 2"
return base
class FilterModule:
def filters(self):
return {
"curl": curl,
}