@@ -103,6 +103,65 @@ fn ensure_schedulable_in_oss(alert: &dyn AlertTrait) -> Result<(), AlertError> {
103103 Ok ( ( ) )
104104}
105105
106+ async fn parse_alert_config ( alert_bytes : & [ u8 ] , tenant : & Option < String > ) -> Option < AlertConfig > {
107+ let json_value: JsonValue = match serde_json:: from_slice ( alert_bytes) {
108+ Ok ( val) => val,
109+ Err ( e) => {
110+ error ! ( "Failed to parse alert JSON: {e}" ) ;
111+ return None ;
112+ }
113+ } ;
114+
115+ match json_value[ "version" ] . as_str ( ) {
116+ Some ( "v1" ) => migrate_v1_alert ( & json_value, tenant, "Failed to migrate v1 alert" ) . await ,
117+ Some ( _) if json_value[ "query" ] . is_null ( ) || json_value. get ( "stream" ) . is_some ( ) => {
118+ migrate_v1_alert ( & json_value, tenant, "Failed to migrate v1 alert" ) . await
119+ }
120+ Some ( _) => match serde_json:: from_value :: < AlertConfig > ( json_value) {
121+ Ok ( alert) => Some ( alert) ,
122+ Err ( e) => {
123+ error ! ( "Failed to parse v2 alert: {e}" ) ;
124+ None
125+ }
126+ } ,
127+ None => {
128+ warn ! ( "Found alert without version field, assuming v1 and migrating" ) ;
129+ migrate_v1_alert (
130+ & json_value,
131+ tenant,
132+ "Failed to migrate alert without version" ,
133+ )
134+ . await
135+ }
136+ }
137+ }
138+
139+ async fn migrate_v1_alert (
140+ json_value : & JsonValue ,
141+ tenant : & Option < String > ,
142+ error_context : & str ,
143+ ) -> Option < AlertConfig > {
144+ match AlertConfig :: migrate_from_v1 ( json_value, tenant) . await {
145+ Ok ( migrated) => Some ( migrated) ,
146+ Err ( e) => {
147+ error ! ( "{error_context}: {e}" ) ;
148+ None
149+ }
150+ }
151+ }
152+
153+ fn alert_from_config_oss ( alert : AlertConfig ) -> anyhow:: Result < Box < dyn AlertTrait > > {
154+ match & alert. alert_type {
155+ AlertType :: Threshold => Ok ( Box :: new ( ThresholdAlert :: from ( alert) ) as Box < dyn AlertTrait > ) ,
156+ AlertType :: Anomaly ( _) => Err ( anyhow:: Error :: msg (
157+ AlertError :: NotPresentInOSS ( "anomaly" ) . to_string ( ) ,
158+ ) ) ,
159+ AlertType :: Forecast ( _) => Err ( anyhow:: Error :: msg (
160+ AlertError :: NotPresentInOSS ( "forecast" ) . to_string ( ) ,
161+ ) ) ,
162+ }
163+ }
164+
106165pub fn create_default_alerts_manager ( ) -> Alerts {
107166 let ( tx, rx) = mpsc:: channel :: < AlertTask > ( 1000 ) ;
108167 let alerts = Alerts {
@@ -1107,81 +1166,14 @@ impl AlertManagerTrait for Alerts {
11071166 & Some ( tenant_id. clone ( ) )
11081167 } ;
11091168 for alert_bytes in raw_bytes {
1110- // First, try to parse as JSON Value to check version
1111- let json_value: JsonValue = match serde_json:: from_slice ( & alert_bytes) {
1112- Ok ( val) => val,
1113- Err ( e) => {
1114- error ! ( "Failed to parse alert JSON: {e}" ) ;
1115- continue ;
1116- }
1117- } ;
1118-
1119- // Check version and handle migration
1120- let mut alert = if let Some ( version_str) = json_value[ "version" ] . as_str ( ) {
1121- if version_str == "v1"
1122- || json_value[ "query" ] . is_null ( )
1123- || json_value. get ( "stream" ) . is_some ( )
1124- {
1125- // This is a v1 alert that needs migration
1126- match AlertConfig :: migrate_from_v1 ( & json_value, tenant) . await {
1127- Ok ( migrated) => migrated,
1128- Err ( e) => {
1129- error ! ( "Failed to migrate v1 alert: {e}" ) ;
1130- continue ;
1131- }
1132- }
1133- } else {
1134- // Try to parse as v2
1135- match serde_json:: from_value :: < AlertConfig > ( json_value) {
1136- Ok ( alert) => alert,
1137- Err ( e) => {
1138- error ! ( "Failed to parse v2 alert: {e}" ) ;
1139- continue ;
1140- }
1141- }
1142- }
1143- } else {
1144- // No version field, assume v1 and migrate
1145- warn ! ( "Found alert without version field, assuming v1 and migrating" ) ;
1146- match AlertConfig :: migrate_from_v1 ( & json_value, tenant) . await {
1147- Ok ( migrated) => migrated,
1148- Err ( e) => {
1149- error ! ( "Failed to migrate alert without version: {e}" ) ;
1150- continue ;
1151- }
1152- }
1169+ let Some ( mut alert) = parse_alert_config ( & alert_bytes, tenant) . await else {
1170+ continue ;
11531171 } ;
11541172
11551173 // ensure that alert config's tenant is correctly set
11561174 alert. tenant_id . clone_from ( tenant) ;
11571175
1158- let alert: Box < dyn AlertTrait > = match & alert. alert_type {
1159- AlertType :: Threshold => {
1160- let alert = ThresholdAlert :: from ( alert) ;
1161- if let Err ( e) = alert. validate_oss_query_type ( ) {
1162- warn ! (
1163- "Skipping unsupported alert task for alert {}: {e}" ,
1164- alert. id
1165- ) ;
1166- map. entry ( tenant_id. clone ( ) )
1167- . or_default ( )
1168- . insert ( alert. id , Box :: new ( alert) as Box < dyn AlertTrait > ) ;
1169- continue ;
1170- }
1171-
1172- Box :: new ( alert) as Box < dyn AlertTrait >
1173- }
1174- AlertType :: Anomaly ( _) => {
1175- return Err ( anyhow:: Error :: msg (
1176- AlertError :: NotPresentInOSS ( "anomaly" ) . to_string ( ) ,
1177- ) ) ;
1178- }
1179- AlertType :: Forecast ( _) => {
1180- return Err ( anyhow:: Error :: msg (
1181- AlertError :: NotPresentInOSS ( "forecast" ) . to_string ( ) ,
1182- ) ) ;
1183- }
1184- } ;
1176+ let alert = alert_from_config_oss ( alert) ?;
11851177
11861178 // Create alert task iff alert's state is not paused
11871179 if alert. get_state ( ) . eq ( & AlertState :: Disabled ) {
@@ -1191,6 +1183,17 @@ impl AlertManagerTrait for Alerts {
11911183 continue ;
11921184 }
11931185
1186+ if let Err ( e) = ensure_schedulable_in_oss ( alert. as_ref ( ) ) {
1187+ warn ! (
1188+ "Skipping unsupported alert task for alert {}: {e}" ,
1189+ alert. get_id( )
1190+ ) ;
1191+ map. entry ( tenant_id. clone ( ) )
1192+ . or_default ( )
1193+ . insert ( * alert. get_id ( ) , alert) ;
1194+ continue ;
1195+ }
1196+
11941197 match self . sender . send ( AlertTask :: Create ( alert. clone_box ( ) ) ) . await {
11951198 Ok ( _) => { }
11961199 Err ( e) => {
0 commit comments