@@ -2,19 +2,24 @@ pub mod config;
22pub mod error;
33pub mod output;
44pub mod planner;
5+ pub mod prometheus_client;
56pub mod query_log;
67
8+ use promql_utilities:: data_model:: KeyByLabelNames ;
79use serde_yaml:: Value as YamlValue ;
810use sketch_db_common:: enums:: QueryLanguage ;
911use sketch_db_common:: inference_config:: InferenceConfig ;
1012use sketch_db_common:: streaming_config:: StreamingConfig ;
1113use std:: path:: Path ;
14+ use tracing:: debug;
1215
1316pub use config:: input:: ControllerConfig ;
1417pub use config:: input:: SQLControllerConfig ;
1518pub use error:: ControllerError ;
1619pub use output:: generator:: { GeneratorOutput , PuntedQuery } ;
1720pub use output:: sql_generator:: SQLRuntimeOptions ;
21+ pub use prometheus_client:: build_schema_from_prometheus;
22+ pub use sketch_db_common:: PromQLSchema ;
1823
1924#[ derive( Debug , Clone , Copy , PartialEq , Eq ) ]
2025pub enum StreamingEngine {
@@ -34,6 +39,7 @@ pub struct RuntimeOptions {
3439
3540pub struct Controller {
3641 config : ControllerConfig ,
42+ schema : PromQLSchema ,
3743 options : RuntimeOptions ,
3844}
3945
@@ -302,49 +308,135 @@ impl SQLController {
302308}
303309
304310impl Controller {
305- pub fn new ( config : ControllerConfig , options : RuntimeOptions ) -> Self {
306- Self { config, options }
311+ pub fn new ( config : ControllerConfig , schema : PromQLSchema , options : RuntimeOptions ) -> Self {
312+ Self {
313+ config,
314+ schema,
315+ options,
316+ }
307317 }
308318
309- pub fn from_file ( path : & Path , opts : RuntimeOptions ) -> Result < Self , ControllerError > {
319+ /// Build a `Controller` from a config file, fetching metric labels from Prometheus.
320+ ///
321+ /// `prometheus_url` is queried via `GET /api/v1/series?match[]=<metric>` for each metric
322+ /// name found in the config's PromQL queries.
323+ pub fn from_file (
324+ path : & Path ,
325+ opts : RuntimeOptions ,
326+ prometheus_url : & str ,
327+ ) -> Result < Self , ControllerError > {
310328 let yaml_str = std:: fs:: read_to_string ( path) ?;
311- Self :: from_yaml ( & yaml_str, opts)
329+ let config: ControllerConfig = serde_yaml:: from_str ( & yaml_str) ?;
330+ let all_queries: Vec < String > = config
331+ . query_groups
332+ . iter ( )
333+ . flat_map ( |qg| qg. queries . clone ( ) )
334+ . collect ( ) ;
335+ let mut schema =
336+ prometheus_client:: build_schema_from_prometheus ( prometheus_url, & all_queries) ?;
337+ // For any metric that Prometheus had no series for, fall back to the
338+ // `metrics` hint in the config file (if present).
339+ if let Some ( metric_hints) = & config. metrics {
340+ for hint in metric_hints {
341+ if !schema. config . contains_key ( & hint. metric ) {
342+ debug ! (
343+ "Prometheus had no series for '{}'; falling back to config-file hint with labels {:?}" ,
344+ hint. metric, hint. labels
345+ ) ;
346+ schema = schema. add_metric (
347+ hint. metric . clone ( ) ,
348+ KeyByLabelNames :: new ( hint. labels . clone ( ) ) ,
349+ ) ;
350+ }
351+ }
352+ }
353+ Ok ( Self {
354+ config,
355+ schema,
356+ options : opts,
357+ } )
358+ }
359+
360+ /// Build a `Controller` from a config file with a caller-supplied `PromQLSchema`.
361+ ///
362+ /// Use this when the schema is available without querying Prometheus (e.g. in tests
363+ /// or when the schema is constructed in-process by the caller).
364+ pub fn from_file_with_schema (
365+ path : & Path ,
366+ schema : PromQLSchema ,
367+ opts : RuntimeOptions ,
368+ ) -> Result < Self , ControllerError > {
369+ let yaml_str = std:: fs:: read_to_string ( path) ?;
370+ let config: ControllerConfig = serde_yaml:: from_str ( & yaml_str) ?;
371+ Ok ( Self {
372+ config,
373+ schema,
374+ options : opts,
375+ } )
312376 }
313377
314- pub fn from_yaml ( yaml : & str , opts : RuntimeOptions ) -> Result < Self , ControllerError > {
378+ /// Build a `Controller` from a YAML string with a caller-supplied `PromQLSchema`.
379+ pub fn from_yaml_with_schema (
380+ yaml : & str ,
381+ schema : PromQLSchema ,
382+ opts : RuntimeOptions ,
383+ ) -> Result < Self , ControllerError > {
315384 let config: ControllerConfig = serde_yaml:: from_str ( yaml) ?;
316385 Ok ( Self {
317386 config,
387+ schema,
318388 options : opts,
319389 } )
320390 }
321391
322- /// Build a `Controller` from a Prometheus query log file and a metrics config YAML.
392+ /// Build a `Controller` from a Prometheus query log file, fetching metric labels from
393+ /// Prometheus.
323394 ///
324395 /// - `log_path`: newline-delimited JSON query log (Prometheus `--query.log-file` output)
325- /// - `metrics_path `: YAML file with a `metrics:` section listing metric names and labels
396+ /// - `prometheus_url `: base URL queried for label discovery
326397 pub fn from_query_log (
327398 log_path : & Path ,
328- metrics_path : & Path ,
329399 opts : RuntimeOptions ,
400+ prometheus_url : & str ,
330401 ) -> Result < Self , ControllerError > {
331402 let entries = query_log:: parse_log_file ( log_path) ?;
332403 let ( instants, ranges) =
333404 query_log:: infer_queries ( & entries, opts. prometheus_scrape_interval ) ;
405+ let config = query_log:: to_controller_config ( instants, ranges) ;
406+ let all_queries: Vec < String > = config
407+ . query_groups
408+ . iter ( )
409+ . flat_map ( |qg| qg. queries . clone ( ) )
410+ . collect ( ) ;
411+ let schema = prometheus_client:: build_schema_from_prometheus ( prometheus_url, & all_queries) ?;
412+ Ok ( Self {
413+ config,
414+ schema,
415+ options : opts,
416+ } )
417+ }
334418
335- let metrics_yaml = std:: fs:: read_to_string ( metrics_path) ?;
336- let metrics_config: query_log:: MetricsConfig = serde_yaml:: from_str ( & metrics_yaml) ?;
337-
338- let config = query_log:: to_controller_config ( instants, ranges, metrics_config. metrics ) ;
339-
419+ /// Build a `Controller` from a Prometheus query log file with a caller-supplied `PromQLSchema`.
420+ ///
421+ /// Use this when the schema is available without querying Prometheus (e.g. in tests).
422+ pub fn from_query_log_with_schema (
423+ log_path : & Path ,
424+ schema : PromQLSchema ,
425+ opts : RuntimeOptions ,
426+ ) -> Result < Self , ControllerError > {
427+ let entries = query_log:: parse_log_file ( log_path) ?;
428+ let ( instants, ranges) =
429+ query_log:: infer_queries ( & entries, opts. prometheus_scrape_interval ) ;
430+ let config = query_log:: to_controller_config ( instants, ranges) ;
340431 Ok ( Self {
341432 config,
433+ schema,
342434 options : opts,
343435 } )
344436 }
345437
346438 pub fn generate ( & self ) -> Result < PlannerOutput , ControllerError > {
347- let output = output:: generator:: generate_plan ( & self . config , & self . options ) ?;
439+ let output = output:: generator:: generate_plan ( & self . config , & self . schema , & self . options ) ?;
348440 Ok ( PlannerOutput {
349441 punted_queries : output. punted_queries ,
350442 streaming_yaml : output. streaming_yaml ,
0 commit comments