Move metrics related code into metrics.rs

To improve readability metrics related code moves to `metrics.rs`.
This commit is contained in:
finga 2021-11-19 11:40:21 +01:00
parent 3a95ecfd11
commit 5e1d433c38
2 changed files with 105 additions and 99 deletions

View file

@ -1,9 +1,11 @@
mod cli; mod cli;
mod filters; mod filters;
mod metrics;
use crate::{ use crate::{
cli::Opts, cli::Opts,
filters::{FilterType, IpFilter}, filters::{FilterType, IpFilter},
metrics::Metrics,
}; };
use anyhow::{anyhow, bail, Result}; use anyhow::{anyhow, bail, Result};
use clap::Parser; use clap::Parser;
@ -12,7 +14,6 @@ use log::{debug, error, info, trace, warn};
use rocket::{ use rocket::{
data::{FromData, ToByteUnit}, data::{FromData, ToByteUnit},
futures::TryFutureExt, futures::TryFutureExt,
get,
http::{HeaderMap, Status}, http::{HeaderMap, Status},
outcome::Outcome::{self, Failure, Success}, outcome::Outcome::{self, Failure, Success},
post, routes, post, routes,
@ -27,7 +28,7 @@ use std::{
fs::File, fs::File,
io::BufReader, io::BufReader,
net::{IpAddr, Ipv4Addr, SocketAddr}, net::{IpAddr, Ipv4Addr, SocketAddr},
sync::atomic::{AtomicUsize, Ordering}, sync::atomic::Ordering,
}; };
use thiserror::Error; use thiserror::Error;
@ -47,19 +48,6 @@ pub enum WebhookeyError {
Serde(serde_json::Error), Serde(serde_json::Error),
} }
#[derive(Debug, Default)]
struct WebhookeyMetrics {
requests_received: AtomicUsize,
requests_invalid: AtomicUsize,
hooks_successful: AtomicUsize,
hooks_forbidden: AtomicUsize,
hooks_unmatched: AtomicUsize,
commands_executed: AtomicUsize,
commands_execution_failed: AtomicUsize,
commands_successful: AtomicUsize,
commands_failed: AtomicUsize,
}
#[derive(Debug, Deserialize, Serialize)] #[derive(Debug, Deserialize, Serialize)]
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
struct MetricsConfig { struct MetricsConfig {
@ -69,7 +57,7 @@ struct MetricsConfig {
#[derive(Debug, Deserialize, Serialize)] #[derive(Debug, Deserialize, Serialize)]
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
struct Config { pub struct Config {
metrics: Option<MetricsConfig>, metrics: Option<MetricsConfig>,
hooks: BTreeMap<String, Hook>, hooks: BTreeMap<String, Hook>,
} }
@ -311,48 +299,6 @@ fn get_config() -> Result<File> {
bail!("No configuration file found."); bail!("No configuration file found.");
} }
fn get_metrics(metrics: &WebhookeyMetrics) -> String {
format!(
r"# HELP webhookey_requests_received Number of requests received
# TYPE webhookey_requests_received gauge
webhookey_requests_received {}
# HELP webhookey_requests_invalid Number of invalid requests received
# TYPE webhookey_requests_invalid gauge
webhookey_requests_invalid {}
# HELP webhookey_hooks_successful Number of successfully executed hooks
# TYPE webhookey_hooks_successful gauge
webhookey_hooks_sucessful {}
# HELP webhookey_hooks_forbidden Number of forbidden requests
# TYPE webhookey_hooks_forbidden gauge
webhookey_hooks_forbidden {}
# HELP webhookey_hooks_unmatched Number of unmatched requests
# TYPE webhookey_hooks_unmatched gauge
webhookey_hooks_unmatched {}
# HELP webhookey_commands_executed Number of commands executed
# TYPE webhookey_commands_executed gauge
webhookey_commands_executed {}
# HELP webhookey_commands_execution_failed Number of commands failed to execute
# TYPE webhookey_commands_execution_failed gauge
webhookey_commands_execution_failed {}
# HELP webhookey_commands_successful Number of executed commands returning return code 0
# TYPE webhookey_commands_successful gauge
webhookey_commands_successful {}
# HELP webhookey_commands_failed Number of executed commands returning different return code than 0
# TYPE webhookey_commands_failed gauge
webhookey_commands_failed {}
",
metrics.requests_received.load(Ordering::Relaxed),
metrics.requests_invalid.load(Ordering::Relaxed),
metrics.hooks_successful.load(Ordering::Relaxed),
metrics.hooks_forbidden.load(Ordering::Relaxed),
metrics.hooks_unmatched.load(Ordering::Relaxed),
metrics.commands_executed.load(Ordering::Relaxed),
metrics.commands_execution_failed.load(Ordering::Relaxed),
metrics.commands_successful.load(Ordering::Relaxed),
metrics.commands_failed.load(Ordering::Relaxed),
)
}
#[rocket::async_trait] #[rocket::async_trait]
impl<'r> FromData<'r> for Hooks { impl<'r> FromData<'r> for Hooks {
type Error = WebhookeyError; type Error = WebhookeyError;
@ -363,7 +309,7 @@ impl<'r> FromData<'r> for Hooks {
) -> Outcome<Self, (Status, Self::Error), Data<'r>> { ) -> Outcome<Self, (Status, Self::Error), Data<'r>> {
{ {
request request
.guard::<&State<WebhookeyMetrics>>() .guard::<&State<Metrics>>()
.await .await
.unwrap() // TODO: Check if unwrap need to be fixed .unwrap() // TODO: Check if unwrap need to be fixed
.requests_received .requests_received
@ -378,7 +324,7 @@ impl<'r> FromData<'r> for Hooks {
.unwrap_or(IpAddr::V4(Ipv4Addr::UNSPECIFIED)); .unwrap_or(IpAddr::V4(Ipv4Addr::UNSPECIFIED));
request request
.guard::<&State<WebhookeyMetrics>>() .guard::<&State<Metrics>>()
.await .await
.unwrap() // TODO: Check if unwrap need to be fixed .unwrap() // TODO: Check if unwrap need to be fixed
.hooks_unmatched .hooks_unmatched
@ -394,7 +340,7 @@ impl<'r> FromData<'r> for Hooks {
error!("{}", WebhookeyError::Unauthorized(e)); error!("{}", WebhookeyError::Unauthorized(e));
request request
.guard::<&State<WebhookeyMetrics>>() .guard::<&State<Metrics>>()
.await .await
.unwrap() // TODO: Check if unwrap need to be fixed .unwrap() // TODO: Check if unwrap need to be fixed
.hooks_forbidden .hooks_forbidden
@ -406,7 +352,7 @@ impl<'r> FromData<'r> for Hooks {
error!("{}", e); error!("{}", e);
request request
.guard::<&State<WebhookeyMetrics>>() .guard::<&State<Metrics>>()
.await .await
.unwrap() // TODO: Check if unwrap need to be fixed .unwrap() // TODO: Check if unwrap need to be fixed
.requests_invalid .requests_invalid
@ -419,11 +365,7 @@ impl<'r> FromData<'r> for Hooks {
} }
#[post("/", format = "json", data = "<hooks>")] #[post("/", format = "json", data = "<hooks>")]
async fn receive_hook<'a>( async fn receive_hook<'a>(address: SocketAddr, hooks: Hooks, metrics: &State<Metrics>) -> Status {
address: SocketAddr,
hooks: Hooks,
metrics: &State<WebhookeyMetrics>,
) -> Status {
info!("Post request received from: {}", address); info!("Post request received from: {}", address);
hooks.inner.iter().for_each(|(name, command)| { hooks.inner.iter().for_each(|(name, command)| {
@ -455,33 +397,6 @@ async fn receive_hook<'a>(
Status::Ok Status::Ok
} }
#[get("/metrics")]
async fn metrics(
address: SocketAddr,
metrics: &State<WebhookeyMetrics>,
config: &State<Config>,
) -> Option<String> {
// Are metrics configured?
if let Some(metrics_config) = &config.metrics {
// Are metrics enabled?
if metrics_config.enabled {
// Is a filter configured?
if let Some(filter) = &metrics_config.ip_filter {
// Does the request match the filter?
if filter.validate(&address.ip()) {
return Some(get_metrics(metrics));
}
} else {
return Some(get_metrics(metrics));
}
}
}
warn!("Forbidden request for metrics: {:?}", address);
None
}
#[rocket::main] #[rocket::main]
async fn main() -> Result<()> { async fn main() -> Result<()> {
env_logger::init(); env_logger::init();
@ -502,9 +417,9 @@ async fn main() -> Result<()> {
} }
rocket::build() rocket::build()
.mount("/", routes![receive_hook, metrics]) .mount("/", routes![receive_hook, metrics::metrics])
.manage(config) .manage(config)
.manage(WebhookeyMetrics::default()) .manage(Metrics::default())
.launch() .launch()
.await?; .await?;
@ -548,7 +463,7 @@ mod tests {
let rocket = rocket::build() let rocket = rocket::build()
.mount("/", routes![receive_hook]) .mount("/", routes![receive_hook])
.manage(config) .manage(config)
.manage(WebhookeyMetrics::default()); .manage(Metrics::default());
let client = Client::tracked(rocket).await.unwrap(); let client = Client::tracked(rocket).await.unwrap();
let response = client let response = client
@ -764,7 +679,7 @@ mod tests {
let rocket = rocket::build() let rocket = rocket::build()
.mount("/", routes![receive_hook]) .mount("/", routes![receive_hook])
.manage(config) .manage(config)
.manage(WebhookeyMetrics::default()); .manage(Metrics::default());
let client = Client::tracked(rocket).await.unwrap(); let client = Client::tracked(rocket).await.unwrap();
@ -822,7 +737,7 @@ mod tests {
let rocket = rocket::build() let rocket = rocket::build()
.mount("/", routes![receive_hook]) .mount("/", routes![receive_hook])
.manage(config) .manage(config)
.manage(WebhookeyMetrics::default()); .manage(Metrics::default());
let client = Client::tracked(rocket).await.unwrap(); let client = Client::tracked(rocket).await.unwrap();

91
src/metrics.rs Normal file
View file

@ -0,0 +1,91 @@
use crate::Config;
use log::warn;
use rocket::{get, State};
use std::{
net::SocketAddr,
sync::atomic::{AtomicUsize, Ordering},
};
#[derive(Debug, Default)]
pub struct Metrics {
pub requests_received: AtomicUsize,
pub requests_invalid: AtomicUsize,
pub hooks_successful: AtomicUsize,
pub hooks_forbidden: AtomicUsize,
pub hooks_unmatched: AtomicUsize,
pub commands_executed: AtomicUsize,
pub commands_execution_failed: AtomicUsize,
pub commands_successful: AtomicUsize,
pub commands_failed: AtomicUsize,
}
#[get("/metrics")]
pub async fn metrics(
address: SocketAddr,
metrics: &State<Metrics>,
config: &State<Config>,
) -> Option<String> {
// Are metrics configured?
if let Some(metrics_config) = &config.metrics {
// Are metrics enabled?
if metrics_config.enabled {
// Is a filter configured?
if let Some(filter) = &metrics_config.ip_filter {
// Does the request match the filter?
if filter.validate(&address.ip()) {
return Some(metrics.get_metrics());
}
} else {
return Some(metrics.get_metrics());
}
}
}
warn!("Forbidden request for metrics: {:?}", address);
None
}
impl Metrics {
fn get_metrics(&self) -> String {
format!(
r"# HELP webhookey_requests_received Number of requests received
# TYPE webhookey_requests_received gauge
webhookey_requests_received {}
# HELP webhookey_requests_invalid Number of invalid requests received
# TYPE webhookey_requests_invalid gauge
webhookey_requests_invalid {}
# HELP webhookey_hooks_successful Number of successfully executed hooks
# TYPE webhookey_hooks_successful gauge
webhookey_hooks_sucessful {}
# HELP webhookey_hooks_forbidden Number of forbidden requests
# TYPE webhookey_hooks_forbidden gauge
webhookey_hooks_forbidden {}
# HELP webhookey_hooks_unmatched Number of unmatched requests
# TYPE webhookey_hooks_unmatched gauge
webhookey_hooks_unmatched {}
# HELP webhookey_commands_executed Number of commands executed
# TYPE webhookey_commands_executed gauge
webhookey_commands_executed {}
# HELP webhookey_commands_execution_failed Number of commands failed to execute
# TYPE webhookey_commands_execution_failed gauge
webhookey_commands_execution_failed {}
# HELP webhookey_commands_successful Number of executed commands returning return code 0
# TYPE webhookey_commands_successful gauge
webhookey_commands_successful {}
# HELP webhookey_commands_failed Number of executed commands returning different return code than 0
# TYPE webhookey_commands_failed gauge
webhookey_commands_failed {}
",
self.requests_received.load(Ordering::Relaxed),
self.requests_invalid.load(Ordering::Relaxed),
self.hooks_successful.load(Ordering::Relaxed),
self.hooks_forbidden.load(Ordering::Relaxed),
self.hooks_unmatched.load(Ordering::Relaxed),
self.commands_executed.load(Ordering::Relaxed),
self.commands_execution_failed.load(Ordering::Relaxed),
self.commands_successful.load(Ordering::Relaxed),
self.commands_failed.load(Ordering::Relaxed),
)
}
}