1
0
mirror of https://github.com/matrix-org/matrix-authentication-service.git synced 2025-07-29 22:01:14 +03:00

Gate the prometheus exporter behind a feature

Also fixes the Docker image building and the CI builds
This commit is contained in:
Quentin Gliech
2022-09-28 10:52:41 +02:00
parent e9fd0eb36a
commit 9e3b3567b2
4 changed files with 72 additions and 30 deletions

View File

@ -170,6 +170,9 @@ jobs:
profile: minimal profile: minimal
override: true override: true
- name: Install Protoc
uses: arduino/setup-protoc@v1
- name: Setup OPA - name: Setup OPA
uses: open-policy-agent/setup-opa@v1 uses: open-policy-agent/setup-opa@v1
with: with:
@ -248,6 +251,9 @@ jobs:
profile: minimal profile: minimal
override: true override: true
- name: Install Protoc
uses: arduino/setup-protoc@v1
- name: Setup OPA - name: Setup OPA
uses: open-policy-agent/setup-opa@v1 uses: open-policy-agent/setup-opa@v1
with: with:
@ -329,6 +335,9 @@ jobs:
override: true override: true
components: llvm-tools-preview components: llvm-tools-preview
- name: Install Protoc
uses: arduino/setup-protoc@v1
- name: Setup OPA - name: Setup OPA
uses: open-policy-agent/setup-opa@v1 uses: open-policy-agent/setup-opa@v1
with: with:

View File

@ -66,7 +66,8 @@ RUN \
g++-aarch64-linux-gnu \ g++-aarch64-linux-gnu \
g++-x86-64-linux-gnu \ g++-x86-64-linux-gnu \
libc6-dev-arm64-cross \ libc6-dev-arm64-cross \
libc6-dev-amd64-cross libc6-dev-amd64-cross \
protobuf-compiler
WORKDIR /app WORKDIR /app
RUN \ RUN \

View File

