fix(ansible): git sources work in the image and survive a redeploy; pulls time out; alert vars go in a JSON file (#5157)
- git ships in the image; checkouts live under /data/ansible/sources (STEWARD_ANSIBLE_SOURCE_DIR), on the persistent volume. - One task, ansible_git_pulls, reads the sources every 30 s and pulls each as its interval passes: a source added in Settings is pulled without a restart, a removed one is forgotten. - git_pull: a 120 s timeout ending the process group, GIT_TERMINAL_PROMPT=0, pull --ff-only, a failed clone removed; it raises GitPullError with git's message, so the Sync button stops saying Synced after a failure (and the message is now escaped). - An alert action's vars and the firing's steward_alert_* vars reach ansible-playbook as one JSON extra-vars file, not -e key=value, which Ansible split on whitespace (a rule named 'High CPU on web'). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+29
-13
@@ -34,21 +34,35 @@ def init_alerts(app: "Quart") -> None:
|
||||
_app = app
|
||||
|
||||
|
||||
def alert_extra_vars(rule: AlertRule, value: float) -> list[str]:
|
||||
"""Ansible extra-vars describing the alert that fired, as `key=value` strings.
|
||||
def alert_extra_vars(rule: AlertRule, value: float) -> dict:
|
||||
"""Ansible extra-vars describing the alert that fired.
|
||||
|
||||
Injected into every alert-triggered playbook run so the playbook can target
|
||||
the exact resource/metric that fired without templating. Pure function.
|
||||
the exact resource/metric that fired without templating. They reach
|
||||
ansible-playbook in its JSON extra-vars file, not as `-e key=value`, which
|
||||
Ansible splits on whitespace: a rule named "High CPU on web" stays one
|
||||
value (#5157). Pure function.
|
||||
"""
|
||||
return [
|
||||
f"steward_alert_rule={rule.name}",
|
||||
f"steward_alert_source={rule.source_module}",
|
||||
f"steward_alert_resource={rule.resource_name}",
|
||||
f"steward_alert_metric={rule.metric_name}",
|
||||
f"steward_alert_value={value}",
|
||||
f"steward_alert_threshold={rule.threshold}",
|
||||
f"steward_alert_operator={rule.operator.value}",
|
||||
]
|
||||
return {
|
||||
"steward_alert_rule": rule.name,
|
||||
"steward_alert_source": rule.source_module,
|
||||
"steward_alert_resource": rule.resource_name,
|
||||
"steward_alert_metric": rule.metric_name,
|
||||
"steward_alert_value": value,
|
||||
"steward_alert_threshold": rule.threshold,
|
||||
"steward_alert_operator": rule.operator.value,
|
||||
}
|
||||
|
||||
|
||||
def action_vars(lines) -> dict:
|
||||
"""An alert action's stored `key=value` lines as a map, split at the first
|
||||
`=` so a value keeps its spaces, quotes and further `=`."""
|
||||
out: dict = {}
|
||||
for line in lines or ():
|
||||
key, sep, val = str(line).partition("=")
|
||||
if sep and key.strip():
|
||||
out[key.strip()] = val
|
||||
return out
|
||||
|
||||
|
||||
async def record_metric(
|
||||
@@ -489,8 +503,10 @@ async def _run_ansible_action(
|
||||
playbook = action.get("playbook") or ""
|
||||
inventory = action.get("inventory") or ""
|
||||
source_name = action.get("source") or ""
|
||||
# One JSON extra-vars file; the firing's own vars win over the action's.
|
||||
params: dict = {
|
||||
"extra_vars": list(action.get("extra_vars") or []) + alert_extra_vars(rule, value),
|
||||
"extra_vars_map": {**action_vars(action.get("extra_vars")),
|
||||
**alert_extra_vars(rule, value)},
|
||||
}
|
||||
if action.get("limit"):
|
||||
params["limit"] = action["limit"]
|
||||
|
||||
Reference in New Issue
Block a user