Skip to content

Commit 2b53b88

Browse files
committed
Bring targets out of alerts
Adds CRUD operations for targets TODO- - Storage of targets depending upon RBAC discussion - Modify alerts to use targets using ID (AlertConfig update)
1 parent 97e5d42 commit 2b53b88

File tree

6 files changed

+188
-15
lines changed

6 files changed

+188
-15
lines changed

src/alerts/mod.rs

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ use ulid::Ulid;
3737
pub mod alerts_utils;
3838
pub mod target;
3939

40+
use crate::alerts::target::TARGETS;
4041
use crate::parseable::{StreamNotFound, PARSEABLE};
4142
use crate::rbac::map::SessionKey;
4243
use crate::storage;
@@ -513,23 +514,28 @@ pub struct AlertRequest {
513514
pub alert_type: AlertType,
514515
pub aggregates: Aggregates,
515516
pub eval_config: EvalConfig,
516-
pub targets: Vec<Target>,
517+
pub targets: Vec<Ulid>,
517518
}
518519

519-
impl From<AlertRequest> for AlertConfig {
520-
fn from(val: AlertRequest) -> AlertConfig {
521-
AlertConfig {
520+
impl AlertRequest {
521+
pub async fn into(self) -> Result<AlertConfig, AlertError> {
522+
let mut targets = Vec::new();
523+
for id in self.targets {
524+
targets.push(TARGETS.get_target_by_id(id).await?);
525+
}
526+
let config = AlertConfig {
522527
version: AlertVerison::from(CURRENT_ALERTS_VERSION),
523528
id: Ulid::new(),
524-
severity: val.severity,
525-
title: val.title,
526-
stream: val.stream,
527-
alert_type: val.alert_type,
528-
aggregates: val.aggregates,
529-
eval_config: val.eval_config,
530-
targets: val.targets,
529+
severity: self.severity,
530+
title: self.title,
531+
stream: self.stream,
532+
alert_type: self.alert_type,
533+
aggregates: self.aggregates,
534+
eval_config: self.eval_config,
535+
targets,
531536
state: AlertState::default(),
532-
}
537+
};
538+
Ok(config)
533539
}
534540
}
535541

@@ -552,14 +558,20 @@ pub struct AlertConfig {
552558
}
553559

554560
impl AlertConfig {
555-
pub fn modify(&mut self, alert: AlertRequest) {
561+
pub async fn modify(&mut self, alert: AlertRequest) -> Result<(), AlertError> {
562+
let mut targets = Vec::new();
563+
for id in alert.targets {
564+
targets.push(TARGETS.get_target_by_id(id).await?);
565+
}
566+
556567
self.title = alert.title;
557568
self.stream = alert.stream;
558569
self.alert_type = alert.alert_type;
559570
self.aggregates = alert.aggregates;
560571
self.eval_config = alert.eval_config;
561-
self.targets = alert.targets;
572+
self.targets = targets;
562573
self.state = AlertState::default();
574+
Ok(())
563575
}
564576

565577
pub fn get_base_query(&self) -> String {
@@ -801,6 +813,8 @@ pub enum AlertError {
801813
InvalidAlertModifyRequest,
802814
#[error("{0}")]
803815
FromStrError(#[from] FromStrError),
816+
#[error("Invalid Target ID- {0}")]
817+
InvalidTargetID(String),
804818
}
805819

806820
impl actix_web::ResponseError for AlertError {
@@ -818,6 +832,7 @@ impl actix_web::ResponseError for AlertError {
818832
Self::Anyhow(_) => StatusCode::INTERNAL_SERVER_ERROR,
819833
Self::InvalidAlertModifyRequest => StatusCode::BAD_REQUEST,
820834
Self::FromStrError(_) => StatusCode::BAD_REQUEST,
835+
Self::InvalidTargetID(_) => StatusCode::BAD_REQUEST,
821836
}
822837
}
823838

src/alerts/target.rs

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,65 @@ use base64::Engine;
2727
use chrono::Utc;
2828
use http::{header::AUTHORIZATION, HeaderMap, HeaderValue};
2929
use humantime_serde::re::humantime;
30+
use itertools::Itertools;
31+
use once_cell::sync::Lazy;
3032
use reqwest::ClientBuilder;
33+
use tokio::sync::RwLock;
3134
use tracing::{error, trace, warn};
35+
use ulid::Ulid;
3236
use url::Url;
3337

38+
use crate::alerts::AlertError;
39+
3440
use super::ALERTS;
3541

3642
use super::{AlertState, CallableTarget, Context};
3743

44+
pub static TARGETS: Lazy<TargetConfigs> = Lazy::new(|| TargetConfigs {
45+
target_configs: RwLock::new(HashMap::new()),
46+
});
47+
48+
#[derive(Debug)]
49+
pub struct TargetConfigs {
50+
pub target_configs: RwLock<HashMap<Ulid, Target>>,
51+
}
52+
53+
impl TargetConfigs {
54+
pub async fn update(&self, target: Target) -> Result<(), AlertError> {
55+
let id = target.id;
56+
self.target_configs.write().await.insert(id, target);
57+
Ok(())
58+
}
59+
60+
pub async fn list(&self) -> Result<Vec<Target>, AlertError> {
61+
let targets = self
62+
.target_configs
63+
.read()
64+
.await
65+
.iter()
66+
.map(|(_, v)| v.clone())
67+
.collect_vec();
68+
Ok(targets)
69+
}
70+
71+
pub async fn get_target_by_id(&self, target_id: Ulid) -> Result<Target, AlertError> {
72+
self.target_configs
73+
.read()
74+
.await
75+
.get(&target_id)
76+
.ok_or(AlertError::InvalidTargetID(target_id.to_string()))
77+
.cloned()
78+
}
79+
80+
pub async fn delete(&self, target_id: Ulid) -> Result<Target, AlertError> {
81+
self.target_configs
82+
.write()
83+
.await
84+
.remove(&target_id)
85+
.ok_or(AlertError::InvalidTargetID(target_id.to_string()))
86+
}
87+
}
88+
3889
#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
3990
#[serde(rename_all = "camelCase")]
4091
#[serde(untagged)]
@@ -57,9 +108,16 @@ pub struct Target {
57108
pub target: TargetType,
58109
#[serde(default, rename = "repeat")]
59110
pub timeout: Timeout,
111+
#[serde(default = "Ulid::new")]
112+
pub id: Ulid,
60113
}
61114

62115
impl Target {
116+
pub async fn validate(&self) {
117+
// just check for liveness
118+
// what if the target is not live yet but is added by the user?
119+
}
120+
63121
pub fn call(&self, context: Context) {
64122
trace!("target.call context- {context:?}");
65123
let timeout = &self.timeout;
@@ -193,6 +251,8 @@ pub struct TargetVerifier {
193251
pub target: TargetType,
194252
#[serde(default)]
195253
pub repeat: Option<RepeatVerifier>,
254+
#[serde(default = "Ulid::new")]
255+
pub id: Ulid,
196256
}
197257

198258
impl TryFrom<TargetVerifier> for Target {
@@ -225,6 +285,7 @@ impl TryFrom<TargetVerifier> for Target {
225285
Ok(Target {
226286
target: value.target,
227287
timeout,
288+
id: value.id,
228289
})
229290
}
230291
}

src/handlers/http/alerts.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ pub async fn post(
4848
req: HttpRequest,
4949
Json(alert): Json<AlertRequest>,
5050
) -> Result<impl Responder, AlertError> {
51-
let alert: AlertConfig = alert.into();
51+
let alert: AlertConfig = alert.into().await?;
5252
alert.validate().await?;
5353

5454
// validate the incoming alert query

src/handlers/http/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ pub mod prism_logstream;
4747
pub mod query;
4848
pub mod rbac;
4949
pub mod role;
50+
pub mod targets;
5051
pub mod users;
5152
pub const MAX_EVENT_PAYLOAD_SIZE: usize = 10485760;
5253
pub const API_BASE_PATH: &str = "api";

src/handlers/http/modal/server.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ use crate::handlers::http::base_path;
2626
use crate::handlers::http::health_check;
2727
use crate::handlers::http::prism_base_path;
2828
use crate::handlers::http::query;
29+
use crate::handlers::http::targets;
2930
use crate::handlers::http::users::dashboards;
3031
use crate::handlers::http::users::filters;
3132
use crate::hottier::HotTierManager;
@@ -87,6 +88,7 @@ impl ParseableServer for Server {
8788
.service(Self::get_roles_webscope())
8889
.service(Self::get_counts_webscope())
8990
.service(Self::get_alerts_webscope())
91+
.service(Self::get_targets_webscope())
9092
.service(Self::get_metrics_webscope()),
9193
)
9294
.service(
@@ -253,6 +255,25 @@ impl Server {
253255
)
254256
}
255257

258+
pub fn get_targets_webscope() -> Scope {
259+
web::scope("/targets")
260+
.service(
261+
web::resource("")
262+
.route(web::get().to(targets::list).authorize(Action::GetAlert))
263+
.route(web::post().to(targets::post).authorize(Action::PutAlert)),
264+
)
265+
.service(
266+
web::resource("/{target_id}")
267+
.route(web::get().to(targets::get).authorize(Action::GetAlert))
268+
.route(web::put().to(targets::update).authorize(Action::PutAlert))
269+
.route(
270+
web::delete()
271+
.to(targets::delete)
272+
.authorize(Action::DeleteAlert),
273+
),
274+
)
275+
}
276+
256277
// get the dashboards web scope
257278
pub fn get_dashboards_webscope() -> Scope {
258279
web::scope("/dashboards")

src/handlers/http/targets.rs

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
use actix_web::{
2+
web::{self, Json, Path},
3+
HttpRequest, Responder,
4+
};
5+
use ulid::Ulid;
6+
7+
use crate::alerts::{
8+
target::{Target, TARGETS},
9+
AlertError,
10+
};
11+
12+
// POST /targets
13+
pub async fn post(
14+
_req: HttpRequest,
15+
Json(target): Json<Target>,
16+
) -> Result<impl Responder, AlertError> {
17+
// should check for duplicacy and liveness (??)
18+
target.validate().await;
19+
20+
// add to the map
21+
TARGETS.update(target.clone()).await?;
22+
23+
Ok(web::Json(target))
24+
}
25+
26+
// GET /targets
27+
pub async fn list(_req: HttpRequest) -> Result<impl Responder, AlertError> {
28+
// add to the map
29+
let list = TARGETS.list().await?;
30+
31+
Ok(web::Json(list))
32+
}
33+
34+
// GET /targets/{target_id}
35+
pub async fn get(_req: HttpRequest, target_id: Path<Ulid>) -> Result<impl Responder, AlertError> {
36+
let target_id = target_id.into_inner();
37+
38+
let target = TARGETS.get_target_by_id(target_id).await?;
39+
40+
Ok(web::Json(target))
41+
}
42+
43+
// POST /targets/{target_id}
44+
pub async fn update(
45+
_req: HttpRequest,
46+
target_id: Path<Ulid>,
47+
Json(mut target): Json<Target>,
48+
) -> Result<impl Responder, AlertError> {
49+
let target_id = target_id.into_inner();
50+
51+
// if target_id does not exist, error
52+
TARGETS.get_target_by_id(target_id).await?;
53+
54+
// esnure that the supplied target id is assigned to the target config
55+
target.id = target_id;
56+
// should check for duplicacy and liveness (??)
57+
target.validate().await;
58+
59+
// add to the map
60+
TARGETS.update(target.clone()).await?;
61+
62+
Ok(web::Json(target))
63+
}
64+
65+
// DELETE /targets/{target_id}
66+
pub async fn delete(
67+
_req: HttpRequest,
68+
target_id: Path<Ulid>,
69+
) -> Result<impl Responder, AlertError> {
70+
let target_id = target_id.into_inner();
71+
72+
let target = TARGETS.delete(target_id).await?;
73+
74+
Ok(web::Json(target))
75+
}

0 commit comments

Comments
 (0)