Skip to content

metrics-exporter-prometheus: Add API to initialize HTTP listener from existing TcpListener #590

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 38 additions & 5 deletions metrics-exporter-prometheus/src/exporter/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,25 @@ impl PrometheusBuilder {
self
}

/// Same as `with_http_listener`, but allows to bring your own configured tcp listener.
#[cfg(feature = "http-listener")]
#[cfg_attr(docsrs, doc(cfg(feature = "http-listener")))]
#[must_use]
pub fn with_http_listener_from_existing_listener(
mut self,
listener: tokio::net::TcpListener,
) -> Self {
use std::sync::Arc;

// We need to wrap the listener in an Arc to allow for cloning.
let listener = Arc::new(listener);

self.exporter_config = ExporterConfig::HttpListener {
destination: super::ListenDestination::ExistingListener(listener),
};
self
}

/// Configures the exporter to push periodic requests to a Prometheus [push gateway].
///
/// Running in push gateway mode is mutually exclusive with the HTTP listener i.e. enabling the push gateway will
Expand Down Expand Up @@ -485,11 +504,25 @@ impl PrometheusBuilder {
#[cfg(feature = "http-listener")]
ExporterConfig::HttpListener { destination } => match destination {
super::ListenDestination::Tcp(listen_address) => {
super::http_listener::new_http_listener(
handle,
listen_address,
allowed_addresses,
)?
let listener = std::net::TcpListener::bind(listen_address)
.and_then(|listener| {
listener.set_nonblocking(true)?;
Ok(listener)
})
.map_err(|e| BuildError::FailedToCreateHTTPListener(e.to_string()))?;
let listener = tokio::net::TcpListener::from_std(listener).unwrap();
super::http_listener::new_http_listener(handle, listener, allowed_addresses)
}
super::ListenDestination::ExistingListener(listener) => {
use std::sync::Arc;

// Should always succeed as we only created the Arc so the TcpListener can be stored in a Clone
let listener = Arc::try_unwrap(listener).map_err(|_| {
BuildError::FailedToCreateHTTPListener(
"Failed to unwrap Arc<TcpListener>".to_string(),
)
})?;
super::http_listener::new_http_listener(handle, listener, allowed_addresses)
}
#[cfg(feature = "uds-listener")]
super::ListenDestination::Uds(listen_path) => {
Expand Down
18 changes: 4 additions & 14 deletions metrics-exporter-prometheus/src/exporter/http_listener.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use std::net::SocketAddr;

use http_body_util::Full;
use hyper::{
body::{Bytes, Incoming},
Expand All @@ -17,7 +15,7 @@ use tokio::net::{TcpListener, TcpStream};
use tokio::net::{UnixListener, UnixStream};
use tracing::warn;

use crate::{common::BuildError, ExporterFuture, PrometheusHandle};
use crate::{ExporterFuture, PrometheusHandle};

struct HttpListeningExporter {
handle: PrometheusHandle,
Expand Down Expand Up @@ -154,24 +152,16 @@ impl HttpListeningExporter {
/// Will return Err if it cannot bind to the listen address
pub(crate) fn new_http_listener(
handle: PrometheusHandle,
listen_address: SocketAddr,
listener: TcpListener,
allowed_addresses: Option<Vec<IpNet>>,
) -> Result<ExporterFuture, BuildError> {
let listener = std::net::TcpListener::bind(listen_address)
.and_then(|listener| {
listener.set_nonblocking(true)?;
Ok(listener)
})
.map_err(|e| BuildError::FailedToCreateHTTPListener(e.to_string()))?;
let listener = TcpListener::from_std(listener).unwrap();

) -> ExporterFuture {
let exporter = HttpListeningExporter {
handle,
allowed_addresses,
listener_type: ListenerType::Tcp(listener),
};

Ok(Box::pin(async move { exporter.serve().await.map_err(super::ExporterError::HttpListener) }))
Box::pin(async move { exporter.serve().await.map_err(super::ExporterError::HttpListener) })
}

/// Creates an `ExporterFuture` implementing a http listener that serves prometheus metrics.
Expand Down
7 changes: 5 additions & 2 deletions metrics-exporter-prometheus/src/exporter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@
use http_listener::HttpListeningError;
#[cfg(any(feature = "http-listener", feature = "push-gateway"))]
use std::future::Future;
#[cfg(feature = "http-listener")]
use std::net::SocketAddr;
#[cfg(any(feature = "http-listener", feature = "push-gateway"))]
use std::pin::Pin;
#[cfg(feature = "push-gateway")]
use std::time::Duration;
#[cfg(feature = "http-listener")]
use std::{net::SocketAddr, sync::Arc};
#[cfg(feature = "http-listener")]
use tokio::net::TcpListener;

#[cfg(feature = "push-gateway")]
use hyper::Uri;
Expand All @@ -28,6 +30,7 @@ pub type ExporterFuture = Pin<Box<dyn Future<Output = Result<(), ExporterError>>
#[derive(Clone, Debug)]
enum ListenDestination {
Tcp(SocketAddr),
ExistingListener(Arc<TcpListener>),
#[cfg(feature = "uds-listener")]
Uds(std::path::PathBuf),
}
Expand Down
Loading