From 1cd9df78e41aceb2889b57ec067326779fbf9ca8 Mon Sep 17 00:00:00 2001 From: Pipeline Agent Date: Sat, 15 Aug 2026 01:12:52 +0800 Subject: [PATCH] =?UTF-8?q?develop:=20=E5=AE=8C=E5=96=84=E9=80=9A=E7=9F=A5?= =?UTF-8?q?=E6=B8=A0=E9=81=93=E9=80=8F=E4=BC=A0=E4=B8=8E=E8=A7=84=E5=88=99?= =?UTF-8?q?ID=E7=94=9F=E6=88=90=EF=BC=8C=E6=B8=85=E7=90=86pycache?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 14 ++++++++++++++ detector/__pycache__/__init__.cpython-310.pyc | Bin 840 -> 0 bytes detector/__pycache__/__main__.cpython-310.pyc | Bin 1823 -> 0 bytes detector/__pycache__/api.cpython-310.pyc | Bin 13378 -> 0 bytes detector/__pycache__/app.cpython-310.pyc | Bin 3731 -> 0 bytes detector/__pycache__/config.cpython-310.pyc | Bin 2550 -> 0 bytes detector/__pycache__/converger.cpython-310.pyc | Bin 4049 -> 0 bytes detector/__pycache__/engine.cpython-310.pyc | Bin 7608 -> 0 bytes detector/__pycache__/models.cpython-310.pyc | Bin 7255 -> 0 bytes detector/__pycache__/notifier.cpython-310.pyc | Bin 4220 -> 0 bytes .../__pycache__/rule_loader.cpython-310.pyc | Bin 3933 -> 0 bytes detector/__pycache__/storage.cpython-310.pyc | Bin 24886 -> 0 bytes detector/api.py | 6 ++++-- detector/converger.py | 7 +++++-- detector/engine.py | 1 + detector/models.py | 6 +++++- tests/__pycache__/test_api.cpython-310.pyc | Bin 4712 -> 0 bytes .../__pycache__/test_converger.cpython-310.pyc | Bin 2992 -> 0 bytes tests/__pycache__/test_engine.cpython-310.pyc | Bin 4327 -> 0 bytes tests/__pycache__/test_models.cpython-310.pyc | Bin 2677 -> 0 bytes .../__pycache__/test_notifier.cpython-310.pyc | Bin 1339 -> 0 bytes .../test_rule_loader.cpython-310.pyc | Bin 2407 -> 0 bytes tests/test_api.py | 17 +++++++++++++++++ tests/test_converger.py | 12 ++++++++++++ tests/test_engine.py | 10 ++++++++++ 25 files changed, 68 insertions(+), 5 deletions(-) create mode 100644 .gitignore delete mode 100644 detector/__pycache__/__init__.cpython-310.pyc delete mode 100644 detector/__pycache__/__main__.cpython-310.pyc delete mode 100644 detector/__pycache__/api.cpython-310.pyc delete mode 100644 detector/__pycache__/app.cpython-310.pyc delete mode 100644 detector/__pycache__/config.cpython-310.pyc delete mode 100644 detector/__pycache__/converger.cpython-310.pyc delete mode 100644 detector/__pycache__/engine.cpython-310.pyc delete mode 100644 detector/__pycache__/models.cpython-310.pyc delete mode 100644 detector/__pycache__/notifier.cpython-310.pyc delete mode 100644 detector/__pycache__/rule_loader.cpython-310.pyc delete mode 100644 detector/__pycache__/storage.cpython-310.pyc delete mode 100644 tests/__pycache__/test_api.cpython-310.pyc delete mode 100644 tests/__pycache__/test_converger.cpython-310.pyc delete mode 100644 tests/__pycache__/test_engine.cpython-310.pyc delete mode 100644 tests/__pycache__/test_models.cpython-310.pyc delete mode 100644 tests/__pycache__/test_notifier.cpython-310.pyc delete mode 100644 tests/__pycache__/test_rule_loader.cpython-310.pyc diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f6bc6d6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +# Python +__pycache__/ +*.py[cod] +*$py.class +*.egg-info/ +.eggs/ +build/ +dist/ +.venv/ +venv/ +.coverage +htmlcov/ +.pytest_cache/ +.mypy_cache/ diff --git a/detector/__pycache__/__init__.cpython-310.pyc b/detector/__pycache__/__init__.cpython-310.pyc deleted file mode 100644 index 1190d6e6066ee7145680dafde69f0b4460204a3a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 840 zcmZ`&OHbQC5H^phD24yvYgEAk?Xl`ZDy&DG*0?prSoAf#w0^g-CcL zp%5UDN=?iGqCgz-Ke*yuuTMGk-btKFT)NVZceFb*-+c2~>glm@um+!f|NO0;<8B+y z-PH!pbJ$8Z7?GRe#I~t6v3-)WcPxB>0MCq!l^JPH?8S@N<>?i%2Rl5H%+qj?9t6#u z5bJv;qDLc3(`fK{t{ zIcoYNMzvViLZeHQ@80n9*dsd~g1j(?QNsv|LvmyOAwPjdr@|ZBCMoRHwJ=SnH0(20 z3Ra@Jwp!QHwm~~@0U=tFZbl(1Q7_G4&`bv`$i$bjP*61iSCGh9D^ahHX0G_RBk0xe z#PHZ_G(I%;623e;Jt46TtHD-(|hCm_~CzkCE921?c61xqaRc$##jvRY2E(`}JOb-#}mgn7#exz95K_LlABr z2*M(k6{qA9gkPN80kUkGKOf+1dsrZlxW diff --git a/detector/__pycache__/__main__.cpython-310.pyc b/detector/__pycache__/__main__.cpython-310.pyc deleted file mode 100644 index cf8d79c9143808e72df7884aaa05d8c3a4284aab..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1823 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 CH-x(Y diff --git a/detector/__pycache__/app.cpython-310.pyc b/detector/__pycache__/app.cpython-310.pyc deleted file mode 100644 index e876df5b90c6c0bd66dcdf5fcd039797058526e7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3731 zcmZ`+-E$Pj5#O)7+uJ+oPNxq*5+El|QkRqv2~Mh#IF1~kTuy=F0-T3+DVt?ybUN5C z&+b8?5)~1IF<@a*kt;TkL^2SYN>b&dY>Wk1|Hr&qcaom`?_kbaiDG`|{uIwqMA|tQ5s;8Nn zmoif@)=0`tdl@q$__V8gSu-p6jB9u~Gbec6&3heYhu3L#dIhr}WLdZ9b(vj)H{6of zZFUPj=k|ELX0O+0_IcaPZQgcsySKyK;q{yS-hesa4Vr`APIITX%iQJdHh0U?OA^VG zj#-IxoL9^t(s@)W7izb``TElQ>imamw=S$-nEUeK^~UAPYah(k-+Zrrd139*qc3L8 zHEzu`KDknV_)g=GS7ErabY#i!Nm`3{O#3p$sI2#1qgz=vS&fEvwu(Ok-Acj?`Sr^9g^{_CgqCQFhb`LJD8% zV?kVTKtIDD3G@{TaFp-s_NQ@g9F!N9>p@0*Ya0bZ%mjVvMM>Er8kQ7J}xD44zwvp|?=|qNb z+QoBi*F474j<|-$#f|%UExDI*o@O+zF`sK=flnQ!%{k<=@a{+d?gKL|hpAXvkQb$C z$x>oV5w+4v>r=gCOGZ3$B7nw?KO%)eO zD5-_k>8ekH)4XU`DvVa}=9`kd_%O2KAgnrEhoDbFPvbdpv#y9e-c3*0ZcXHaRrO=a zV91M%IspW{MQTh;aXs)Y5hUfEEDeXs^$8Yu7WGfDei*XtC^I%ghHXPZt}U)u59;kg z(uZU>l3pZ3NIHP<9>$qlVw$e0oO1zcq)o* z7T=YHQ%`z>o{YjPyfNfXd8(`38}aG?_oNfP-1KDAzS6W0h#In=g3^;_p9k3iG>e-8 zqs6U8gYauo>mt{Func)oKa z#b-jOtJJfrF3+5%$0vi}j)iCV22dL}8z(Db|m0{pdG96jcEzY=3kBK>)|El#X^O@&>@< z*U#w98Lh6Bb-4tOGK1Pv16%TQs}DRQ4i^GLjbbK_^l?Y42LaJI&qSiZiyoKSvN~z{ z#vRP%jUVzUctu_pfl1J18X$ODSyUlsBRDHQ4xcZ*+_&B4shix zmO{)s%i@M*c>%$O)8cu{dc9`5$qaiAKf(gT5Z4)=6vOCX6v^gh1ShJ|4w%5^B0?zF zrMztP8)-u`)Xsej=f=v<@Qh`Vz)1{G)S#0D!wi9&rvWQq&rF2@!@EoNj6IL2i%Hvo zr!6`Rz>I)Eh9~)gfB2H5c@{bBS(2;}-Ax9&aX>GIjxoe~hVj7Q&!NOhND%pwK1no2 rqEEs`O0-wP`LEkC*Pl-g?**)>5q_mf`BYKKE7(}{i00xsxm5TcE099X diff --git a/detector/__pycache__/config.cpython-310.pyc b/detector/__pycache__/config.cpython-310.pyc deleted file mode 100644 index 1a6c2fa48001d802421bfae2fdfffd9adb98bd29..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2550 zcmZ9O>2Djy6~K3ghp6T9&@JCid?-v}QF4(cHjK24$jCxu(j_Se-7nJ(XCyIRa+%qs z3>OBfR1O@YJ_00Y@*x4@G#}i4uo0v!ib{e1kNMCqF)0}+;9lvWp6?AQ+U^qE{mpxC zW@p}eGjH8k%+TOE{+%DaxeW9*HXgr%(D<&V>0TCqX_m%xMjX9HETSU^oPZU8XV3}O zLRJW5G1kwF%Rwv5L$ex-GV`*=%r&y6TYW50(o*r}Dh#!C@4c;C*B-ocee)R_4?+<>cfqz4>zv0 zKmJ9hbNj&zJ06p$%n0t)y+F! zw61>%miWF0Fb6HA%cxziyT0vL-MW{Dc0aRyyW-fMCxdmn#$~|sh3sQ|(QZ1v3=8fz zMZH3>d-p;{H_f1%;sCz(rv53pq+QZ)5m>OEPX#1#J=w?WD^=mvWnhW>DI&0w2*d5? znux;Vn;*a}&C-o(gFDqaPhW7wd9PttxR;(vgU2@4e|i5m>#fhPv_JlB>)k8u>z}0s zZ@6CCUl!b3b{%$%ukgBmjB%e=d{?AL~%c`Vf8DH0-KhhI0hWI zqKXrWO~pyYF~tLl4_isaBfz89fZ{RWacfZVF5unPkm5bSd#z!``+)ab zBkTao^WZ6skFule5IcN12>Ch2jzG93WOzn7t2qKoZ~tty{i~mJa;TTB+ve1y`kj1f z>ZK_W0z28i?E4LBGbUtU`UOu8@fF)?s>Gtzy3YkPWm52pyTS!!O`%khyik)SAY=;ErpvM}vlIyyl9FazJT42s@`tRN*yp zP^ANXQf50`_)p5rid|Xm^)lXQdOIV)!fF@2w;am6`u4cstm>Uir>Bp7`7u5o#0JWbZG8e$f#CZ z^mhQ4G&WU*Z1y!qSYVC34<{IKh=mpR0Y|#r4{WfglAFLW7FV1APO<^TgTO;cz2ig0PpScKH&ZAfa-e?_$l_Z;zPiP*%8G@fhX8Aic`SPvgZ^(4}6SG zu8~jmTN+ERXz%Mcbjac9yhy?-Wem^F)8+D6n#pF1A^|cnh>9^(TNp~*0s6mv6$bGahTE)9lMuVnU@sbsqCxIl zE?=fkSnTU5GxNFP|0(yQQcZm(GjqB(>H!qTalls=U{#%0J%vK$y))l0eQ%y-OL=h+ zWa3ayQOsrMN_3$(FP=ul5mXG}Ft24^eJw*@DHO`3axrsO97gp7ss~VgCRZ-b&6H@l zaCUA+97Sacm1<^c+B>>uQ1=|_)VQTwE(=~iQ=mm~Ur7di(d1BC^l9-j8U-%GCcruZ zr3KG)8FJhUTtHC^RGf}RsRbVXGO{S#8kU0N(DGh|TZ9-o`L999e~bY6w-F@&GD75^ zMwo0HedHfTg#6v;Cx0^x@>e5DzA;Sl7b8agY{bc*j0E|kktBZ@90)%AVWP7ECwIor z!*p=(iRa;#8kZ4Dv9MAq4N7Y+!y}DKlhU`Ew$s)0QOev3rJXEJQ2JPv1}UbnN>Y`M zD(_Tssq9f1A}|vKMqFTg1;$gwPQ^(@LwUX9-l!`0a0)HEOu!k@DaRdWp+l9&1eSK( zRo=63GqfNvi5W2U?jNc1dyEK4>Z)HV+^P0hDpbi=MWf>vl%*nK79Qx5#U^}VfJ+3H vXAwbZwD(a05e}djU+12C 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 q7k{;BM1?kbM}nIO(O&ASMFRixOpGapp$ zomsl^?w1qq#A21t|B`U>nVg&GNt||040+xNYId$L7-U?ko_4ZBnIN%v=UnaGGglWM zTz_|%SHc-9m)@() z{1HPR{q5nSFD_O8{HvwUuh7z$u_-0VL`$Yn@PbT`^9p`nQWx6c!l=-DbJ;-XKg{`o zu#Og~Gvh9^@D1az>+m2;@2~!?cyK3L9VNx!Mb&2T8Z0cWZoqRYEZQnsj|}0njvFqx z^gP~ZGO#p8<+r!0mp-e^+`w}Cgpr}IatlL+8D#jN6Fh0HFYRZ3zz2lMouI@ELOB9Q#$yJ$_pgy6?CrIboIquF7qN zcj5N06DWG@o?@=(xVeJUbK2u0ele4E{GNS1OAp^)eDJ47_a9Vlo~_P)QJFqlz4&Pl zcZ#0h6AW?3AM#v#uk$uOwAXe5CmVRYXQcu!hW~?R4L=udLrqx?Pb$x zzKLF>ovtcq9XyI+11{f0VPQ!fs%%>q2LJpf1~$QPfdXh4D7y3qT)f|DiN)qhl{OCn%9 zDrHty(#oKMk_GCVHcvI(R{9lb*rdUq!zk&AKt#!X847Czg_j)>M%u+Ksq#(?k|Wm` zjLe=ZwUnPX+ZXI`0SbI{p&Hm(KwurX|w)#3(#DpJ;L)z)%QT-&&) zkL$A6D(e)~7uA_f;|S%lF&xzuO&MX{9y>as6jT)eJC49>Ku`kkyG;N@%q3%eV@4@B zxp)6q{D=o421OnlEjnWzfSjAlN`2}X_C2sWSw9JewlhISM1~NT4)=wbcY-0$UN*>3 zOhkfQ-tkJo^55~C+XM1X#)K{>Fziyk=wpPJwn^qpr;zn*N0_1PiU@a3l^iHv2l(_!g zX>MgNel^zF?ATg}NJumztPvud*mBjEB!5fR{uVljhgZje#;NtGI3T+nB6<7)Down- z&E76kD3b3TGCk74+uQ7&_C|XXINWq1czVv>VsD)?CRxgojPks&psZ5SO8YQnsPC0& zq$8K1?2i|&BBiNZKEHJ5ceTuD`Zr7Sx2or_EKmGeDt?6a4%a?;VMXRCEg`MnlW5Yz zfq@2dtRHw}zA`InIb!<%$@~W~QZgR@I@MmFf>^K~pt8TAlG4-^px?tKsXi7`{f4>) zR^qe4b%2rFUZ|oP-mL$qPS+9g2Jt`m92{R}2M|Ved19Ov0n%;=&>=LeLIJHxSlk&z z66bL0l-vOYYAmbX+mIe%)zs8~(zRlC$^&JJHSi?5Qg2eF2&RwXl54(%3TVR$YML~! zOtNuxP#K3Z^(q4|kLyyaw6YGGG0KL`N?`ReKiIN zeRVk`l@qErl8g%7g&CyTg+Ahp`a(kv8fwrkj0yom(I%xa8CgXS6jLHQkfgUR&L%NSj~Hnlu4S9$N<%AJoF&rK)lb2m;D zv%jy*-b3oUG=HII<#>UEgdxvGB62#^Q(@IlMZydZMOn(o>pvA0qM&{PJ?TD0=|BW4 zV}KD>^nrQ`$%`~ql1V8~szUAFCs=l^{bC1**Rx)}coiN zp2z_*C}sRN^-Dpa#3ft+!65AKrT6uussV~_nGcY<$oGjtIh0_El@9~$YAi@-Fp^|pjgp_}W z8qHv|hyxgu;b$0$2Sji<( diff --git a/detector/__pycache__/rule_loader.cpython-310.pyc b/detector/__pycache__/rule_loader.cpython-310.pyc deleted file mode 100644 index 4674d88cafe3fc79e9e1f3b02006120afcea52f8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3933 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 MC~2y}`PQrc4|Q=eOaK4? diff --git a/detector/__pycache__/storage.cpython-310.pyc b/detector/__pycache__/storage.cpython-310.pyc deleted file mode 100644 index 3911cedb617ad5d99e6203bd74ddc94f670f8959..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24886 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 ax6pe{qAS$_Uy%IuCVR8JJ-ti&SNuQR=#OFm diff --git a/detector/api.py b/detector/api.py index 4cf3e8c..ec52e89 100644 --- a/detector/api.py +++ b/detector/api.py @@ -17,6 +17,7 @@ import json import re import threading import time +import uuid from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer from typing import Any, Callable, Dict, List, Optional, Tuple from urllib.parse import urlparse @@ -170,9 +171,10 @@ class APIServer: def handle_create_rule(self, handler) -> None: data = handler._read_json() + # api-design:POST /rules 允许不传 rule_id,服务端自动生成。 + if not data.get("rule_id"): + data["rule_id"] = f"r-{uuid.uuid4().hex[:12]}" rule = MetricRule.from_dict(data) - if not rule.rule_id: - raise _APIError(400, 40001, "rule_id is required") created = self.app.rule_store.create_rule(rule) self.app.rule_loader.load() handler._write_json(200, {"code": 0, "message": "ok", "data": created.to_dict()}) diff --git a/detector/converger.py b/detector/converger.py index a6f9f61..1fe9bfd 100644 --- a/detector/converger.py +++ b/detector/converger.py @@ -73,11 +73,14 @@ class AlertConverger: count=1, first_at=event.fired_at, last_at=event.fired_at, + notify_channels=list(event.notify_channels), ) stored = self.alert_store.upsert_firing(alert) + # 聚合更新时使用已存在告警的渠道列表,避免覆盖丢失 + channels = stored.notify_channels or event.notify_channels self._publish(stored) if self.notifier is not None and hasattr(self.notifier, "notify"): - self.notifier.notify(stored, getattr(stored, "notify_channels", None) or self._channels(event)) + self.notifier.notify(stored, channels) return stored def _handle_resolved(self, event: Event) -> Optional[Alert]: @@ -98,4 +101,4 @@ class AlertConverger: @staticmethod def _channels(event: Event) -> List[str]: - return [] + return list(event.notify_channels) diff --git a/detector/engine.py b/detector/engine.py index 75c98f8..9f477f8 100644 --- a/detector/engine.py +++ b/detector/engine.py @@ -227,6 +227,7 @@ class DetectionEngine: severity=rule.severity, fired_at=now, labels=dict(sample.labels), + notify_channels=list(rule.notify_channels), ) # ------------------------------------------------------------------ # diff --git a/detector/models.py b/detector/models.py index fca3db2..0077113 100644 --- a/detector/models.py +++ b/detector/models.py @@ -153,7 +153,7 @@ class MetricRule: @classmethod def from_dict(cls, data: Dict[str, Any]) -> "MetricRule": return cls( - rule_id=str(data["rule_id"]), + rule_id=str(data.get("rule_id") or ""), name=str(data.get("name") or ""), metric=str(data.get("metric") or ""), aggregation=str(data.get("aggregation") or "avg"), @@ -185,6 +185,7 @@ class Event: severity: str fired_at: float labels: Dict[str, str] = field(default_factory=dict) + notify_channels: List[str] = field(default_factory=list) def to_dict(self) -> Dict[str, Any]: return { @@ -199,6 +200,7 @@ class Event: "severity": self.severity, "fired_at": self.fired_at, "labels": self.labels, + "notify_channels": list(self.notify_channels), } @@ -219,6 +221,7 @@ class Alert: ack_status: str = "open" ack_by: Optional[str] = None ack_at: Optional[float] = None + notify_channels: List[str] = field(default_factory=list) def to_dict(self) -> Dict[str, Any]: return { @@ -235,4 +238,5 @@ class Alert: "ack_status": self.ack_status, "ack_by": self.ack_by, "ack_at": self.ack_at, + "notify_channels": list(self.notify_channels), } diff --git a/tests/__pycache__/test_api.cpython-310.pyc b/tests/__pycache__/test_api.cpython-310.pyc deleted file mode 100644 index b6c02b516f58f1b1cfa0806758e6e05f94316efb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4712 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 diff --git a/tests/__pycache__/test_notifier.cpython-310.pyc b/tests/__pycache__/test_notifier.cpython-310.pyc deleted file mode 100644 index 4d42212f486eb373e3a209f0328c64dcfe5db9cc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1339 zcmZuwOOG2x5bmCrXT9Ftuu6DD5DBCp&B1GyBZ|UC5g-ouvJnSgMiW=9cj9@N?l!@e zS2-k7xU9qv$ia@dYC}Usp_xl`?}uPXbT#@efr?u zu`k5$ytp_5TI|u(eKH_G7Gl6}uaKoT@FaixgA=nq;L zLp1i(brj3Bm7oZ@?`G0^ML9)3kv6I1_{rK?~yJ@W@-prN8h-0@j|&m3QPX#4=d;ClS;RUYl?ssdNeLm1_`TSnt~q z%PcQ!>ptE+tn2aI&$_$w)?s(|6V=_da%|(F%1u7Z3L72dDz8S*LK|n?m^25w1_;wh zI>wpZ$VMZDqs*XN#u~px3Z2CQCygffijt_7{B6zUSc+GpQP34>o?Wdn_sNdkTs2R3&95g9gguozR3)7c2mgkm241~NV9P4WqO zD(u~_)mTrmA?n^oy}y3_;m_wk{qghj7e79J`Q(=u-#>nN{IsWVQtO^MRHzT@0=iU8 zWx9X{ho)9NL)4L*Y!OAi1wnV< zTo9kr!jM)UvJ^-Z=VEYD5X%!eK)fQkG~^TYA*{V4piPtZ`GzeP72%^)}>-_HZqwSp-Cde_!`#Q4O@5A6dg~XuCVVdG9-Yv zT^P|vw@6)O2lLaI_tH;mkF zohoF9PQyYsO?l&N_f#xR$DHV6F6DMc8t1drhh%KPsb7c3#y6TWZZZRU(}C)Em*ln| JZ*^qw&%dB?X;1(F diff --git a/tests/__pycache__/test_rule_loader.cpython-310.pyc b/tests/__pycache__/test_rule_loader.cpython-310.pyc deleted file mode 100644 index 38f22515aabe8a7ab75ca58d877126cdd715b826..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2407 zcmah~OK%)S5bnp!?Ck7%{YV5Tpa>8L23W5H5(gAHk-|er#0N|+BSoX}bhD4lW79pG z$d)4xiIiMcf)f`GYs4jY4u~B1A1!f$1pEWI=&?Flyr5c3O^QNKJr)&&otd6*9B*X>nENrP{rcqyuSI=R>=rDwRyp{Nl%8a2c9@ z2ZSUM6(nMU3U-e~TyWvQzf-#(xuPL_7<;0*O~Sx*zL2^)K0l>kV*X*@g?0s+eiKAR z?vi~H(~8-DUOD?@M4@p!ZOUQI6$I?^EyFKtXt>+5k{R4A9u}2Uw~{RM6vT)aSL0Qb zXZtDMRN+y^Lv5)ZQsZcueff~66`203527b}TPKl$$G#}Dp5nSS3>a$M?OHBeFy%1m_SEm>4uAuE}}(Y*@p4a*>71@Oo9 zx$EOya7|X%#!Wa;qkBH}=}UC)>`9>3tsPGW_DHF}9 zcLzaZGrms`Ceuv@wsIkw)671~23;;ay#j;qrNA~%LHh_rR4`bM=kVg->dKpi0 zfddoZe>KbjdPs(+e9*lRQhUY;IFZ#H+BP)32BHtwyzT;YbVR;kH`#X04cL1QR>5Vq zzG0}cgsH_U%$wk1Hq=#G=Hty*&=lHTBYW>Z7f;IsOVy+hah_D^VEtlNrb(tZp))sO znAi!M3XLcXxN3ngtyIaRl9owo31=KMrOn>C{hzB zhJ++DegKiENr$ur${vw?>eO_eHIR%m|G@4aHJ@oOSgL}#Eg-RXMAU{Q>br*LGB+zA zfT&W$z^@dN_wX8c1kho)Et?bqpw5FhM9;&P#a_LIvsepFvoGuP);3Nm#1)L4>D1BK zD(irl7HZy-N)OAz_(wgS$wDF4&&TAPjme+{^L2sS*`05C4b(MOt$?mkk9DZ?_X|!y z&(Xc`v~Zki<3v=DMOcY!RJNeASXicxEhZGAGcP85=GbE5LNTd}jx8oL4>0Et8z?~s zY@s{^7WMaxBZm5hSw(pQ_mBUDxoOBE*~x&auYx-^!4+Oxy#XTh>-50Ps1e7EsH1{c zv7u3Kqb9d$pt?A_jsjs@$ldc6WpKzIRu}35Fn^XPxKCrrU7>9Xv`37)r(yh`7Pi6$ zL_5ZM7RM%tzVJ@fkLAkI627KT(d~O$Kd?UH pa3z6~gZ`*XXv)FkPxIUO7wd~}VjfxY`s6HY(-peL{cam5{0}J0Whnpv diff --git a/tests/test_api.py b/tests/test_api.py index cc2da54..a23777d 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -73,6 +73,23 @@ class TestAPI(unittest.TestCase): self.assertEqual(status, 200) self.assertEqual(body["data"]["threshold"], 5.0) + def test_create_rule_autogenerates_id(self): + payload = { + "name": "load high", + "metric": "load_1m", + "aggregation": "avg", + "operator": "gt", + "threshold": 5.0, + "for_duration": "60s", + "severity": "warning", + "scope": {"scope_type": "all"}, + "notify_channels": ["email"], + "enabled": True, + } + status, body = self._request("POST", "/api/v1/rules", payload) + self.assertEqual(status, 200) + self.assertTrue(body["data"]["rule_id"].startswith("r-")) + def test_ingest_triggers_firing_event(self): samples = [ {"name": "cpu_usage", "value": 95.0, "timestamp": float(i * 15), "labels": {"service": "web"}} diff --git a/tests/test_converger.py b/tests/test_converger.py index 0f4b181..419325b 100644 --- a/tests/test_converger.py +++ b/tests/test_converger.py @@ -2,6 +2,7 @@ import unittest from detector.converger import AlertConverger from detector.models import Event +from detector.notifier import Notifier from detector.storage import InMemoryAlertStore, InMemoryCache, InMemoryMessageBus @@ -66,6 +67,17 @@ class TestAlertConverger(unittest.TestCase): self.assertEqual(topic, "alerts.converged") self.assertIn("alert_id", value) + def test_notify_channels_propagated(self): + notifier = Notifier.memory(["email", "webhook"]) + conv = AlertConverger( + self.store, self.cache, self.bus, dedup_window="5m", aggregate_window="1m", + host_group_resolver=lambda h: "web", notifier=notifier, + ) + event = make_event() + event.notify_channels = ["email"] + conv.handle_event(event) + self.assertEqual(notifier.sent[0]["channel"], "email") + if __name__ == "__main__": unittest.main() diff --git a/tests/test_engine.py b/tests/test_engine.py index a2469e5..265b771 100644 --- a/tests/test_engine.py +++ b/tests/test_engine.py @@ -17,6 +17,7 @@ def make_rule(rule_id="r-cpu", metric="cpu_usage", operator="gt", threshold=90.0 for_duration=for_duration, severity="critical", scope=RuleScope(scope_type="all"), + notify_channels=["email", "webhook"], ) @@ -102,6 +103,15 @@ class TestStateMachine(unittest.TestCase): self.assertEqual(events, []) self.assertEqual(engine.state_of("h-1", rule.rule_id)["status"], "firing") + def test_event_carries_notify_channels(self): + rule = make_rule(for_duration="60s") + engine = make_engine(rule) + events = [] + for i in range(5): + events.extend(engine.handle(MetricSample(name="cpu_usage", value=95.0, timestamp=float(i * 15)), "h-1")) + self.assertEqual(len(events), 1) + self.assertEqual(events[0].notify_channels, ["email", "webhook"]) + if __name__ == "__main__": unittest.main()