feat(detmir): add rust-first operations tooling

This commit is contained in:
igor04091968
2026-06-02 17:57:58 +03:00
parent 60670d30a8
commit 19e3682bc8
263 changed files with 51678 additions and 718 deletions
@@ -2,10 +2,11 @@
Description=Run AWatch DLP CEF Exporter every 5 minutes
[Timer]
OnBootSec=2min
OnUnitActiveSec=5min
OnCalendar=*:6/10:30
AccuracySec=30s
RandomizedDelaySec=30s
Unit=aw-dlp-cef-exporter.service
Persistent=true
Persistent=false
[Install]
WantedBy=timers.target
@@ -2,9 +2,10 @@
Description=Run AWatch DLP syslog forwarder every 3 minutes
[Timer]
OnBootSec=3min
OnUnitActiveSec=3min
Persistent=true
OnCalendar=*:7/10:40
AccuracySec=30s
RandomizedDelaySec=30s
Persistent=false
Unit=aw-dlp-syslog-forwarder.service
[Install]
@@ -57,7 +57,7 @@ def iter_new_incidents(aw_base: str, state: dict[str, Any], per_bucket_limit: in
for bid in bucket_ids:
try:
events = http_json(f"{aw_base}/buckets/{bid}/events?limit={int(per_bucket_limit)}")
except error.HTTPError as exc:
except (TimeoutError, OSError, error.URLError, error.HTTPError) as exc:
LOG.warning("skip bucket %s: %s", bid, exc)
continue
prev = int(last_ids.get(bid, 0))
@@ -126,7 +126,11 @@ def main() -> None:
app_name = str(cfg.get("app_name", "aw-dlp"))
state = load_json(state_path)
incidents, max_ids = iter_new_incidents(aw_base=aw_base, state=state, per_bucket_limit=per_bucket_limit)
try:
incidents, max_ids = iter_new_incidents(aw_base=aw_base, state=state, per_bucket_limit=per_bucket_limit)
except (TimeoutError, OSError, error.URLError, error.HTTPError) as exc:
LOG.warning("skip syslog forwarder run: AW API unavailable: %s", exc)
return
sent = 0
for event in incidents:
@@ -0,0 +1,59 @@
#!/usr/bin/env python3
import importlib.util
import json
import sys
from pathlib import Path
MODULE_PATH = Path(__file__).with_name("syslog_forwarder.py")
SPEC = importlib.util.spec_from_file_location("syslog_forwarder", MODULE_PATH)
MODULE = importlib.util.module_from_spec(SPEC)
sys.modules[SPEC.name] = MODULE
SPEC.loader.exec_module(MODULE)
def test_iter_new_incidents_skips_timed_out_bucket(monkeypatch):
def fake_http_json(url, timeout=15):
if url.endswith("/buckets/"):
return {"aw-dlp-incidents_SHARKON2025": {}}
raise TimeoutError("timed out")
monkeypatch.setattr(MODULE, "http_json", fake_http_json)
incidents, max_ids = MODULE.iter_new_incidents(
aw_base="http://127.0.0.1:5600/api/0",
state={"last_ids": {"aw-dlp-incidents_SHARKON2025": 42}},
per_bucket_limit=300,
)
assert incidents == []
assert max_ids == {}
def test_main_skips_aw_api_timeout_without_overwriting_state(monkeypatch, tmp_path):
state_path = tmp_path / "syslog-forwarder-state.json"
original_state = {"last_ids": {"aw-dlp-incidents_SHARKON2025": 99}}
state_path.write_text(json.dumps(original_state), encoding="utf-8")
monkeypatch.setattr(
MODULE,
"load_yaml",
lambda path: {
"aw_api_base": "http://127.0.0.1:5600/api/0",
"state_path": str(state_path),
},
)
monkeypatch.setattr(
MODULE,
"iter_new_incidents",
lambda aw_base, state, per_bucket_limit: (_ for _ in ()).throw(TimeoutError("timed out")),
)
monkeypatch.setattr(
MODULE,
"save_json",
lambda path, payload: (_ for _ in ()).throw(AssertionError("state should not be saved on AW API timeout")),
)
MODULE.main()
assert json.loads(state_path.read_text(encoding="utf-8")) == original_state
@@ -2,10 +2,11 @@
Description=Run AWatch DLP Webhook sender every 2 minutes
[Timer]
OnBootSec=90s
OnUnitActiveSec=2min
OnCalendar=*:8/10:45
AccuracySec=30s
RandomizedDelaySec=30s
Unit=aw-dlp-webhook-sender.service
Persistent=true
Persistent=false
[Install]
WantedBy=timers.target