@ -31,8 +31,8 @@ opentelemetry-jaeger = { version = "0.17.0", features = ["rt-tokio", "collector_
opentelemetry-otlp = { version = "0.11.0", features = ["trace", "metrics", "http-proto"], optional = true } opentelemetry-otlp = { version = "0.11.0", features = ["trace", "metrics", "http-proto"], optional = true }
opentelemetry-zipkin = { version = "0.16.0", features = ["opentelemetry-http"], default-features = false, optional = true } opentelemetry-zipkin = { version = "0.16.0", features = ["opentelemetry-http"], default-features = false, optional = true }
opentelemetry-http = { version = "0.7.0", features = ["tokio", "hyper"], optional = true } opentelemetry-http = { version = "0.7.0", features = ["tokio", "hyper"], optional = true }
opentelemetry-prometheus = "0.11.0" opentelemetry-prometheus = { version = "0.11.0", optional = true }
prometheus = "0.13.2" prometheus = { version = "0.13.2", optional = true }
mas-config = { path = "../config" } mas-config = { path = "../config" }
mas-email = { path = "../email" } mas-email = { path = "../email" }
@ -49,7 +49,7 @@ mas-templates = { path = "../templates" }
indoc = "1.0.7" indoc = "1.0.7"
[features] [features]
default = ["otlp", "jaeger", "zipkin", "native-roots"] default = ["otlp", "jaeger", "zipkin", "prometheus", "native-roots"]
# Use the native root certificates # Use the native root certificates
native-roots = ["mas-http/native-roots", "mas-handlers/native-roots"] native-roots = ["mas-http/native-roots", "mas-handlers/native-roots"]
@ -59,9 +59,11 @@ webpki-roots = ["mas-http/webpki-roots", "mas-handlers/webpki-roots"]
# Read the builtin static files and templates from the source directory # Read the builtin static files and templates from the source directory
dev = ["mas-templates/dev", "mas-static-files/dev"] dev = ["mas-templates/dev", "mas-static-files/dev"]
# Enable OpenTelemetry OTLP exporter. Requires "protoc" # Enable OpenTelemetry OTLP exporter.
otlp = ["dep:opentelemetry-otlp", "dep:opentelemetry-http"] otlp = ["dep:opentelemetry-otlp", "dep:opentelemetry-http"]
# Enable OpenTelemetry Jaeger exporter and propagator. # Enable OpenTelemetry Jaeger exporter and propagator.
jaeger = ["dep:opentelemetry-jaeger"] jaeger = ["dep:opentelemetry-jaeger"]
# Enable OpenTelemetry Zipkin exporter and B3 propagator. # Enable OpenTelemetry Zipkin exporter and B3 propagator.
zipkin = ["dep:opentelemetry-zipkin", "dep:opentelemetry-http"] zipkin = ["dep:opentelemetry-zipkin", "dep:opentelemetry-http"]
# Enable OpenTelemetry Prometheus exporter. Requires "protoc"
prometheus = ["dep:opentelemetry-prometheus", "dep:prometheus"]

View File

@ -12,14 +12,9 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
use std::{ use std::time::Duration;
convert::Infallible,
net::{SocketAddr, TcpListener},
time::Duration,
};
use anyhow::{bail, Context as _}; use anyhow::{bail, Context as _};
use hyper::{header::CONTENT_TYPE, service::make_service_fn, Body, Method, Request, Response};
use mas_config::{ use mas_config::{
JaegerExporterProtocolConfig, MetricsExporterConfig, Propagator, TelemetryConfig, JaegerExporterProtocolConfig, MetricsExporterConfig, Propagator, TelemetryConfig,
TracingExporterConfig, TracingExporterConfig,
@ -41,10 +36,7 @@ use opentelemetry_jaeger::Propagator as JaegerPropagator;
use opentelemetry_semantic_conventions as semcov; use opentelemetry_semantic_conventions as semcov;
#[cfg(feature = "zipkin")] #[cfg(feature = "zipkin")]
use opentelemetry_zipkin::{B3Encoding, Propagator as ZipkinPropagator}; use opentelemetry_zipkin::{B3Encoding, Propagator as ZipkinPropagator};
use prometheus::{Encoder, TextEncoder};
use tokio::sync::OnceCell; use tokio::sync::OnceCell;
use tower::service_fn;
use tracing::info;
use url::Url; use url::Url;
static METRICS_BASIC_CONTROLLER: OnceCell<Option<BasicController>> = OnceCell::const_new(); static METRICS_BASIC_CONTROLLER: OnceCell<Option<BasicController>> = OnceCell::const_new();
@ -60,8 +52,10 @@ pub async fn setup(
mas_http::set_propagator(&propagator); mas_http::set_propagator(&propagator);
global::set_text_map_propagator(propagator); global::set_text_map_propagator(propagator);
let tracer = tracer(&config.tracing.exporter).await?; let tracer = tracer(&config.tracing.exporter)
let meter = meter(&config.metrics.exporter)?; .await
.context("Failed to configure traces exporter")?;
let meter = meter(&config.metrics.exporter).context("Failed to configure metrics exporter")?;
METRICS_BASIC_CONTROLLER.set(meter.clone())?; METRICS_BASIC_CONTROLLER.set(meter.clone())?;
Ok((tracer, meter)) Ok((tracer, meter))
@ -97,8 +91,7 @@ fn match_propagator(
))), ))),
p => bail!( p => bail!(
"The service was compiled without support for the {:?} propagator, but config uses it.", "The service was compiled without support for the {p:?} propagator, but config uses it.",
p
), ),
} }
} }
@ -112,7 +105,9 @@ fn propagator(propagators: &[Propagator]) -> anyhow::Result<impl TextMapPropagat
#[cfg(any(feature = "otlp", feature = "jaeger"))] #[cfg(any(feature = "otlp", feature = "jaeger"))]
async fn http_client() -> anyhow::Result<impl opentelemetry_http::HttpClient + 'static> { async fn http_client() -> anyhow::Result<impl opentelemetry_http::HttpClient + 'static> {
let client = mas_http::make_untraced_client().await?; let client = mas_http::make_untraced_client()
.await
.context("Failed to build HTTP client used by telemetry exporter")?;
let client = let client =
opentelemetry_http::hyper::HyperClient::new_with_timeout(client, Duration::from_secs(30)); opentelemetry_http::hyper::HyperClient::new_with_timeout(client, Duration::from_secs(30));
Ok(client) Ok(client)
@ -138,18 +133,22 @@ fn otlp_tracer(endpoint: &Option<Url>) -> anyhow::Result<Tracer> {
.tracing() .tracing()
.with_exporter(exporter) .with_exporter(exporter)
.with_trace_config(trace_config()) .with_trace_config(trace_config())
.install_batch(opentelemetry::runtime::Tokio)?; .install_batch(opentelemetry::runtime::Tokio)
.context("Failed to configure OTLP trace exporter")?;
Ok(tracer) Ok(tracer)
} }
#[cfg(not(feature = "otlp"))] #[cfg(not(feature = "otlp"))]
fn otlp_tracer(_endpoint: &Option<Url>) -> anyhow::Result<Tracer> { fn otlp_tracer(endpoint: &Option<Url>) -> anyhow::Result<Tracer> {
let _ = endpoint;
anyhow::bail!("The service was compiled without OTLP exporter support, but config exports traces via OTLP.") anyhow::bail!("The service was compiled without OTLP exporter support, but config exports traces via OTLP.")
} }
#[cfg(not(feature = "jaeger"))] #[cfg(not(feature = "jaeger"))]
fn jaeger_agent_tracer(host: &str, port: u16) -> anyhow::Result<Tracer> { fn jaeger_agent_tracer(host: &str, port: u16) -> anyhow::Result<Tracer> {
let _ = host;
let _ = port;
anyhow::bail!("The service was compiled without Jaeger exporter support, but config exports traces via Jaeger.") anyhow::bail!("The service was compiled without Jaeger exporter support, but config exports traces via Jaeger.")
} }
@ -160,7 +159,9 @@ fn jaeger_agent_tracer(host: &str, port: u16) -> anyhow::Result<Tracer> {
.with_trace_config(trace_config()) .with_trace_config(trace_config())
.with_endpoint((host, port)); .with_endpoint((host, port));
let tracer = pipeline.install_batch(opentelemetry::runtime::Tokio)?; let tracer = pipeline
.install_batch(opentelemetry::runtime::Tokio)
.context("Failed to configure Jaeger agent exporter")?;
Ok(tracer) Ok(tracer)
} }
@ -171,6 +172,10 @@ async fn jaeger_collector_tracer(
username: Option<&str>, username: Option<&str>,
password: Option<&str>, password: Option<&str>,
) -> anyhow::Result<Tracer> { ) -> anyhow::Result<Tracer> {
let _ = endpoint;
let _ = username;
let _ = password;
futures_util::future::ready(()).await; // Silence the "unused async" lint
anyhow::bail!("The service was compiled without Jaeger exporter support, but config exports traces via Jaeger.") anyhow::bail!("The service was compiled without Jaeger exporter support, but config exports traces via Jaeger.")
} }
@ -195,13 +200,17 @@ async fn jaeger_collector_tracer(
pipeline = pipeline.with_password(password); pipeline = pipeline.with_password(password);
} }
let tracer = pipeline.install_batch(opentelemetry::runtime::Tokio)?; let tracer = pipeline
.install_batch(opentelemetry::runtime::Tokio)
.context("Failed to configure Jaeger collector exporter")?;
Ok(tracer) Ok(tracer)
} }
#[cfg(not(feature = "zipkin"))] #[cfg(not(feature = "zipkin"))]
async fn zipkin_tracer(_collector_endpoint: &Option<Url>) -> anyhow::Result<Tracer> { async fn zipkin_tracer(collector_endpoint: &Option<Url>) -> anyhow::Result<Tracer> {
let _ = collector_endpoint;
futures_util::future::ready(()).await; // Silence the "unused async" lint
anyhow::bail!("The service was compiled without Jaeger exporter support, but config exports traces via Jaeger.") anyhow::bail!("The service was compiled without Jaeger exporter support, but config exports traces via Jaeger.")
} }
@ -218,7 +227,9 @@ async fn zipkin_tracer(collector_endpoint: &Option<Url>) -> anyhow::Result<Trace
pipeline = pipeline.with_collector_endpoint(collector_endpoint.as_str()); pipeline = pipeline.with_collector_endpoint(collector_endpoint.as_str());
} }
let tracer = pipeline.install_batch(opentelemetry::runtime::Tokio)?; let tracer = pipeline
.install_batch(opentelemetry::runtime::Tokio)
.context("Failed to configure Zipkin exporter")?;
Ok(tracer) Ok(tracer)
} }
@ -262,13 +273,15 @@ fn otlp_meter(endpoint: &Option<url::Url>) -> anyhow::Result<BasicController> {
) )
.with_resource(resource()) .with_resource(resource())
.with_exporter(exporter) .with_exporter(exporter)
.build()?; .build()
.context("Failed to configure OTLP metrics exporter")?;
Ok(controller) Ok(controller)
} }
#[cfg(not(feature = "otlp"))] #[cfg(not(feature = "otlp"))]
fn otlp_meter(_endpoint: &Option<url::Url>) -> anyhow::Result<BasicController> { fn otlp_meter(endpoint: &Option<url::Url>) -> anyhow::Result<BasicController> {
let _ = endpoint;
anyhow::bail!("The service was compiled without OTLP exporter support, but config exports metrics via OTLP.") anyhow::bail!("The service was compiled without OTLP exporter support, but config exports metrics via OTLP.")
} }
@ -289,7 +302,22 @@ fn stdout_meter() -> anyhow::Result<BasicController> {
Ok(controller) Ok(controller)
} }
#[cfg(not(feature = "prometheus"))]
fn prometheus_meter(address: &str) -> anyhow::Result<BasicController> { fn prometheus_meter(address: &str) -> anyhow::Result<BasicController> {
let _ = address;
anyhow::bail!("The service was compiled without Prometheus exporter support, but config exports metrics via Prometheus.")
}
#[cfg(feature = "prometheus")]
fn prometheus_meter(address: &str) -> anyhow::Result<BasicController> {
use std::{
convert::Infallible,
net::{SocketAddr, TcpListener},
};
use hyper::{header::CONTENT_TYPE, service::make_service_fn, Body, Method, Request, Response};
use prometheus::{Encoder, TextEncoder};
let controller = sdk::metrics::controllers::basic( let controller = sdk::metrics::controllers::basic(
sdk::metrics::processors::factory( sdk::metrics::processors::factory(
sdk::metrics::selectors::simple::histogram([ sdk::metrics::selectors::simple::histogram([
@ -307,7 +335,7 @@ fn prometheus_meter(address: &str) -> anyhow::Result<BasicController> {
let make_svc = make_service_fn(move |_conn| { let make_svc = make_service_fn(move |_conn| {
let exporter = exporter.clone(); let exporter = exporter.clone();
async move { async move {
Ok::<_, Infallible>(service_fn(move |req: Request<Body>| { Ok::<_, Infallible>(tower::service_fn(move |req: Request<Body>| {
let exporter = exporter.clone(); let exporter = exporter.clone();
async move { async move {
let response = match (req.method(), req.uri().path()) { let response = match (req.method(), req.uri().path()) {
@ -340,12 +368,14 @@ fn prometheus_meter(address: &str) -> anyhow::Result<BasicController> {
.context("could not parse listener address")?; .context("could not parse listener address")?;
let listener = TcpListener::bind(address).context("could not bind address")?; let listener = TcpListener::bind(address).context("could not bind address")?;
info!( tracing::info!(
"Prometheus exporter listening on on http://{}/metrics", "Prometheus exporter listening on on http://{}/metrics",
listener.local_addr().unwrap() listener.local_addr().unwrap()
); );
let server = hyper::server::Server::from_tcp(listener)?.serve(make_svc); let server = hyper::server::Server::from_tcp(listener)
.context("Failed to start HTTP server for the Prometheus metrics exporter")?
.serve(make_svc);
tokio::spawn(server); tokio::spawn(server);
Ok(controller) Ok(controller)