Files
computer-playbook/plugins/filter/curl.py
T
kevinveenbirkenbachandClaude Fable 5 15f95f0725 fix(curl): timeout-hardened curl SPOT filter, lint, and fleet-wide rollout
A curl against an accepted-but-silent peer hangs forever: task retries
never fire because the first attempt never returns, and the CI job dies
at the 6h runner cut (proven by the mailu admin-API hang that took the
suitecrm/peertube swarm legs down). The new `curl` filter renders the
hardened base command ("{{ 30 | curl }}" -> curl -s --connect-timeout 5
--max-time 30); scaling composes with lookup('timeout').

test_curl_via_filter enforces it: bare curl invocations in role tasks,
templates and shipped scripts fail the lint with a pointer to the
filter. Plain .sh files, which cannot render Jinja, pass by pinning
--max-time inline; Docker healthchecks are exempt (their timeout field
caps them); `# nocheck: curl-timeout` opts out (catalogued in
suppression.md).

Convert all 34 existing invocations: mailu (replacing the short-lived
MAILU_CURL var), elasticsearch, checkmk, yourls (300s for the
schema-creating installer POST), the telegram alert script, and the
plain scripts of jellyfin, svc-runner, apt_repo, moodle (600s tarball)
and the mattermost provisioner. Mailu's user/token tasks additionally
carry task-level timeouts so a wedged container exec cannot stall the
play.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 02:37:28 +02:00

44 lines
1.5 KiB
Python

"""Render the timeout-hardened curl base command (SPOT).
{{ 30 | curl }} -> curl -s --connect-timeout 5 --max-time 30
{{ 300 | curl }} -> curl -s --connect-timeout 5 --max-time 300
{{ 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).
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) -> str:
try:
max_time_val = int(max_time)
connect_timeout_val = int(connect_timeout)
except (TypeError, ValueError) as exc:
raise AnsibleFilterError(
f"curl filter: max_time {max_time!r} / connect_timeout "
f"{connect_timeout!r} must be integers (seconds)"
) 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"
)
return f"curl -s --connect-timeout {connect_timeout_val} --max-time {max_time_val}"
class FilterModule:
def filters(self):
return {
"curl": curl,
}