Skip to content

Commit

Permalink
Document some stuff better
Browse files Browse the repository at this point in the history
  • Loading branch information
harrytucker committed Nov 14, 2023
1 parent d9978e6 commit 1454d9a
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 70 deletions.
12 changes: 11 additions & 1 deletion src/crd/service_alert.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use std::collections::HashMap;
use std::hash::Hash;
use std::{collections::HashMap, fmt::Display};

use kube::CustomResource;
use schemars::JsonSchema;
Expand Down Expand Up @@ -87,6 +87,16 @@ pub enum Operation {
MoreThan,
}

impl Display for Operation {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Operation::EqualTo => write!(f, "=="),
Operation::LessThan => write!(f, "<"),
Operation::MoreThan => write!(f, ">"),
}
}
}

#[derive(Debug, Serialize, Deserialize, Clone, JsonSchema, Eq, PartialEq, Hash)]
#[serde(rename_all = "camelCase")]
pub enum Severity {
Expand Down
51 changes: 0 additions & 51 deletions src/prometheus/alert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,54 +105,3 @@ impl From<&HashMap<String, String>> for PrometheusSeverity {
}
}
}

#[cfg(test)]
mod test {
use color_eyre::Result;
use pretty_assertions::assert_eq;

use super::*;

const SERIALIZED_PROM_ALERT: &str = r#"
groups:
- name: example
rules:
- alert: HighRequestLatency
expr: job:request_latency_seconds:mean5m{job="myjob"} > 0.5
for: 10m
labels:
severity: page
source: cloud
owner: service
annotations:
summary: High request latency
description: Request latency over 9000"#;

#[test]
fn test_serialisation_happy_path() -> Result<()> {
let rust_repr = PromAlerts {
groups: vec![AlertGroup {
name: "example".into(),
rules: vec![AlertRules {
alert: "HighRequestLatency".into(),
expr: r#"job:request_latency_seconds:mean5m{job="myjob"} > 0.5"#.into(),
for_: "10m".into(),
labels: Labels {
severity: PrometheusSeverity::Page,
source: "cloud".into(),
owner: "service".into(),
},
annotations: Annotations {
summary: "High request latency".into(),
description: "Request latency over 9000".into(),
},
}],
}],
};

let yaml_repr: PromAlerts = serde_yaml::from_str(SERIALIZED_PROM_ALERT)?;
assert_eq!(yaml_repr, rust_repr);

Ok(())
}
}
3 changes: 3 additions & 0 deletions src/prometheus/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,6 @@
pub mod alert;
pub mod replica_alerts;

#[cfg(test)]
mod tests;
39 changes: 21 additions & 18 deletions src/prometheus/replica_alerts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ use crate::crd::{AlertConfig, Operation, ServiceAlertSpec};

use super::alert::{AlertGroup, AlertRules, Annotations, Labels, PrometheusSeverity};

/// Generates an [`AlertGroup`] for a list of defined replica alerts. Caller is
/// responsible for only passing in a slice of alerts that are actually replica
/// alerts!
pub fn replica_count_rules(alert_configs: &[AlertConfig], spec: &ServiceAlertSpec) -> AlertGroup {
// Prometheus Alert Rules in a single file must be uniquely named, but we
// can't generate a *random* unique identifier, since that would break the
Expand Down Expand Up @@ -40,26 +43,28 @@ pub fn replica_count_rules(alert_configs: &[AlertConfig], spec: &ServiceAlertSpe
// aggr: String,
// }

/// Returns a [`String`] containing the PromQL expression for a given
/// [`AlertConfig`] that alerts based on the number of pod replicas deployed.
///
/// Note that, as [AlertConfigs](AlertConfig) are agnostic to the type of alert,
/// it is the caller's responsibility to *not* call this function on other alert
/// types, like HTTP or gRPC alerts.
fn replicas_promql(alert_config: &AlertConfig, spec: &ServiceAlertSpec) -> String {
match alert_config.operation {
Operation::EqualTo => {
format!(
r#"sum by (app_kubernetes_io_name) (up{{app_kubernetes_io_name="{0}"}}) == {1}"#,
spec.deployment_name, alert_config.value,
)
}
Operation::LessThan => format!(
r#"sum by (app_kubernetes_io_name) (up{{app_kubernetes_io_name="{0}"}}) < {1}"#,
spec.deployment_name, alert_config.value,
),
Operation::MoreThan => format!(
r#"sum by (app_kubernetes_io_name) (up{{app_kubernetes_io_name="{0}"}}) > {1}"#,
spec.deployment_name, alert_config.value,
),
}
let operation = &alert_config.operation;
format!(
r#"sum by (app_kubernetes_io_name) (up{{app_kubernetes_io_name="{0}"}}) {operation} {1}"#,
spec.deployment_name, alert_config.value,
)
}

/// Returns the [`Annotations`] struct for a given [`AlertConfig`].
fn replicas_annotations(alert_config: &AlertConfig) -> Annotations {
// Alert annotations and labels for Prometheus can be templated, using two
// pairs of braces.
//
// Rust uses a single pair of braces for `format!()` macro templating, so
// you need to use an extra pair of braces for every literal brace you want
// in the string. This is why you see quadruple brace pairs in descriptions!
match alert_config.operation {
Operation::EqualTo => Annotations {
summary: String::from("Replicas reached alert boundary"),
Expand All @@ -68,15 +73,13 @@ fn replicas_annotations(alert_config: &AlertConfig) -> Annotations {
Operation::LessThan => Annotations {
summary: String::from("Replicas less than alert boundary"),
description: format!(
// quad-braces necessary for escaping within format! macro
"{{{{ $value }}}} replicas currently up, expected at least {0}",
alert_config.value
),
},
Operation::MoreThan => Annotations {
summary: String::from("Replicas more than alert boundary"),
description: format!(
// quad-braces necessary for escaping within format! macro
"{{{{ $value }}}} replicas currently up, expected less than {0}",
alert_config.value
),
Expand Down
47 changes: 47 additions & 0 deletions src/prometheus/tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
use color_eyre::Result;
use pretty_assertions::assert_eq;

use crate::prometheus::alert::*;

const SERIALIZED_PROM_ALERT: &str = r#"
groups:
- name: example
rules:
- alert: HighRequestLatency
expr: job:request_latency_seconds:mean5m{job="myjob"} > 0.5
for: 10m
labels:
severity: page
source: cloud
owner: service
annotations:
summary: High request latency
description: Request latency over 9000"#;

#[test]
fn test_serialisation_happy_path() -> Result<()> {
let rust_repr = PromAlerts {
groups: vec![AlertGroup {
name: "example".into(),
rules: vec![AlertRules {
alert: "HighRequestLatency".into(),
expr: r#"job:request_latency_seconds:mean5m{job="myjob"} > 0.5"#.into(),
for_: "10m".into(),
labels: Labels {
severity: PrometheusSeverity::Page,
source: "cloud".into(),
owner: "service".into(),
},
annotations: Annotations {
summary: "High request latency".into(),
description: "Request latency over 9000".into(),
},
}],
}],
};

let yaml_repr: PromAlerts = serde_yaml::from_str(SERIALIZED_PROM_ALERT)?;
assert_eq!(yaml_repr, rust_repr);

Ok(())
}

0 comments on commit 1454d9a

Please sign in to comment.