From da37ed3acb48704b835091e829662bcdd149c170 Mon Sep 17 00:00:00 2001 From: Benjamin Naecker Date: Thu, 6 Jun 2024 03:17:12 +0000 Subject: [PATCH] more wip: handle late elided metric versions, improve CLI --- oximeter/impl/src/bin/oximeter-schema.rs | 58 ++++++++++++++++------- oximeter/impl/src/schema/ir.rs | 59 +++++++++++++++++++++++- oximeter/impl/src/schema/mod.rs | 24 +++++++++- 3 files changed, 122 insertions(+), 19 deletions(-) diff --git a/oximeter/impl/src/bin/oximeter-schema.rs b/oximeter/impl/src/bin/oximeter-schema.rs index a48d918bca0..2c6644d1b01 100644 --- a/oximeter/impl/src/bin/oximeter-schema.rs +++ b/oximeter/impl/src/bin/oximeter-schema.rs @@ -29,17 +29,31 @@ enum Cmd { /// Print the derived timeseries schema. Schema { + /// Show the schema for a specified timeseries by name. + /// + /// If not provided, all timeseries are printed. + #[arg(short, long)] + timeseries: Option, + /// Show the schema for a specified version. /// /// If not provided, all versions are shown. + #[arg(short, long)] version: Option, }, /// Print the Rust code that would be emitted in the macro format. Emit { + /// Show the schema for a specified timeseries by name. + /// + /// If not provided, all timeseries are printed. + #[arg(short, long)] + timeseries: Option, + /// Show the emitted code for a specified version. /// /// If not provided, all versions are shown. + #[arg(short, long)] version: Option, }, } @@ -54,26 +68,38 @@ fn main() -> anyhow::Result<()> { let def: TimeseriesDefinition = toml::from_str(&contents)?; println!("{def:#?}"); } - Cmd::Schema { version } => { + Cmd::Schema { timeseries, version } => { let schema = oximeter_impl::schema::ir::load_schema(&contents)?; - if let Some(ver) = version { - let Some(s) = schema.get(usize::from(ver.get() - 1)) else { - anyhow::bail!( - "Schema file {} does not contain version {}", - args.path.display(), - ver, - ); - }; - println!("{s:#?}"); - } else { - for (version, schema) in schema.into_iter().enumerate() { - println!("Version {}", version + 1); - println!("----------"); - println!("{:#?}", schema); + match (timeseries, version) { + (None, None) => { + for each in schema.into_iter() { + println!("{each:#?}"); + } + } + (None, Some(version)) => { + for each in + schema.into_iter().filter(|s| s.version == version) + { + println!("{each:#?}"); + } + } + (Some(name), None) => { + for each in + schema.into_iter().filter(|s| s.timeseries_name == name) + { + println!("{each:#?}"); + } + } + (Some(name), Some(version)) => { + for each in schema.into_iter().filter(|s| { + s.timeseries_name == name && s.version == version + }) { + println!("{each:#?}"); + } } } } - Cmd::Emit { version } => todo!(), + Cmd::Emit { .. } => todo!(), } Ok(()) } diff --git a/oximeter/impl/src/schema/ir.rs b/oximeter/impl/src/schema/ir.rs index c9fca232100..d22d0571932 100644 --- a/oximeter/impl/src/schema/ir.rs +++ b/oximeter/impl/src/schema/ir.rs @@ -234,9 +234,9 @@ pub fn load_schema( timeseries_name, field_schema, datum_type: metric.datum_type, + version: NonZeroU8::new(last_target_version).unwrap(), /* TODO(ben): Add these fields. units: metric.units, - version: last_target_version, authz_scope, */ created: Utc::now(), @@ -245,6 +245,63 @@ pub fn load_schema( last_target_version.checked_add(1).expect("version < 256"); } } + + // We also allow omitting later versions of metrics if they are + // unchanged. A target has to specify every version, even if it's the + // same, but the metrics need only specify differents. + // + // Here, look for any target version strictly later than the last metric + // version, and create a corresponding target / metric pair for it. + if let Some(last_metric_fields) = metric.versions.last() { + match last_metric_fields { + MetricFields::Removed { .. } => {} + MetricFields::Added { + added_in: last_metric_version, + fields, + } + | MetricFields::Versioned(VersionedFields { + version: last_metric_version, + fields, + }) => { + let metric_field_names: BTreeSet<_> = + fields.iter().collect(); + let next_version = last_metric_version + .get() + .checked_add(1) + .expect("version < 256"); + for (version, target_fields) in + target_fields_by_version.range(next_version..) + { + let field_schema = construct_field_schema( + &def.fields, + target_name, + target_fields, + metric_name, + &metric_field_names, + )?; + let _authz_scope = extract_authz_scope( + metric_name, + def.target.authz_scope, + &field_schema, + )?; + let timeseries_name = TimeseriesName::try_from( + format!("{}:{}", target_name, metric_name), + )?; + out.push(TimeseriesSchema { + timeseries_name, + field_schema, + datum_type: metric.datum_type, + version: NonZeroU8::new(*version).unwrap(), + /* TODO(ben): Add these fields. + units: metric.units, + authz_scope, + */ + created: Utc::now(), + }); + } + } + } + } } Ok(out) diff --git a/oximeter/impl/src/schema/mod.rs b/oximeter/impl/src/schema/mod.rs index a9dfcf1c827..1b96faa6bd2 100644 --- a/oximeter/impl/src/schema/mod.rs +++ b/oximeter/impl/src/schema/mod.rs @@ -24,6 +24,7 @@ use std::collections::btree_map::Entry; use std::collections::BTreeMap; use std::collections::BTreeSet; use std::fmt::Write; +use std::num::NonZeroU8; use std::path::Path; /// The name and type information for a field of a timeseries schema. @@ -166,9 +167,15 @@ pub struct TimeseriesSchema { pub timeseries_name: TimeseriesName, pub field_schema: BTreeSet, pub datum_type: DatumType, + #[serde(default = "default_version")] + pub version: NonZeroU8, pub created: DateTime, } +pub const fn default_version() -> NonZeroU8 { + unsafe { NonZeroU8::new_unchecked(1) } +} + impl From<&Sample> for TimeseriesSchema { fn from(sample: &Sample) -> Self { let timeseries_name = sample.timeseries_name.parse().unwrap(); @@ -190,7 +197,13 @@ impl From<&Sample> for TimeseriesSchema { field_schema.insert(schema); } let datum_type = sample.measurement.datum_type(); - Self { timeseries_name, field_schema, datum_type, created: Utc::now() } + Self { + timeseries_name, + field_schema, + datum_type, + version: default_version(), + created: Utc::now(), + } } } @@ -222,7 +235,13 @@ impl TimeseriesSchema { field_schema.insert(schema); } let datum_type = metric.datum_type(); - Self { timeseries_name, field_schema, datum_type, created: Utc::now() } + Self { + timeseries_name, + field_schema, + datum_type, + version: default_version(), + created: Utc::now(), + } } /// Construct a timeseries schema from a sample @@ -643,6 +662,7 @@ mod tests { timeseries_name, field_schema, datum_type, + version: default_version(), created: Utc::now(), };