feat: add service monitors support to prom #66
							
								
								
									
										1
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							| @ -1160,6 +1160,7 @@ version = "0.1.0" | |||||||
| dependencies = [ | dependencies = [ | ||||||
|  "harmony", |  "harmony", | ||||||
|  "harmony_cli", |  "harmony_cli", | ||||||
|  |  "harmony_macros", | ||||||
|  "tokio", |  "tokio", | ||||||
|  "url", |  "url", | ||||||
| ] | ] | ||||||
|  | |||||||
| @ -8,5 +8,6 @@ license.workspace = true | |||||||
| [dependencies] | [dependencies] | ||||||
| harmony = { version = "0.1.0", path = "../../harmony" } | harmony = { version = "0.1.0", path = "../../harmony" } | ||||||
| harmony_cli = { version = "0.1.0", path = "../../harmony_cli" } | harmony_cli = { version = "0.1.0", path = "../../harmony_cli" } | ||||||
|  | harmony_macros = { version = "0.1.0", path = "../../harmony_macros" } | ||||||
| tokio.workspace = true | tokio.workspace = true | ||||||
| url.workspace = true | url.workspace = true | ||||||
|  | |||||||
| @ -1,3 +1,5 @@ | |||||||
|  | use std::collections::HashMap; | ||||||
|  | 
 | ||||||
