switch to alertmanager interface with askama template
This commit is contained in:
parent
2cf4e1dbc0
commit
6ecff10377
|
@ -6,6 +6,7 @@ version = 3
|
|||
name = "alert2muc"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"askama",
|
||||
"axum",
|
||||
"futures",
|
||||
"serde",
|
||||
|
@ -18,6 +19,54 @@ dependencies = [
|
|||
"xmpp-parsers",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "askama"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fb98f10f371286b177db5eeb9a6e5396609555686a35e1d4f7b9a9c6d8af0139"
|
||||
dependencies = [
|
||||
"askama_derive",
|
||||
"askama_escape",
|
||||
"askama_shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "askama_derive"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87bf87e6e8b47264efa9bde63d6225c6276a52e05e91bf37eaa8afd0032d6b71"
|
||||
dependencies = [
|
||||
"askama_shared",
|
||||
"proc-macro2",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "askama_escape"
|
||||
version = "0.10.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "619743e34b5ba4e9703bba34deac3427c72507c7159f5fd030aea8cac0cfe341"
|
||||
|
||||
[[package]]
|
||||
name = "askama_shared"
|
||||
version = "0.12.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf722b94118a07fcbc6640190f247334027685d4e218b794dbfe17c32bf38ed0"
|
||||
dependencies = [
|
||||
"askama_escape",
|
||||
"humansize",
|
||||
"mime",
|
||||
"mime_guess",
|
||||
"nom",
|
||||
"num-traits",
|
||||
"percent-encoding",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde",
|
||||
"syn",
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.59"
|
||||
|
@ -503,6 +552,12 @@ version = "1.0.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421"
|
||||
|
||||
[[package]]
|
||||
name = "humansize"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02296996cb8796d7c6e3bc2d9211b7802812d36999a51bb754123ead7d37d026"
|
||||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "0.14.23"
|
||||
|
@ -694,6 +749,16 @@ version = "0.3.16"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
|
||||
|
||||
[[package]]
|
||||
name = "mime_guess"
|
||||
version = "2.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef"
|
||||
dependencies = [
|
||||
"mime",
|
||||
"unicase",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "minidom"
|
||||
version = "0.15.0"
|
||||
|
@ -703,6 +768,12 @@ dependencies = [
|
|||
"rxml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "minimal-lexical"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "0.8.5"
|
||||
|
@ -733,6 +804,16 @@ dependencies = [
|
|||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "7.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"minimal-lexical",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nu-ansi-term"
|
||||
version = "0.46.0"
|
||||
|
@ -1512,6 +1593,15 @@ dependencies = [
|
|||
"xmpp-parsers",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.5.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1333c76748e868a4d9d1017b5ab53171dfd095f70c712fdb4653a406547f598f"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower"
|
||||
version = "0.4.13"
|
||||
|
@ -1679,6 +1769,15 @@ version = "1.16.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
|
||||
|
||||
[[package]]
|
||||
name = "unicase"
|
||||
version = "2.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
|
||||
dependencies = [
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-bidi"
|
||||
version = "0.3.8"
|
||||
|
|
|
@ -17,3 +17,4 @@ tokio-xmpp = "3"
|
|||
xmpp-parsers = "0.19"
|
||||
axum = "0.6"
|
||||
systemd = "0.10"
|
||||
askama = "0.11"
|
||||
|
|
93
src/main.rs
93
src/main.rs
|
@ -1,8 +1,5 @@
|
|||
use std::{
|
||||
net::SocketAddr,
|
||||
collections::HashMap,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
use std::net::SocketAddr;
|
||||
use askama::Template;
|
||||
use axum::{
|
||||
extract::State,
|
||||
routing::post,
|
||||
|
@ -14,61 +11,59 @@ use serde::Deserialize;
|
|||
|
||||
mod jabber;
|
||||
|
||||
#[derive(Deserialize, Clone, Debug)]
|
||||
#[allow(non_snake_case)]
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
struct Payload {
|
||||
alerts: Vec<Alert>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Template)]
|
||||
#[template(path="alert.txt", escape="none")]
|
||||
struct Alert {
|
||||
generatorURL: String,
|
||||
startsAt: String,
|
||||
status: String,
|
||||
labels: AlertLabels,
|
||||
annotations: AlertAnnotations,
|
||||
labels: HashMap<String, String>,
|
||||
#[serde(rename = "generatorURL")]
|
||||
generator_url: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Clone, Debug)]
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
struct AlertLabels {
|
||||
alertname: Option<String>,
|
||||
host: Option<String>,
|
||||
instance: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
struct AlertAnnotations {
|
||||
summary: String,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct AppState {
|
||||
jabber: jabber::Handle,
|
||||
alerts: Arc<Mutex<HashMap<String, Alert>>>,
|
||||
message: Option<String>,
|
||||
description: Option<String>,
|
||||
}
|
||||
|
||||
async fn alerts(
|
||||
State(state): State<AppState>,
|
||||
Json(payload): Json<Vec<Alert>>,
|
||||
State(jabber): State<jabber::Handle>,
|
||||
Json(payload): Json<Payload>,
|
||||
) -> Response {
|
||||
let mut message = String::new();
|
||||
let mut alerts = state.alerts.lock().unwrap();
|
||||
for alert in payload {
|
||||
let is_old = if let Some(old_alert) = alerts.get(&alert.generatorURL) {
|
||||
old_alert.startsAt == alert.startsAt
|
||||
} else {
|
||||
false
|
||||
};
|
||||
let mut error_message = None;
|
||||
|
||||
if ! is_old {
|
||||
if !message.is_empty() {
|
||||
message += "\n";
|
||||
for alert in payload.alerts {
|
||||
match alert.render() {
|
||||
Ok(message) => {
|
||||
jabber.send_message(message).await;
|
||||
}
|
||||
Err(e) => {
|
||||
error_message = Some(format!("{}", e));
|
||||
}
|
||||
let title = match (alert.labels.get("alertname"), alert.labels.get("instance")) {
|
||||
(Some(name), Some(instance)) => format!("{}@{}", name, instance),
|
||||
_ => alert.annotations.summary.clone(),
|
||||
};
|
||||
message += &format!("{}: {}", title, alert.generatorURL);
|
||||
alerts.insert(alert.generatorURL.clone(), alert);
|
||||
}
|
||||
}
|
||||
drop(alerts);
|
||||
|
||||
if !message.is_empty() {
|
||||
let jabber = state.jabber.clone();
|
||||
tokio::spawn(async move {
|
||||
jabber.send_message(message).await;
|
||||
});
|
||||
match error_message {
|
||||
None =>
|
||||
StatusCode::OK.into_response(),
|
||||
Some(error_message) =>
|
||||
(StatusCode::INTERNAL_SERVER_ERROR,
|
||||
error_message
|
||||
).into_response()
|
||||
}
|
||||
|
||||
StatusCode::OK.into_response()
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
|
@ -91,15 +86,11 @@ async fn main() {
|
|||
).expect("read config")
|
||||
).expect("parse config");
|
||||
let jabber = jabber::run(config.jid, config.password, config.muc).await;
|
||||
let state = AppState {
|
||||
jabber,
|
||||
alerts: Arc::new(Mutex::new(HashMap::new())),
|
||||
};
|
||||
|
||||
// build our application with a route
|
||||
let app = Router::new()
|
||||
.route("/api/v2/alerts", post(alerts))
|
||||
.with_state(state);
|
||||
.route("/alert", post(alerts))
|
||||
.with_state(jabber);
|
||||
|
||||
let addr = SocketAddr::from(([127, 0, 0, 1], config.listen_port));
|
||||
tracing::debug!("listening on {}", addr);
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
{{ status|upper -}}:
|
||||
{% if labels.alertname.as_ref().is_some() %}*{{ labels.alertname.as_ref().unwrap() }}*{% endif -%}
|
||||
{% if labels.host.as_ref().is_some() -%}
|
||||
at {{ labels.host.as_ref().unwrap() -}}
|
||||
{% else if labels.instance.as_ref().is_some() -%}
|
||||
at {{ labels.instance.as_ref().unwrap() }}{% endif -%}
|
||||
{% if annotations.message.as_ref().is_some() %}
|
||||
{{ annotations.message.as_ref().unwrap() }}{% endif %}
|
||||
{% if annotations.description.as_ref().is_some() %}
|
||||
{{ annotations.description.as_ref().unwrap() }}{% endif %}
|
||||
Link: {{ generator_url }}
|
Loading…
Reference in New Issue