Merge pull request 'feat: scrape targets to be able to get snmp alerts from machines to prometheus' (#171) from feat/scrape_target into master
Reviewed-on: https://git.nationtech.io/NationTech/harmony/pulls/171
This commit is contained in:
commit
05205f4ac1
@ -21,6 +21,7 @@ pub struct AlertingInterpret<S: AlertSender> {
|
|||||||
pub sender: S,
|
pub sender: S,
|
||||||
pub receivers: Vec<Box<dyn AlertReceiver<S>>>,
|
pub receivers: Vec<Box<dyn AlertReceiver<S>>>,
|
||||||
pub rules: Vec<Box<dyn AlertRule<S>>>,
|
pub rules: Vec<Box<dyn AlertRule<S>>>,
|
||||||
|
pub scrape_targets: Option<Vec<Box<dyn ScrapeTarget<S>>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
@ -38,6 +39,12 @@ impl<S: AlertSender + Installable<T>, T: Topology> Interpret<T> for AlertingInte
|
|||||||
debug!("installing rule: {:#?}", rule);
|
debug!("installing rule: {:#?}", rule);
|
||||||
rule.install(&self.sender).await?;
|
rule.install(&self.sender).await?;
|
||||||
}
|
}
|
||||||
|
if let Some(targets) = &self.scrape_targets {
|
||||||
|
for target in targets.iter() {
|
||||||
|
debug!("installing scrape_target: {:#?}", target);
|
||||||
|
target.install(&self.sender).await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
self.sender.ensure_installed(inventory, topology).await?;
|
self.sender.ensure_installed(inventory, topology).await?;
|
||||||
Ok(Outcome::success(format!(
|
Ok(Outcome::success(format!(
|
||||||
"successfully installed alert sender {}",
|
"successfully installed alert sender {}",
|
||||||
@ -77,6 +84,6 @@ pub trait AlertRule<S: AlertSender>: std::fmt::Debug + Send + Sync {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait ScrapeTarget<S: AlertSender> {
|
pub trait ScrapeTarget<S: AlertSender>: std::fmt::Debug + Send + Sync {
|
||||||
async fn install(&self, sender: &S) -> Result<(), InterpretError>;
|
async fn install(&self, sender: &S) -> Result<Outcome, InterpretError>;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,187 @@
|
|||||||
|
use std::net::IpAddr;
|
||||||
|
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use kube::CustomResource;
|
||||||
|
use schemars::JsonSchema;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
modules::monitoring::kube_prometheus::crd::{
|
||||||
|
crd_alertmanager_config::CRDPrometheus, crd_prometheuses::LabelSelector,
|
||||||
|
},
|
||||||
|
topology::oberservability::monitoring::ScrapeTarget,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(CustomResource, Serialize, Deserialize, Debug, Clone, JsonSchema)]
|
||||||
|
#[kube(
|
||||||
|
group = "monitoring.coreos.com",
|
||||||
|
version = "v1alpha1",
|
||||||
|
kind = "ScrapeConfig",
|
||||||
|
plural = "scrapeconfigs",
|
||||||
|
namespaced
|
||||||
|
)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct ScrapeConfigSpec {
|
||||||
|
/// List of static configurations.
|
||||||
|
pub static_configs: Option<Vec<StaticConfig>>,
|
||||||
|
|
||||||
|
/// Kubernetes service discovery.
|
||||||
|
pub kubernetes_sd_configs: Option<Vec<KubernetesSDConfig>>,
|
||||||
|
|
||||||
|
/// HTTP-based service discovery.
|
||||||
|
pub http_sd_configs: Option<Vec<HttpSDConfig>>,
|
||||||
|
|
||||||
|
/// File-based service discovery.
|
||||||
|
pub file_sd_configs: Option<Vec<FileSDConfig>>,
|
||||||
|
|
||||||
|
/// DNS-based service discovery.
|
||||||
|
pub dns_sd_configs: Option<Vec<DnsSDConfig>>,
|
||||||
|
|
||||||
|
/// Consul service discovery.
|
||||||
|
pub consul_sd_configs: Option<Vec<ConsulSDConfig>>,
|
||||||
|
|
||||||
|
/// Relabeling configuration applied to discovered targets.
|
||||||
|
pub relabel_configs: Option<Vec<RelabelConfig>>,
|
||||||
|
|
||||||
|
/// Metric relabeling configuration applied to scraped samples.
|
||||||
|
pub metric_relabel_configs: Option<Vec<RelabelConfig>>,
|
||||||
|
|
||||||
|
/// Path to scrape metrics from (defaults to `/metrics`).
|
||||||
|
pub metrics_path: Option<String>,
|
||||||
|
|
||||||
|
/// Interval at which Prometheus scrapes targets (e.g., "30s").
|
||||||
|
pub scrape_interval: Option<String>,
|
||||||
|
|
||||||
|
/// Timeout for scraping (e.g., "10s").
|
||||||
|
pub scrape_timeout: Option<String>,
|
||||||
|
|
||||||
|
/// Optional job name override.
|
||||||
|
pub job_name: Option<String>,
|
||||||
|
|
||||||
|
/// Optional scheme (http or https).
|
||||||
|
pub scheme: Option<String>,
|
||||||
|
|
||||||
|
/// Authorization paramaters for snmp walk
|
||||||
|
pub params: Option<Params>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Static configuration section of a ScrapeConfig.
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct StaticConfig {
|
||||||
|
pub targets: Vec<String>,
|
||||||
|
|
||||||
|
pub labels: Option<LabelSelector>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Relabeling configuration for target or metric relabeling.
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct RelabelConfig {
|
||||||
|
pub source_labels: Option<Vec<String>>,
|
||||||
|
pub separator: Option<String>,
|
||||||
|
pub target_label: Option<String>,
|
||||||
|
pub regex: Option<String>,
|
||||||
|
pub modulus: Option<u64>,
|
||||||
|
pub replacement: Option<String>,
|
||||||
|
pub action: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Kubernetes service discovery configuration.
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct KubernetesSDConfig {
|
||||||
|
///"pod", "service", "endpoints"pub role: String,
|
||||||
|
pub namespaces: Option<NamespaceSelector>,
|
||||||
|
pub selectors: Option<Vec<LabelSelector>>,
|
||||||
|
pub api_server: Option<String>,
|
||||||
|
pub bearer_token_file: Option<String>,
|
||||||
|
pub tls_config: Option<TLSConfig>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Namespace selector for Kubernetes service discovery.
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct NamespaceSelector {
|
||||||
|
pub any: Option<bool>,
|
||||||
|
pub match_names: Option<Vec<String>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// HTTP-based service discovery configuration.
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct HttpSDConfig {
|
||||||
|
pub url: String,
|
||||||
|
pub refresh_interval: Option<String>,
|
||||||
|
pub basic_auth: Option<BasicAuth>,
|
||||||
|
pub authorization: Option<Authorization>,
|
||||||
|
pub tls_config: Option<TLSConfig>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// File-based service discovery configuration.
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct FileSDConfig {
|
||||||
|
pub files: Vec<String>,
|
||||||
|
pub refresh_interval: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// DNS-based service discovery configuration.
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct DnsSDConfig {
|
||||||
|
pub names: Vec<String>,
|
||||||
|
pub refresh_interval: Option<String>,
|
||||||
|
pub type_: Option<String>, // SRV, A, AAAA
|
||||||
|
pub port: Option<u16>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Consul service discovery configuration.
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct ConsulSDConfig {
|
||||||
|
pub server: String,
|
||||||
|
pub services: Option<Vec<String>>,
|
||||||
|
pub scheme: Option<String>,
|
||||||
|
pub datacenter: Option<String>,
|
||||||
|
pub tag_separator: Option<String>,
|
||||||
|
pub refresh_interval: Option<String>,
|
||||||
|
pub tls_config: Option<TLSConfig>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Basic authentication credentials.
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct BasicAuth {
|
||||||
|
pub username: String,
|
||||||
|
pub password: Option<String>,
|
||||||
|
pub password_file: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Bearer token or other auth mechanisms.
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct Authorization {
|
||||||
|
pub credentials: Option<String>,
|
||||||
|
pub credentials_file: Option<String>,
|
||||||
|
pub type_: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// TLS configuration for secure scraping.
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct TLSConfig {
|
||||||
|
pub ca_file: Option<String>,
|
||||||
|
pub cert_file: Option<String>,
|
||||||
|
pub key_file: Option<String>,
|
||||||
|
pub server_name: Option<String>,
|
||||||
|
pub insecure_skip_verify: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Authorization parameters for SNMP walk.
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct Params {
|
||||||
|
pub auth: Option<Vec<String>>,
|
||||||
|
pub module: Option<Vec<String>>,
|
||||||
|
}
|
@ -4,6 +4,7 @@ pub mod crd_default_rules;
|
|||||||
pub mod crd_grafana;
|
pub mod crd_grafana;
|
||||||
pub mod crd_prometheus_rules;
|
pub mod crd_prometheus_rules;
|
||||||
pub mod crd_prometheuses;
|
pub mod crd_prometheuses;
|
||||||
|
pub mod crd_scrape_config;
|
||||||
pub mod grafana_default_dashboard;
|
pub mod grafana_default_dashboard;
|
||||||
pub mod grafana_operator;
|
pub mod grafana_operator;
|
||||||
pub mod prometheus_operator;
|
pub mod prometheus_operator;
|
||||||
|
@ -31,6 +31,7 @@ impl<T: Topology + HelmCommand + TenantManager> Score<T> for HelmPrometheusAlert
|
|||||||
sender: KubePrometheus { config },
|
sender: KubePrometheus { config },
|
||||||
receivers: self.receivers.clone(),
|
receivers: self.receivers.clone(),
|
||||||
rules: self.rules.clone(),
|
rules: self.rules.clone(),
|
||||||
|
scrape_targets: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
fn name(&self) -> String {
|
fn name(&self) -> String {
|
||||||
|
@ -6,3 +6,4 @@ pub mod kube_prometheus;
|
|||||||
pub mod ntfy;
|
pub mod ntfy;
|
||||||
pub mod okd;
|
pub mod okd;
|
||||||
pub mod prometheus;
|
pub mod prometheus;
|
||||||
|
pub mod scrape_target;
|
||||||
|
1
harmony/src/modules/monitoring/scrape_target/mod.rs
Normal file
1
harmony/src/modules/monitoring/scrape_target/mod.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
pub mod server;
|
76
harmony/src/modules/monitoring/scrape_target/server.rs
Normal file
76
harmony/src/modules/monitoring/scrape_target/server.rs
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
use std::net::IpAddr;
|
||||||
|
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use kube::api::ObjectMeta;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
interpret::{InterpretError, Outcome},
|
||||||
|
modules::monitoring::kube_prometheus::crd::{
|
||||||
|
crd_alertmanager_config::CRDPrometheus,
|
||||||
|
crd_scrape_config::{Params, RelabelConfig, ScrapeConfig, ScrapeConfigSpec, StaticConfig},
|
||||||
|
},
|
||||||
|
topology::oberservability::monitoring::ScrapeTarget,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize)]
|
||||||
|
pub struct Server {
|
||||||
|
pub name: String,
|
||||||
|
pub ip: IpAddr,
|
||||||
|
pub auth: String,
|
||||||
|
pub module: String,
|
||||||
|
pub domain: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl ScrapeTarget<CRDPrometheus> for Server {
|
||||||
|
async fn install(&self, sender: &CRDPrometheus) -> Result<Outcome, InterpretError> {
|
||||||
|
let scrape_config_spec = ScrapeConfigSpec {
|
||||||
|
static_configs: Some(vec![StaticConfig {
|
||||||
|
targets: vec![self.ip.to_string()],
|
||||||
|
labels: None,
|
||||||
|
}]),
|
||||||
|
scrape_interval: Some("2m".to_string()),
|
||||||
|
kubernetes_sd_configs: None,
|
||||||
|
http_sd_configs: None,
|
||||||
|
file_sd_configs: None,
|
||||||
|
dns_sd_configs: None,
|
||||||
|
params: Some(Params {
|
||||||
|
auth: Some(vec![self.auth.clone()]),
|
||||||
|
module: Some(vec![self.module.clone()]),
|
||||||
|
}),
|
||||||
|
consul_sd_configs: None,
|
||||||
|
relabel_configs: Some(vec![RelabelConfig {
|
||||||
|
action: None,
|
||||||
|
source_labels: Some(vec!["__address__".to_string()]),
|
||||||
|
separator: None,
|
||||||
|
target_label: Some("__param_target".to_string()),
|
||||||
|
regex: None,
|
||||||
|
replacement: Some(format!("snmp.{}:31080", self.domain.clone())),
|
||||||
|
modulus: None,
|
||||||
|
}]),
|
||||||
|
metric_relabel_configs: None,
|
||||||
|
metrics_path: Some("/snmp".to_string()),
|
||||||
|
scrape_timeout: Some("2m".to_string()),
|
||||||
|
job_name: Some(format!("snmp_exporter/cloud/{}", self.name.clone())),
|
||||||
|
scheme: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let scrape_config = ScrapeConfig {
|
||||||
|
metadata: ObjectMeta {
|
||||||
|
name: Some(self.name.clone()),
|
||||||
|
namespace: Some(sender.namespace.clone()),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
spec: scrape_config_spec,
|
||||||
|
};
|
||||||
|
sender
|
||||||
|
.client
|
||||||
|
.apply(&scrape_config, Some(&sender.namespace.clone()))
|
||||||
|
.await?;
|
||||||
|
Ok(Outcome::success(format!(
|
||||||
|
"installed scrape target {}",
|
||||||
|
self.name.clone()
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user