| use harmony::{ | use harmony::{ | ||||||
|     inventory::Inventory, |     inventory::Inventory, | ||||||
|     maestro::Maestro, |     maestro::Maestro, | ||||||
| @ -41,9 +43,30 @@ async fn main() { | |||||||
|         ], |         ], | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
|  |     let service_monitor_endpoint = ServiceMonitorEndpoint { | ||||||
|  |         port: Some("80".to_string()), | ||||||
|  |         path: "/metrics".to_string(), | ||||||
|  |         scheme: HTTPScheme::HTTP, | ||||||
|  |         ..Default::default() | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     let service_monitor = ServiceMonitor { | ||||||
|  |         name: "test-service-monitor".to_string(), | ||||||
|  |         selector: Selector { | ||||||
|  |             match_labels: HashMap::new(), | ||||||
|  |             match_expressions: vec![MatchExpression { | ||||||
|  |                 key: "test".to_string(), | ||||||
|  |                 operator: Operator::In, | ||||||
|  |                 values: vec!["test-service".to_string()], | ||||||
|  |             }], | ||||||
|  |         }, | ||||||
|  |         endpoints: vec![service_monitor_endpoint], | ||||||
|  |         ..Default::default() | ||||||
|  |     }; | ||||||
|     let alerting_score = HelmPrometheusAlertingScore { |     let alerting_score = HelmPrometheusAlertingScore { | ||||||
|         receivers: vec![Box::new(discord_receiver)], |         receivers: vec![Box::new(discord_receiver)], | ||||||
|         rules: vec![Box::new(additional_rules), Box::new(additional_rules2)], |         rules: vec![Box::new(additional_rules), Box::new(additional_rules2)], | ||||||
|  |         service_monitors: vec![service_monitor], | ||||||
|     }; |     }; | ||||||
|     let mut maestro = Maestro::<K8sAnywhereTopology>::initialize( |     let mut maestro = Maestro::<K8sAnywhereTopology>::initialize( | ||||||
|         Inventory::autoload(), |         Inventory::autoload(), | ||||||
|  | |||||||
| @ -26,6 +26,7 @@ pub struct KubePrometheusConfig { | |||||||
|     pub prometheus_operator: bool, |     pub prometheus_operator: bool, | ||||||
|     pub alert_receiver_configs: Vec<AlertManagerChannelConfig>, |     pub alert_receiver_configs: Vec<AlertManagerChannelConfig>, | ||||||
|     pub alert_rules: Vec<AlertManagerAdditionalPromRules>, |     pub alert_rules: Vec<AlertManagerAdditionalPromRules>, | ||||||
|  |     pub additional_service_monitors: Vec<ServiceMonitor>, | ||||||
| } | } | ||||||
| impl KubePrometheusConfig { | impl KubePrometheusConfig { | ||||||
|     pub fn new() -> Self { |     pub fn new() -> Self { | ||||||
| @ -49,6 +50,7 @@ impl KubePrometheusConfig { | |||||||
|             kube_scheduler: false, |             kube_scheduler: false, | ||||||
|             alert_receiver_configs: vec![], |             alert_receiver_configs: vec![], | ||||||
|             alert_rules: vec![], |             alert_rules: vec![], | ||||||
|  |             additional_service_monitors: vec![], | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -12,7 +12,7 @@ use crate::modules::{ | |||||||
|     helm::chart::HelmChartScore, |     helm::chart::HelmChartScore, | ||||||
|     monitoring::kube_prometheus::types::{ |     monitoring::kube_prometheus::types::{ | ||||||
|         AlertGroup, AlertManager, AlertManagerAdditionalPromRules, AlertManagerConfig, |         AlertGroup, AlertManager, AlertManagerAdditionalPromRules, AlertManagerConfig, | ||||||
|         AlertManagerRoute, AlertManagerValues, |         AlertManagerRoute, AlertManagerValues, PrometheusConfig, | ||||||
|     }, |     }, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| @ -101,11 +101,26 @@ nodeExporter: | |||||||
|   enabled: {node_exporter} |   enabled: {node_exporter} | ||||||
| prometheusOperator: | prometheusOperator: | ||||||
|   enabled: {prometheus_operator} |   enabled: {prometheus_operator} | ||||||
| prometheus: | 
 | ||||||
|   enabled: {prometheus} |  | ||||||
| "#,
 | "#,
 | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
|  |     let prometheus_config = | ||||||
|  |         crate::modules::monitoring::kube_prometheus::types::PrometheusConfigValues { | ||||||
|  |             prometheus: PrometheusConfig { | ||||||
|  |                 prometheus: bool::from_str(prometheus.as_str()).expect("couldn't parse bool"), | ||||||
|  |                 additional_service_monitors: config.additional_service_monitors.clone(), | ||||||
|  |             }, | ||||||
|  |         }; | ||||||
|  |     let prometheus_config_yaml = | ||||||
|  |         serde_yaml::to_string(&prometheus_config).expect("Failed to serialize YAML"); | ||||||
|  | 
 | ||||||
|  |     debug!( | ||||||
|  |         "serialized prometheus config: \n {:#}", | ||||||
|  |         prometheus_config_yaml | ||||||
|  |     ); | ||||||
|  |     values.push_str(&prometheus_config_yaml); | ||||||
|  | 
 | ||||||
|     // add required null receiver for prometheus alert manager
 |     // add required null receiver for prometheus alert manager
 | ||||||
|     let mut null_receiver = Mapping::new(); |     let mut null_receiver = Mapping::new(); | ||||||
|     null_receiver.insert( |     null_receiver.insert( | ||||||
|  | |||||||
| @ -4,6 +4,7 @@ use serde::Serialize; | |||||||
| 
 | 
 | ||||||
| use super::{helm::config::KubePrometheusConfig, prometheus::Prometheus}; | use super::{helm::config::KubePrometheusConfig, prometheus::Prometheus}; | ||||||
| use crate::{ | use crate::{ | ||||||
|  |     modules::monitoring::kube_prometheus::types::ServiceMonitor, | ||||||
|     score::Score, |     score::Score, | ||||||
|     topology::{ |     topology::{ | ||||||
|         HelmCommand, Topology, |         HelmCommand, Topology, | ||||||
| @ -15,14 +16,18 @@ use crate::{ | |||||||
| pub struct HelmPrometheusAlertingScore { | pub struct HelmPrometheusAlertingScore { | ||||||
|     pub receivers: Vec<Box<dyn AlertReceiver<Prometheus>>>, |     pub receivers: Vec<Box<dyn AlertReceiver<Prometheus>>>, | ||||||
|     pub rules: Vec<Box<dyn AlertRule<Prometheus>>>, |     pub rules: Vec<Box<dyn AlertRule<Prometheus>>>, | ||||||
|  |     pub service_monitors: Vec<ServiceMonitor>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl<T: Topology + HelmCommand> Score<T> for HelmPrometheusAlertingScore { | impl<T: Topology + HelmCommand> Score<T> for HelmPrometheusAlertingScore { | ||||||
|     fn create_interpret(&self) -> Box<dyn crate::interpret::Interpret<T>> { |     fn create_interpret(&self) -> Box<dyn crate::interpret::Interpret<T>> { | ||||||
|  |         let config = Arc::new(Mutex::new(KubePrometheusConfig::new())); | ||||||
|  |         config | ||||||
|  |             .try_lock() | ||||||
|  |             .expect("couldn't lock config") | ||||||
|  |             .additional_service_monitors = self.service_monitors.clone(); | ||||||
|         Box::new(AlertingInterpret { |         Box::new(AlertingInterpret { | ||||||
|             sender: Prometheus { |             sender: Prometheus { config }, | ||||||
|                 config: Arc::new(Mutex::new(KubePrometheusConfig::new())), |  | ||||||
|             }, |  | ||||||
|             receivers: self.receivers.clone(), |             receivers: self.receivers.clone(), | ||||||
|             rules: self.rules.clone(), |             rules: self.rules.clone(), | ||||||
|         }) |         }) | ||||||
|  | |||||||
| @ -53,3 +53,202 @@ pub struct AlertManagerAdditionalPromRules { | |||||||
| pub struct AlertGroup { | pub struct AlertGroup { | ||||||
|     pub groups: Vec<AlertManagerRuleGroup>, |     pub groups: Vec<AlertManagerRuleGroup>, | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, Clone, Serialize)] | ||||||
|  | pub enum HTTPScheme { | ||||||
|  |     #[serde(rename = "http")] | ||||||
|  |     HTTP, | ||||||
|  |     #[serde(rename = "https")] | ||||||
|  |     HTTPS, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, Clone, Serialize)] | ||||||
|  | pub enum Operator { | ||||||
|  |     In, | ||||||
|  |     NotIn, | ||||||
|  |     Exists, | ||||||
|  |     DoesNotExist, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, Clone, Serialize)] | ||||||
|  | #[serde(rename_all = "camelCase")] | ||||||
|  | pub struct PrometheusConfigValues { | ||||||
|  |     pub prometheus: PrometheusConfig, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, Clone, Serialize)] | ||||||
|  | #[serde(rename_all = "camelCase")] | ||||||
|  | pub struct PrometheusConfig { | ||||||
|  |     pub prometheus: bool, | ||||||
|  |     pub additional_service_monitors: Vec<ServiceMonitor>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, Clone, Serialize)] | ||||||
|  | #[serde(rename_all = "camelCase")] | ||||||
|  | pub struct ServiceMonitorTLSConfig { | ||||||
|  |     // ## Path to the CA file
 | ||||||
|  |     // ##
 | ||||||
|  |     pub ca_file: Option<String>, | ||||||
|  | 
 | ||||||
|  |     // ## Path to client certificate file
 | ||||||
|  |     // ##
 | ||||||
|  |     pub cert_file: Option<String>, | ||||||
|  | 
 | ||||||
|  |     // ## Skip certificate verification
 | ||||||
|  |     // ##
 | ||||||
|  |     pub insecure_skip_verify: Option<bool>, | ||||||
|  | 
 | ||||||
|  |     // ## Path to client key file
 | ||||||
|  |     // ##
 | ||||||
|  |     pub key_file: Option<String>, | ||||||
|  | 
 | ||||||
|  |     // ## Server name used to verify host name
 | ||||||
|  |     // ##
 | ||||||
|  |     pub server_name: Option<String>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, Clone, Serialize)] | ||||||
|  | #[serde(rename_all = "camelCase")] | ||||||
|  | pub struct ServiceMonitorEndpoint { | ||||||
|  |     // ## Name of the endpoint's service port
 | ||||||
|  |     // ## Mutually exclusive with targetPort
 | ||||||
|  |     pub port: Option<String>, | ||||||
|  | 
 | ||||||
|  |     // ## Name or number of the endpoint's target port
 | ||||||
|  |     // ## Mutually exclusive with port
 | ||||||
|  |     pub target_port: Option<String>, | ||||||
|  | 
 | ||||||
|  |     // ## File containing bearer token to be used when scraping targets
 | ||||||
|  |     // ##
 | ||||||
|  |     pub bearer_token_file: Option<String>, | ||||||
|  | 
 | ||||||
|  |     // ## Interval at which metrics should be scraped
 | ||||||
|  |     // ##
 | ||||||
|  |     pub interval: Option<String>, | ||||||
|  | 
 | ||||||
|  |     // ## HTTP path to scrape for metrics
 | ||||||
|  |     // ##
 | ||||||
|  |     pub path: String, | ||||||
|  | 
 | ||||||
|  |     // ## HTTP scheme to use for scraping
 | ||||||
|  |     // ##
 | ||||||
|  |     pub scheme: HTTPScheme, | ||||||
|  | 
 | ||||||
|  |     // ## TLS configuration to use when scraping the endpoint
 | ||||||
|  |     // ##
 | ||||||
|  |     pub tls_config: Option<ServiceMonitorTLSConfig>, | ||||||
|  | 
 | ||||||
|  |     // ## MetricRelabelConfigs to apply to samples after scraping, but before ingestion.
 | ||||||
|  |     // ## ref: https://github.com/prometheus-operator/prometheus-operator/blob/main/Documentation/api-reference/api.md#relabelconfig
 | ||||||
|  |     // ##
 | ||||||
|  |     // # - action: keep
 | ||||||
|  |     // #   regex: 'kube_(daemonset|deployment|pod|namespace|node|statefulset).+'
 | ||||||
|  |     // #   sourceLabels: [__name__]
 | ||||||
|  |     pub metric_relabelings: Vec<Mapping>, | ||||||
|  | 
 | ||||||
|  |     // ## RelabelConfigs to apply to samples before scraping
 | ||||||
|  |     // ## ref: https://github.com/prometheus-operator/prometheus-operator/blob/main/Documentation/api-reference/api.md#relabelconfig
 | ||||||
|  |     // ##
 | ||||||
|  |     // # - sourceLabels: [__meta_kubernetes_pod_node_name]
 | ||||||
|  |     // #   separator: ;
 | ||||||
|  |     // #   regex: ^(.*)$
 | ||||||
|  |     // #   targetLabel: nodename
 | ||||||
|  |     // #   replacement: $1
 | ||||||
|  |     // #   action: replace
 | ||||||
|  |     pub relabelings: Vec<Mapping>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, Clone, Serialize)] | ||||||
|  | #[serde(rename_all = "camelCase")] | ||||||
|  | pub struct MatchExpression { | ||||||
|  |     pub key: String, | ||||||
|  |     pub operator: Operator, | ||||||
|  |     pub values: Vec<String>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, Clone, Serialize)] | ||||||
|  | #[serde(rename_all = "camelCase")] | ||||||
|  | pub struct Selector { | ||||||
|  |     //   # label selector for services
 | ||||||
|  |     pub match_labels: HashMap<String, String>, | ||||||
|  |     pub match_expressions: Vec<MatchExpression>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, Clone, Serialize)] | ||||||
|  | #[serde(rename_all = "camelCase")] | ||||||
|  | pub struct ServiceMonitor { | ||||||
|  |     pub name: String, | ||||||
|  | 
 | ||||||
|  |     // # Additional labels to set used for the ServiceMonitorSelector. Together with standard labels from the chart
 | ||||||
|  |     pub additional_labels: Option<Mapping>, | ||||||
|  | 
 | ||||||
|  |     // # Service label for use in assembling a job name of the form <label value>-<port>
 | ||||||
|  |     // # If no label is specified, the service name is used.
 | ||||||
|  |     pub job_label: Option<String>, | ||||||
|  | 
 | ||||||
|  |     // # labels to transfer from the kubernetes service to the target
 | ||||||
|  |     pub target_labels: Vec<String>, | ||||||
|  | 
 | ||||||
|  |     // # labels to transfer from the kubernetes pods to the target
 | ||||||
|  |     pub pod_target_labels: Vec<String>, | ||||||
|  | 
 | ||||||
|  |     // # Label selector for services to which this ServiceMonitor applies
 | ||||||
|  |     //   # Example which selects all services to be monitored
 | ||||||
|  |     //   # with label "monitoredby" with values any of "example-service-1" or "example-service-2"
 | ||||||
|  |     //   matchExpressions:
 | ||||||
|  |     //     - key: "monitoredby"
 | ||||||
|  |     //       operator: In
 | ||||||
|  |     //       values:
 | ||||||
|  |     //         - example-service-1
 | ||||||
|  |     //         - example-service-2
 | ||||||
|  |     pub selector: Selector, | ||||||
|  | 
 | ||||||
|  |     // # Namespaces from which services are selected
 | ||||||
|  |     //   # Match any namespace
 | ||||||
|  |     //   any: bool,
 | ||||||
|  |     //   # Explicit list of namespace names to select
 | ||||||
|  |     //   matchNames: Vec,
 | ||||||
|  |     pub namespace_selector: Option<Mapping>, | ||||||
|  | 
 | ||||||
|  |     // # Endpoints of the selected service to be monitored
 | ||||||
|  |     pub endpoints: Vec<ServiceMonitorEndpoint>, | ||||||
|  | 
 | ||||||
|  |     // # Fallback scrape protocol used by Prometheus for scraping metrics
 | ||||||
|  |     // # ref: https://github.com/prometheus-operator/prometheus-operator/blob/main/Documentation/api-reference/api.md#monitoring.coreos.com/v1.ScrapeProtocol
 | ||||||
|  |     pub fallback_scrape_protocol: Option<String>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Default for ServiceMonitor { | ||||||
|  |     fn default() -> Self { | ||||||
|  |         Self { | ||||||
|  |             name: Default::default(), | ||||||
|  |             additional_labels: Default::default(), | ||||||
|  |             job_label: Default::default(), | ||||||
|  |             target_labels: Default::default(), | ||||||
|  |             pod_target_labels: Default::default(), | ||||||
|  |             selector: Selector { | ||||||
|  |                 match_labels: HashMap::new(), | ||||||
|  |                 match_expressions: vec![], | ||||||
|  |             }, | ||||||
|  |             namespace_selector: Default::default(), | ||||||
|  |             endpoints: Default::default(), | ||||||
|  |             fallback_scrape_protocol: Default::default(), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Default for ServiceMonitorEndpoint { | ||||||
|  |     fn default() -> Self { | ||||||
|  |         Self { | ||||||
|  |             port: Some("80".to_string()), | ||||||
|  |             target_port: Default::default(), | ||||||
|  |             bearer_token_file: Default::default(), | ||||||
|  |             interval: Default::default(), | ||||||
|  |             path: "/metrics".to_string(), | ||||||
|  |             scheme: HTTPScheme::HTTP, | ||||||
|  |             tls_config: Default::default(), | ||||||
|  |             metric_relabelings: Default::default(), | ||||||
|  |             relabelings: Default::default(), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user