From 710f73332dcd05dfc9269f4d91cf269bede5858e Mon Sep 17 00:00:00 2001 From: Jeremy Wall Date: Tue, 13 Feb 2024 16:15:19 -0600 Subject: [PATCH] feat: Dashboard level timespans --- examples/example_dashboards.yaml | 4 +++ src/dashboard.rs | 62 +++++++++++++++++++------------- src/routes.rs | 9 ++--- 3 files changed, 46 insertions(+), 29 deletions(-) diff --git a/examples/example_dashboards.yaml b/examples/example_dashboards.yaml index 90d9e36..cd0de21 100644 --- a/examples/example_dashboards.yaml +++ b/examples/example_dashboards.yaml @@ -11,6 +11,10 @@ step_duration: 1min name_label: instance - title: Test Dasbboard 2 + span: + start: 2024-02-10T00:00:00.00Z + duration: 2d + step_duration: 1min graphs: - title: Node cpu source: http://heimdall:9001 diff --git a/src/dashboard.rs b/src/dashboard.rs index a7a7d55..1dca17e 100644 --- a/src/dashboard.rs +++ b/src/dashboard.rs @@ -14,18 +14,13 @@ use std::path::Path; use chrono::prelude::*; +use chrono::Duration; use serde::Deserialize; use serde_yaml; use tracing::{debug, error}; use crate::query::{QueryConn, QueryType}; -#[derive(Deserialize)] -pub struct Dashboard { - pub title: String, - pub graphs: Vec, -} - #[derive(Deserialize)] pub struct GraphSpan { pub start: DateTime, @@ -33,6 +28,13 @@ pub struct GraphSpan { pub step_duration: String, } +#[derive(Deserialize)] +pub struct Dashboard { + pub title: String, + pub graphs: Vec, + pub span: Option +} + #[derive(Deserialize)] pub struct Graph { pub title: String, @@ -44,9 +46,9 @@ pub struct Graph { pub query_type: QueryType, } -fn duration_from_string(duration: &str) -> Option { +fn duration_from_string(duration: &str) -> Option { match parse_duration::parse(duration) { - Ok(d) => match chrono::Duration::from_std(d) { + Ok(d) => match Duration::from_std(d) { Ok(d) => Some(d), Err(e) => { error!(err = ?e, "specified Duration is out of bounds"); @@ -63,30 +65,40 @@ fn duration_from_string(duration: &str) -> Option { } } +fn graph_span_to_tuple(span: &Option) -> Option<(DateTime, Duration, Duration)> { + if span.is_none() { + return None; + } + let span = span.as_ref().unwrap(); + let duration = match duration_from_string(&span.duration) { + Some(d) => d, + None => { + error!("Invalid query duration not assigning span to to graph query"); + return None; + } + }; + let step_duration = match duration_from_string(&span.step_duration) { + Some(d) => d, + None => { + error!("Invalid query step resolution not assigning span to to graph query"); + return None; + } + }; + Some((span.start.clone(), duration, step_duration)) +} + impl Graph { - pub fn get_query_connection<'conn, 'graph: 'conn>(&'graph self) -> QueryConn<'conn> { + pub fn get_query_connection<'conn, 'graph: 'conn>(&'graph self, graph_span: &'graph Option) -> QueryConn<'conn> { debug!( query = self.query, source = self.source, "Getting query connection for graph" ); let mut conn = QueryConn::new(&self.source, &self.query, self.query_type.clone()); - if let Some(span) = &self.span { - let duration = match duration_from_string(&span.duration) { - Some(d) => d, - None => { - error!("Invalid query duration not assigning span to to graph query"); - return conn; - } - }; - let step_duration = match duration_from_string(&span.step_duration) { - Some(d) => d, - None => { - error!("Invalid query step resolution not assigning span to to graph query"); - return conn; - } - }; - conn = conn.with_span(span.start.clone(), duration, step_duration); + if let Some((start, duration, step_duration)) = graph_span_to_tuple(&self.span) { + conn = conn.with_span(start, duration, step_duration); + } else if let Some((start, duration, step_duration)) = graph_span_to_tuple(graph_span) { + conn = conn.with_span(start, duration, step_duration); } conn } diff --git a/src/routes.rs b/src/routes.rs index ea73342..3b08e54 100644 --- a/src/routes.rs +++ b/src/routes.rs @@ -34,15 +34,15 @@ pub async fn graph_query( Path((dash_idx, graph_idx)): Path<(usize, usize)>, ) -> Json { debug!("Getting data for query"); - let graph = config + let dash = config .get(dash_idx) - .expect("No such dashboard index") - .graphs + .expect("No such dashboard index"); + let graph = dash.graphs .get(graph_idx) .expect(&format!("No such graph in dasboard {}", dash_idx)); let data = to_samples( graph - .get_query_connection() + .get_query_connection(&dash.span) .get_results() .await .expect("Unable to get query results") @@ -54,6 +54,7 @@ pub async fn graph_query( pub fn mk_api_routes(config: Arc>) -> Router { // Query routes + // TODO(zaphar): Allow passing the timespan in via query Router::new().route( "/dash/:dash_idx/graph/:graph_idx", get(graph_query).with_state(config),