-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: 🌈 Prometheus remote write support
- Loading branch information
1 parent
1a752dd
commit 5c043c0
Showing
13 changed files
with
348 additions
and
18 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
pub mod app_error; | ||
pub mod influxdb; | ||
pub mod prometheus; | ||
pub mod server; | ||
pub mod state; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
use std::sync::Arc; | ||
|
||
use crate::{ | ||
datamodel::{ | ||
batch_builder::BatchBuilder, sensapp_datetime::SensAppDateTimeExt, | ||
sensapp_vec::SensAppLabels, unit::Unit, Sample, SensAppDateTime, Sensor, SensorType, | ||
TypedSamples, | ||
}, | ||
parsing::prometheus::remote_write_parser::parse_remote_write_request, | ||
}; | ||
|
||
use super::{app_error::AppError, state::HttpServerState}; | ||
use anyhow::Result; | ||
use axum::{ | ||
debug_handler, | ||
extract::State, | ||
http::{HeaderMap, StatusCode}, | ||
}; | ||
use tokio_util::bytes::Bytes; | ||
|
||
fn verify_headers(headers: &HeaderMap) -> Result<(), AppError> { | ||
// Check that we have the right content encoding, that must be snappy | ||
match headers.get("content-encoding") { | ||
Some(content_encoding) => match content_encoding.to_str() { | ||
Ok("snappy") | Ok("SNAPPY") => {} | ||
_ => { | ||
return Err(AppError::BadRequest(anyhow::anyhow!( | ||
"Unsupported content-encoding, must be snappy" | ||
))); | ||
} | ||
}, | ||
None => { | ||
return Err(AppError::BadRequest(anyhow::anyhow!( | ||
"Missing content-encoding header" | ||
))); | ||
} | ||
} | ||
|
||
// Check that the content type is protocol buffer | ||
match headers.get("content-type") { | ||
Some(content_type) => match content_type.to_str() { | ||
Ok("application/x-protobuf") | Ok("APPLICATION/X-PROTOBUF") => {} | ||
_ => { | ||
return Err(AppError::BadRequest(anyhow::anyhow!( | ||
"Unsupported content-type, must be application/x-protobuf" | ||
))); | ||
} | ||
}, | ||
None => { | ||
return Err(AppError::BadRequest(anyhow::anyhow!( | ||
"Missing content-type header" | ||
))); | ||
} | ||
} | ||
|
||
// Check that the remote write version is supported | ||
match headers.get("x-prometheus-remote-write-version") { | ||
Some(version) => match version.to_str() { | ||
Ok("0.1.0") => {} | ||
_ => { | ||
return Err(AppError::BadRequest(anyhow::anyhow!( | ||
"Unsupported x-prometheus-remote-write-version, must be 0.1.0" | ||
))); | ||
} | ||
}, | ||
None => { | ||
return Err(AppError::BadRequest(anyhow::anyhow!( | ||
"Missing x-prometheus-remote-write-version header" | ||
))); | ||
} | ||
} | ||
|
||
Ok(()) | ||
} | ||
|
||
#[debug_handler] | ||
pub async fn publish_prometheus( | ||
State(state): State<HttpServerState>, | ||
headers: HeaderMap, | ||
bytes: Bytes, | ||
) -> Result<StatusCode, AppError> { | ||
// println!("InfluxDB publish"); | ||
// println!("bucket: {}", bucket); | ||
// println!("org: {:?}", org); | ||
// println!("org_id: {:?}", org_id); | ||
// println!("precision: {:?}", precision); | ||
// println!("bytes: {:?}", bytes); | ||
|
||
println!("Received {} bytes", bytes.len()); | ||
|
||
// Verify headers | ||
verify_headers(&headers)?; | ||
|
||
// Parse the content | ||
let write_request = parse_remote_write_request(&bytes)?; | ||
|
||
// Regularly, prometheus sends metadata on the undocumented reserved field, | ||
// so we stop immediately when it happens. | ||
if write_request.timeseries.is_empty() { | ||
return Ok(StatusCode::NO_CONTENT); | ||
} | ||
|
||
println!("Received {} timeseries", write_request.timeseries.len()); | ||
|
||
let mut batch_builder = BatchBuilder::new()?; | ||
for time_serie in write_request.timeseries { | ||
let mut labels = SensAppLabels::with_capacity(time_serie.labels.len()); | ||
let mut name: Option<String> = None; | ||
let mut unit: Option<Unit> = None; | ||
for label in time_serie.labels { | ||
match label.name.as_str() { | ||
"__name__" => { | ||
name = Some(label.value.clone()); | ||
} | ||
"unit" => { | ||
unit = Some(Unit::new(label.value.clone(), None)); | ||
} | ||
_ => {} | ||
} | ||
labels.push((label.name, label.value)); | ||
} | ||
let name = match name { | ||
Some(name) => name, | ||
None => { | ||
return Err(AppError::BadRequest(anyhow::anyhow!( | ||
"A time serie is missing its __name__ label" | ||
))); | ||
} | ||
}; | ||
|
||
// Prometheus has a very simple model, it's always a float. | ||
let sensor = Sensor::new_without_uuid(name, SensorType::Float, unit, Some(labels))?; | ||
|
||
// We can now add the samples | ||
let samples = TypedSamples::Float( | ||
time_serie | ||
.samples | ||
.into_iter() | ||
.map(|sample| Sample { | ||
datetime: SensAppDateTime::from_unix_milliseconds_i64(sample.timestamp), | ||
value: sample.value, | ||
}) | ||
.collect(), | ||
); | ||
|
||
batch_builder.add(Arc::new(sensor), samples).await?; | ||
// batch_builder.send_if_batch_full(event_bus.clone()).await?; | ||
} | ||
|
||
match batch_builder.send_what_is_left(state.event_bus).await { | ||
Ok(Some(mut receiver)) => { | ||
receiver.wait().await?; | ||
} | ||
Ok(None) => {} | ||
Err(error) => { | ||
return Err(AppError::InternalServerError(anyhow::anyhow!(error))); | ||
} | ||
} | ||
|
||
// OK no content | ||
Ok(StatusCode::NO_CONTENT) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,6 +20,7 @@ mod http; | |
mod importers; | ||
mod infer; | ||
mod name_to_uuid; | ||
mod parsing; | ||
mod storage; | ||
|
||
#[tokio::main] | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
pub mod prometheus; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
pub mod remote_write_models; | ||
pub mod remote_write_parser; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
// https://prometheus.io/docs/concepts/remote_write_spec/ | ||
// Version: 1.0 | ||
// Status: Published | ||
// Date: April 2023 | ||
|
||
syntax = "proto3"; | ||
|
||
message WriteRequest { | ||
repeated TimeSeries timeseries = 1; | ||
// Cortex uses this field to determine the source of the write request. | ||
// We reserve it to avoid any compatibility issues. | ||
reserved 2; | ||
|
||
// Prometheus uses this field to send metadata, but this is | ||
// omitted from v1 of the spec as it is experimental. | ||
reserved 3; | ||
} | ||
|
||
message TimeSeries { | ||
repeated Label labels = 1; | ||
repeated Sample samples = 2; | ||
} | ||
|
||
message Label { | ||
string name = 1; | ||
string value = 2; | ||
} | ||
|
||
message Sample { | ||
double value = 1; | ||
int64 timestamp = 2; | ||
} |
Oops, something went wrong.