mirror of
https://github.com/zaphar/Heracles.git
synced 2025-07-22 20:19:50 -04:00
feat: Flag to validate config and queries succeeed.
This commit is contained in:
parent
835c120d4b
commit
8aa41eb68c
18
examples/example_bad_dashboard.yaml
Normal file
18
examples/example_bad_dashboard.yaml
Normal file
@ -0,0 +1,18 @@
|
||||
--- # A list of dashboards
|
||||
- title: Invalid Dasbboard
|
||||
graphs: # Each Dashboard can have 1 or more graphs in it.
|
||||
- title: Node cpu # Graphs have titles
|
||||
query_type: Range # The type of graph. Range for timeseries and Scalar for point in time
|
||||
d3_tick_format: "~s" # Default tick format for the graph y axis
|
||||
plots: # List of pluts to show on the graph
|
||||
- source: http://heimdall:9001 # Prometheus source uri for this plot
|
||||
query: 'sum by (instance)(irate(node_cpu_seconds_total{job="nodestats"}[5m])' # syntax error in query
|
||||
meta: # metadata for this plot
|
||||
name_format: "`${labels.instance}`" # javascript template literal to format the trace name
|
||||
fill: tozeroy
|
||||
#d3_tick_format: "~%" # d3 tick format override for this plot's yaxis
|
||||
#named_axis: "y" # yaxis name to use for this subplots traces
|
||||
span: # The span for this range query
|
||||
end: now # Where the span ends. RFC3339 format with special handling for the now keyword
|
||||
duration: 1d # duration of the span. Uses SI formatting for duration amounts.
|
||||
step_duration: 10min # step size for the duration amounts.
|
@ -18,8 +18,9 @@ use chrono::Duration;
|
||||
use serde::Deserialize;
|
||||
use serde_yaml;
|
||||
use tracing::{debug, error};
|
||||
use anyhow::Result;
|
||||
|
||||
use crate::query::{QueryConn, QueryType, PlotMeta};
|
||||
use crate::query::{QueryConn, QueryType, QueryResult, PlotMeta, to_samples};
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct GraphSpan {
|
||||
@ -52,6 +53,21 @@ pub struct Graph {
|
||||
pub d3_tick_format: Option<String>,
|
||||
}
|
||||
|
||||
pub async fn query_data(graph: &Graph, dash: &Dashboard, query_span: Option<GraphSpan>) -> Result<Vec<QueryResult>> {
|
||||
let connections = graph.get_query_connections(&dash.span, &query_span);
|
||||
let mut data = Vec::new();
|
||||
for conn in connections {
|
||||
data.push(to_samples(
|
||||
conn.get_results()
|
||||
.await?
|
||||
.data()
|
||||
.clone(),
|
||||
conn.meta,
|
||||
));
|
||||
}
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
fn duration_from_string(duration_string: &str) -> Option<Duration> {
|
||||
match parse_duration::parse(duration_string) {
|
||||
Ok(d) => match Duration::from_std(d) {
|
||||
|
24
src/main.rs
24
src/main.rs
@ -12,12 +12,13 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
use std::path::PathBuf;
|
||||
|
||||
use anyhow;
|
||||
use axum::{self, extract::State, routing::*, Router};
|
||||
use clap::{self, Parser, ValueEnum};
|
||||
use dashboard::{Dashboard, query_data};
|
||||
use tokio::net::TcpListener;
|
||||
use tower_http::trace::TraceLayer;
|
||||
use tracing::{error, info};
|
||||
use tracing::Level;
|
||||
use tracing_subscriber::FmtSubscriber;
|
||||
|
||||
@ -43,6 +44,19 @@ struct Cli {
|
||||
pub config: PathBuf,
|
||||
#[arg(long, value_enum, default_value_t = Verbosity::INFO)]
|
||||
pub verbose: Verbosity,
|
||||
#[arg(long, default_value_t = false)]
|
||||
pub validate: bool,
|
||||
}
|
||||
|
||||
async fn validate(dash: &Dashboard) -> anyhow::Result<()> {
|
||||
for graph in dash.graphs.iter() {
|
||||
let data = query_data(graph, &dash, None).await;
|
||||
if data.is_err() {
|
||||
error!(err=?data, "Invalid dashboard query or queries");
|
||||
}
|
||||
let _ = data?;
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
@ -61,6 +75,14 @@ async fn main() -> anyhow::Result<()> {
|
||||
.expect("setting default subscriber failed");
|
||||
|
||||
let config = std::sync::Arc::new(dashboard::read_dashboard_list(args.config.as_path())?);
|
||||
|
||||
if args.validate {
|
||||
for dash in config.iter() {
|
||||
validate(&dash).await?;
|
||||
info!("All Queries successfully run against source");
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
let router = Router::new()
|
||||
// JSON api endpoints
|
||||
.nest("/js", routes::mk_js_routes(config.clone()))
|
||||
|
@ -24,7 +24,7 @@ use axum::{
|
||||
use maud::{html, Markup};
|
||||
use tracing::debug;
|
||||
|
||||
use crate::dashboard::{Dashboard, Graph, GraphSpan};
|
||||
use crate::dashboard::{Dashboard, Graph, GraphSpan, query_data};
|
||||
use crate::query::{to_samples, QueryResult};
|
||||
|
||||
type Config = State<Arc<Vec<Dashboard>>>;
|
||||
@ -54,18 +54,7 @@ pub async fn graph_query(
|
||||
None
|
||||
}
|
||||
};
|
||||
let connections = graph.get_query_connections(&dash.span, &query_span);
|
||||
let mut data = Vec::new();
|
||||
for conn in connections {
|
||||
data.push(to_samples(
|
||||
conn.get_results()
|
||||
.await
|
||||
.expect("Unable to get query results")
|
||||
.data()
|
||||
.clone(),
|
||||
conn.meta,
|
||||
));
|
||||
}
|
||||
let data = query_data(graph, dash, query_span).await.expect("Unable to get query results");
|
||||
Json(data)
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user