From 9912f4577b3c0e689046260a63d967abc546078b Mon Sep 17 00:00:00 2001 From: Oliver Gould Date: Wed, 8 Aug 2018 11:03:15 -0700 Subject: [PATCH] Consolidate telemetry::metrics::tls_config_reload (#45) Details of TLS configuration reload metrics span several modules. This change consolidates all of this logic into a single module with a single public type, `TlsConfigReload`, that supports recording successful reloads, recording errors, and formatting its current state. This sets up further simplifications, eventually leading to the removal of the `Event` API. --- src/telemetry/metrics/labels/mod.rs | 61 +---------- src/telemetry/metrics/mod.rs | 55 ++-------- src/telemetry/metrics/record.rs | 16 +-- src/telemetry/metrics/tls_config_reload.rs | 112 +++++++++++++++++++++ 4 files changed, 122 insertions(+), 122 deletions(-) create mode 100644 src/telemetry/metrics/tls_config_reload.rs diff --git a/src/telemetry/metrics/labels/mod.rs b/src/telemetry/metrics/labels/mod.rs index 08c74453d..aea037e47 100644 --- a/src/telemetry/metrics/labels/mod.rs +++ b/src/telemetry/metrics/labels/mod.rs @@ -2,7 +2,6 @@ use std::{ collections::HashMap, fmt::{self, Write}, hash, - path::PathBuf, sync::Arc, }; @@ -46,7 +45,7 @@ macro_rules! mk_err_enum { } mod errno; -use self::errno::Errno; +pub use self::errno::Errno; #[derive(Clone, Debug, Eq, PartialEq, Hash)] pub struct RequestLabels { @@ -133,15 +132,6 @@ pub struct DstLabels { #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] pub struct TlsStatus(ctx::transport::TlsStatus); -#[derive(Clone, Debug, Eq, PartialEq, Hash)] -pub enum TlsConfigLabels { - Reloaded, - InvalidTrustAnchors, - InvalidPrivateKey, - InvalidEndEntityCert, - Io { path: PathBuf, errno: Option, }, -} - // ===== impl RequestLabels ===== impl RequestLabels { @@ -461,55 +451,6 @@ impl Into for TlsStatus { self.0 } } - -// ===== impl TlsConfigLabels ===== - -impl TlsConfigLabels { - pub fn success() -> Self { - TlsConfigLabels::Reloaded - } -} - -impl From for TlsConfigLabels { - fn from(err: tls::ConfigError) -> Self { - match err { - tls::ConfigError::Io(path, error_code) => - TlsConfigLabels::Io { path, errno: error_code.map(Errno::from) }, - tls::ConfigError::FailedToParseTrustAnchors(_) => - TlsConfigLabels::InvalidTrustAnchors, - tls::ConfigError::EndEntityCertIsNotValid(_) => - TlsConfigLabels::InvalidEndEntityCert, - tls::ConfigError::InvalidPrivateKey => - TlsConfigLabels::InvalidPrivateKey, - } - } -} - -impl fmt::Display for TlsConfigLabels { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - TlsConfigLabels::Reloaded => - f.pad("status=\"reloaded\""), - TlsConfigLabels::Io { ref path, errno: Some(errno) } => - write!(f, - "status=\"io_error\",path=\"{}\",errno=\"{}\"", - path.display(), errno - ), - TlsConfigLabels::Io { ref path, errno: None } => - write!(f, - "status=\"io_error\",path=\"{}\",errno=\"UNKNOWN\"", - path.display(), - ), - TlsConfigLabels::InvalidPrivateKey => - f.pad("status=\"invalid_private_key\""), - TlsConfigLabels::InvalidEndEntityCert => - f.pad("status=\"invalid_end_entity_cert\""), - TlsConfigLabels::InvalidTrustAnchors => - f.pad("status=\"invalid_trust_anchors\""), - } - } -} - impl fmt::Display for tls::ReasonForNoIdentity { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { diff --git a/src/telemetry/metrics/mod.rs b/src/telemetry/metrics/mod.rs index f496886ec..7013dc10e 100644 --- a/src/telemetry/metrics/mod.rs +++ b/src/telemetry/metrics/mod.rs @@ -59,6 +59,7 @@ mod latency; mod process; mod record; mod serve; +mod tls_config_reload; mod transport; use self::counter::Counter; @@ -70,12 +71,10 @@ use self::labels::{ TransportLabels, TransportCloseLabels, }; -pub use self::labels::{ - DstLabels, - TlsConfigLabels, -}; +pub use self::labels::DstLabels; pub use self::record::Record; pub use self::serve::Serve; +pub use self::tls_config_reload::TlsConfigReload; /// Writes a metric in prometheus-formatted output. /// @@ -112,10 +111,7 @@ struct Root { responses: http::ResponseScopes, transports: transport::OpenScopes, transport_closes: transport::CloseScopes, - - tls_config: TlsConfigScopes, - tls_config_last_reload_seconds: Option, - + tls_config_reload: TlsConfigReload, process: process::Process, } @@ -134,8 +130,6 @@ struct Stamped { inner: T, } -type TlsConfigScopes = Scopes; - /// Construct the Prometheus metrics. /// /// Returns the `Record` and `Serve` sides. The `Serve` side @@ -180,13 +174,6 @@ impl<'a, M: FmtMetric> Metric<'a, M> { // ===== impl Root ===== impl Root { - metrics! { - tls_config_last_reload_seconds: Gauge { - "Timestamp of when the TLS configuration files were last reloaded \ - successfully (in seconds since the UNIX epoch)" - } - } - pub fn new(process: &Arc) -> Self { Self { process: process::Process::new(&process), @@ -218,9 +205,8 @@ impl Root { .stamped() } - fn tls_config(&mut self, labels: TlsConfigLabels) -> &mut Counter { - self.tls_config.scopes.entry(labels) - .or_insert_with(|| Counter::default()) + fn tls_config(&mut self) -> &mut TlsConfigReload { + &mut self.tls_config_reload } fn retain_since(&mut self, epoch: Instant) { @@ -237,40 +223,13 @@ impl fmt::Display for Root { self.responses.fmt(f)?; self.transports.fmt(f)?; self.transport_closes.fmt(f)?; - self.tls_config.fmt(f)?; - - if let Some(timestamp) = self.tls_config_last_reload_seconds { - Self::tls_config_last_reload_seconds.fmt_help(f)?; - Self::tls_config_last_reload_seconds.fmt_metric(f, timestamp)?; - } - + self.tls_config_reload.fmt(f)?; self.process.fmt(f)?; Ok(()) } } -impl TlsConfigScopes { - metrics! { - tls_config_reload_total: Counter { - "Total number of times the proxy's TLS config files were reloaded." - } - } -} - -impl fmt::Display for TlsConfigScopes { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - if self.scopes.is_empty() { - return Ok(()); - } - - Self::tls_config_reload_total.fmt_help(f)?; - Self::tls_config_reload_total.fmt_scopes(f, &self, |s| &s)?; - - Ok(()) - } -} - // ===== impl Stamped ===== impl Stamped { diff --git a/src/telemetry/metrics/record.rs b/src/telemetry/metrics/record.rs index bd855c5fd..23eaeb736 100644 --- a/src/telemetry/metrics/record.rs +++ b/src/telemetry/metrics/record.rs @@ -7,9 +7,7 @@ use super::labels::{ ResponseLabels, TransportLabels, TransportCloseLabels, - TlsConfigLabels, }; -use std::time::UNIX_EPOCH; /// Tracks Prometheus metrics #[derive(Clone, Debug)] @@ -86,20 +84,10 @@ impl Record { }, Event::TlsConfigReloaded(ref when) => - self.update(|metrics| { - let timestamp_secs = when - .duration_since(UNIX_EPOCH) - .expect("SystemTime before UNIX_EPOCH!") - .as_secs() - .into(); - metrics.tls_config_last_reload_seconds = Some(timestamp_secs); - metrics.tls_config(TlsConfigLabels::success()).incr(); - }), + self.update(|m| m.tls_config().success(when)), Event::TlsConfigReloadFailed(ref err) => - self.update(|metrics| { - metrics.tls_config(err.clone().into()).incr(); - }), + self.update(|m| m.tls_config().error(err.clone())), }; } } diff --git a/src/telemetry/metrics/tls_config_reload.rs b/src/telemetry/metrics/tls_config_reload.rs new file mode 100644 index 000000000..280166463 --- /dev/null +++ b/src/telemetry/metrics/tls_config_reload.rs @@ -0,0 +1,112 @@ +use std::{fmt, path::PathBuf, time::{SystemTime, UNIX_EPOCH}}; + +use telemetry::metrics::{Counter, Gauge, Metric, Scopes, labels::Errno}; +use transport::tls; + +metrics! { + tls_config_last_reload_seconds: Gauge { + "Timestamp of the last successful TLS configuration reload \ + (in seconds since the UNIX epoch)" + }, + tls_config_reload_total: Counter { + "Total number of TLS configuration reloads" + } +} + +#[derive(Debug, Default)] +pub struct TlsConfigReload { + last_reload: Option, + by_status: Scopes, +} + +#[derive(Debug, Eq, PartialEq, Hash)] +enum Status { + Reloaded, + InvalidTrustAnchors, + InvalidPrivateKey, + InvalidEndEntityCert, + Io { path: PathBuf, errno: Option, }, +} + +// ===== impl TlsConfigReload ===== + +impl TlsConfigReload { + pub fn success(&mut self, when: &SystemTime) { + let t = when + .duration_since(UNIX_EPOCH) + .expect("times must be after UNIX epoch") + .as_secs(); + self.last_reload = Some(t.into()); + + self.by_status.scopes + .entry(Status::Reloaded) + .or_insert_with(|| Counter::default()) + .incr() + } + + pub fn error(&mut self, e: tls::ConfigError) { + self.by_status.scopes + .entry(e.into()) + .or_insert_with(|| Counter::default()) + .incr() + } +} + +impl fmt::Display for TlsConfigReload { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + if !self.by_status.scopes.is_empty() { + tls_config_reload_total.fmt_help(f)?; + tls_config_reload_total.fmt_scopes(f, &self.by_status, |s| &s)?; + } + + if let Some(timestamp) = self.last_reload { + tls_config_last_reload_seconds.fmt_help(f)?; + tls_config_last_reload_seconds.fmt_metric(f, timestamp)?; + } + + Ok(()) + } +} + +// ===== impl Status ===== + +impl From for Status { + fn from(err: tls::ConfigError) -> Self { + match err { + tls::ConfigError::Io(path, error_code) => + Status::Io { path, errno: error_code.map(Errno::from) }, + tls::ConfigError::FailedToParseTrustAnchors(_) => + Status::InvalidTrustAnchors, + tls::ConfigError::EndEntityCertIsNotValid(_) => + Status::InvalidEndEntityCert, + tls::ConfigError::InvalidPrivateKey => + Status::InvalidPrivateKey, + } + } +} + +impl fmt::Display for Status { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Status::Reloaded => + f.pad("status=\"reloaded\""), + Status::Io { ref path, errno: Some(errno) } => + write!(f, + "status=\"io_error\",path=\"{}\",errno=\"{}\"", + path.display(), errno + ), + Status::Io { ref path, errno: None } => + write!(f, + "status=\"io_error\",path=\"{}\",errno=\"UNKNOWN\"", + path.display(), + ), + Status::InvalidPrivateKey => + f.pad("status=\"invalid_private_key\""), + Status::InvalidEndEntityCert => + f.pad("status=\"invalid_end_entity_cert\""), + Status::InvalidTrustAnchors => + f.pad("status=\"invalid_trust_anchors\""), + } + } +} +